mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35:45 +01:00
initial styling
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bulma": "^0.7.1",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.2.5",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"react": "^16.4.1",
|
"react": "^16.4.1",
|
||||||
@@ -19,8 +20,12 @@
|
|||||||
"redux-thunk": "^2.3.0"
|
"redux-thunk": "^2.3.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
|
||||||
"build": "react-scripts build",
|
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/ --watch --recursive",
|
||||||
|
"start-js": "react-scripts start",
|
||||||
|
"start": "npm-run-all -p watch-css start-js",
|
||||||
|
"build-js": "react-scripts build",
|
||||||
|
"build": "npm-run-all build-css build-js",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
"flow": "flow"
|
"flow": "flow"
|
||||||
@@ -35,6 +40,8 @@
|
|||||||
"enzyme-adapter-react-16": "^1.1.1",
|
"enzyme-adapter-react-16": "^1.1.1",
|
||||||
"flow-bin": "^0.75.0",
|
"flow-bin": "^0.75.0",
|
||||||
"flow-typed": "^2.5.1",
|
"flow-typed": "^2.5.1",
|
||||||
|
"node-sass-chokidar": "^1.3.0",
|
||||||
|
"npm-run-all": "^4.1.3",
|
||||||
"prettier": "^1.13.7",
|
"prettier": "^1.13.7",
|
||||||
"react-test-renderer": "^16.4.1"
|
"react-test-renderer": "^16.4.1"
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -19,7 +19,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>SCM-Manager</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"short_name": "React App",
|
"short_name": "SCM-Manager",
|
||||||
"name": "Create React App Sample",
|
"name": "SCM-Manager",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
|||||||
31
scm-ui/src/components/Header.js
Normal file
31
scm-ui/src/components/Header.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import Logo from "./Logo";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children?: React.Node
|
||||||
|
};
|
||||||
|
|
||||||
|
class Header extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { children } = this.props;
|
||||||
|
return (
|
||||||
|
<section className="hero is-dark is-small">
|
||||||
|
<div className="hero-body">
|
||||||
|
<div className="container">
|
||||||
|
<div className="columns is-vcentered">
|
||||||
|
<div className="column">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="hero-foot">
|
||||||
|
<div className="container">{children}</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Header;
|
||||||
49
scm-ui/src/components/InputField.js
Normal file
49
scm-ui/src/components/InputField.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import Image from "../images/logo.png";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
label?: string,
|
||||||
|
placeholder?: string,
|
||||||
|
type?: string,
|
||||||
|
onChange: string => void
|
||||||
|
};
|
||||||
|
|
||||||
|
class InputField extends React.Component<Props> {
|
||||||
|
static defaultProps = {
|
||||||
|
type: "text",
|
||||||
|
placeholder: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||||
|
this.props.onChange(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLabel = () => {
|
||||||
|
const label = this.props.label;
|
||||||
|
if (label) {
|
||||||
|
return <label class="label">{label}</label>;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { type, placeholder } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="field">
|
||||||
|
{this.renderLabel()}
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
class="input"
|
||||||
|
type={type}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={this.handleInput}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InputField;
|
||||||
11
scm-ui/src/components/Logo.js
Normal file
11
scm-ui/src/components/Logo.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import Image from "../images/logo.png";
|
||||||
|
|
||||||
|
class Logo extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
return <img src={Image} alt="SCM-Manager logo" />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Logo;
|
||||||
17
scm-ui/src/components/PrimaryNavigation.js
Normal file
17
scm-ui/src/components/PrimaryNavigation.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import PrimaryNavigationLink from "./PrimaryNavigationLink";
|
||||||
|
|
||||||
|
class PrimaryNavigation extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<nav className="tabs is-boxed">
|
||||||
|
<ul>
|
||||||
|
<PrimaryNavigationLink to="/users">Users</PrimaryNavigationLink>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrimaryNavigation;
|
||||||
29
scm-ui/src/components/PrimaryNavigationLink.js
Normal file
29
scm-ui/src/components/PrimaryNavigationLink.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { Route, Link } from "react-router-dom";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
to: string,
|
||||||
|
activeOnlyWhenExact?: boolean,
|
||||||
|
children?: React.Node
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrimaryNavigationLink extends React.Component<Props> {
|
||||||
|
renderLink = (route: any) => {
|
||||||
|
const { to, children } = this.props;
|
||||||
|
return (
|
||||||
|
<li className={route.match ? "is-active" : ""}>
|
||||||
|
<Link to={to}>{children}</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { to, activeOnlyWhenExact } = this.props;
|
||||||
|
return (
|
||||||
|
<Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrimaryNavigationLink;
|
||||||
38
scm-ui/src/components/SubmitButton.js
Normal file
38
scm-ui/src/components/SubmitButton.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import Logo from "./Logo";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
value: string,
|
||||||
|
large?: boolean,
|
||||||
|
fullWidth?: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubmitButton extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { value, large, fullWidth } = this.props;
|
||||||
|
|
||||||
|
const largeClass = large ? "is-large" : "";
|
||||||
|
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="field">
|
||||||
|
<div className="control">
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
className={classNames(
|
||||||
|
"button",
|
||||||
|
"is-link",
|
||||||
|
largeClass,
|
||||||
|
fullWidthClass
|
||||||
|
)}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubmitButton;
|
||||||
6435
scm-ui/src/containers/App.css
Normal file
6435
scm-ui/src/containers/App.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,14 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import Navigation from "./Navigation";
|
|
||||||
import Main from "./Main";
|
import Main from "./Main";
|
||||||
import Login from "./Login";
|
import Login from "./Login";
|
||||||
import { getIsAuthenticated } from "../modules/login";
|
import { getIsAuthenticated } from "../modules/login";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { withRouter } from "react-router-dom";
|
import { withRouter } from "react-router-dom";
|
||||||
|
|
||||||
|
import "./App.css";
|
||||||
|
import Header from "../components/Header";
|
||||||
|
import PrimaryNavigation from "../components/PrimaryNavigation";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
login: boolean,
|
login: boolean,
|
||||||
username: string,
|
username: string,
|
||||||
@@ -18,25 +21,27 @@ class App extends Component<Props> {
|
|||||||
this.props.getAuthState();
|
this.props.getAuthState();
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const { login, username, loading } = this.props;
|
const { login, loading } = this.props;
|
||||||
|
|
||||||
|
let content;
|
||||||
|
let navigation;
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <div>Loading...</div>;
|
content = <div>Loading...</div>;
|
||||||
} else if (!login) {
|
} else if (!login) {
|
||||||
return (
|
content = <Login />;
|
||||||
<div>
|
|
||||||
<Login />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
|
content = <Main />;
|
||||||
|
navigation = <PrimaryNavigation />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<h2>Welcome, {username}!</h2>
|
<Header>{navigation}</Header>
|
||||||
<Navigation />
|
{content}
|
||||||
<Main />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
|
|||||||
30
scm-ui/src/containers/App.scss
Normal file
30
scm-ui/src/containers/App.scss
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@import "bulma/sass/utilities/initial-variables";
|
||||||
|
@import "bulma/sass/utilities/functions";
|
||||||
|
|
||||||
|
$blue: #33B2E8;
|
||||||
|
|
||||||
|
// $footer-background-color
|
||||||
|
|
||||||
|
.is-ellipsis-overflow {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-rounded-border {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fitParent {
|
||||||
|
// TODO get rid of important
|
||||||
|
margin: 0 !important;
|
||||||
|
// 3.8em for line-numbers
|
||||||
|
padding: 0 0 0 3.8em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Import the rest of Bulma
|
||||||
|
@import "bulma/bulma";
|
||||||
@@ -4,15 +4,30 @@ import injectSheet from "react-jss";
|
|||||||
import { login } from "../modules/login";
|
import { login } from "../modules/login";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
import InputField from "../components/InputField";
|
||||||
|
import SubmitButton from "../components/SubmitButton";
|
||||||
|
|
||||||
|
import classNames from "classnames";
|
||||||
|
import Avatar from "../images/blib.jpg";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
wrapper: {
|
spacing: {
|
||||||
width: "100%",
|
paddingTop: "5rem"
|
||||||
display: "flex",
|
|
||||||
height: "10em"
|
|
||||||
},
|
},
|
||||||
login: {
|
avatar: {
|
||||||
margin: "auto",
|
marginTop: "-70px",
|
||||||
textAlign: "center"
|
paddingBottom: "20px"
|
||||||
|
},
|
||||||
|
avatarImage: {
|
||||||
|
border: "1px solid lightgray",
|
||||||
|
padding: "5px",
|
||||||
|
background: "#fff",
|
||||||
|
borderRadius: "50%",
|
||||||
|
width: "128px",
|
||||||
|
height: "128px"
|
||||||
|
},
|
||||||
|
avatarSpacing: {
|
||||||
|
marginTop: "5rem"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,13 +47,13 @@ class Login extends React.Component<Props, State> {
|
|||||||
this.state = { username: "", password: "" };
|
this.state = { username: "", password: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUsernameChange(event: SyntheticInputEvent<HTMLInputElement>) {
|
handleUsernameChange = (value: string) => {
|
||||||
this.setState({ username: event.target.value });
|
this.setState({ username: value });
|
||||||
}
|
};
|
||||||
|
|
||||||
handlePasswordChange(event: SyntheticInputEvent<HTMLInputElement>) {
|
handlePasswordChange = (value: string) => {
|
||||||
this.setState({ password: event.target.value });
|
this.setState({ password: value });
|
||||||
}
|
};
|
||||||
|
|
||||||
handleSubmit(event: Event) {
|
handleSubmit(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -48,24 +63,37 @@ class Login extends React.Component<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<section className="hero is-fullheight">
|
||||||
<div className={classes.login}>
|
<div className={classes.spacing}>
|
||||||
You need to log in! ...
|
<div className="container has-text-centered">
|
||||||
|
<div className="column is-4 is-offset-4">
|
||||||
|
<h3 className="title">Login</h3>
|
||||||
|
<p className="subtitle">Please login to proceed.</p>
|
||||||
|
<div className={classNames("box", classes.avatarSpacing)}>
|
||||||
|
<figure className={classes.avatar}>
|
||||||
|
<img
|
||||||
|
className={classes.avatarImage}
|
||||||
|
src={Avatar}
|
||||||
|
alt="SCM-Manager"
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
<form onSubmit={this.handleSubmit.bind(this)}>
|
<form onSubmit={this.handleSubmit.bind(this)}>
|
||||||
<input
|
<InputField
|
||||||
type="text"
|
placeholder="Your Username"
|
||||||
defaultValue="Username"
|
onChange={this.handleUsernameChange}
|
||||||
onChange={this.handleUsernameChange.bind(this)}
|
|
||||||
/>
|
/>
|
||||||
<input
|
<InputField
|
||||||
|
placeholder="Your Password"
|
||||||
type="password"
|
type="password"
|
||||||
defaultValue="Password"
|
onChange={this.handlePasswordChange}
|
||||||
onChange={this.handlePasswordChange.bind(this)}
|
|
||||||
/>
|
/>
|
||||||
<input type="submit" value="Login" />
|
<SubmitButton value="Login" fullWidth={true} />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,36 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import injectSheet from 'react-jss';
|
import injectSheet from "react-jss";
|
||||||
import classNames from 'classnames';
|
import classNames from "classnames";
|
||||||
|
|
||||||
import { Route, withRouter } from 'react-router';
|
import { Route, withRouter } from "react-router";
|
||||||
|
|
||||||
import Repositories from '../repositories/containers/Repositories';
|
import Repositories from "../repositories/containers/Repositories";
|
||||||
import Users from '../users/containers/Users';
|
import Users from "../users/containers/Users";
|
||||||
import {Switch} from 'react-router-dom';
|
import { Switch } from "react-router-dom";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
content: {
|
content: {
|
||||||
paddingTop: '60px'
|
paddingTop: "60px"
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
classes: any
|
classes: any
|
||||||
}
|
};
|
||||||
|
|
||||||
class Main extends React.Component<Props> {
|
class Main extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes } = this.props;
|
const { classes } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className={classNames('container', classes.content)}>
|
<div className={classNames("container", classes.content)}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/" component={Repositories} />
|
<Route exact path="/" component={Repositories} />
|
||||||
<Route exact path="/users" component={Users} />
|
<Route path="/users" component={Users} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withRouter(injectSheet(styles)(Main));
|
export default withRouter(injectSheet(styles)(Main));
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
//@flow
|
|
||||||
import React from 'react';
|
|
||||||
import {Link} from 'react-router-dom';
|
|
||||||
|
|
||||||
type Props = {};
|
|
||||||
|
|
||||||
type State = {
|
|
||||||
collapsed: boolean
|
|
||||||
};
|
|
||||||
|
|
||||||
class Navigation extends React.Component<Props, State> {
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
collapsed: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleCollapse = () => {
|
|
||||||
this.setState({
|
|
||||||
collapsed: !this.state.collapsed
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className="navbar navbar-default navbar-fixed-top">
|
|
||||||
<div className="container">
|
|
||||||
<div className="navbar-header">
|
|
||||||
<button type="button" className="navbar-toggle collapsed" data-toggle="collapse"
|
|
||||||
data-target="#navbar" aria-expanded="false" aria-controls="navbar"
|
|
||||||
onClick={this.toggleCollapse}>
|
|
||||||
<span className="sr-only">Toggle navigation</span>
|
|
||||||
<span className="icon-bar"></span>
|
|
||||||
<span className="icon-bar"></span>
|
|
||||||
<span className="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Link className="navbar-brand" to="/">
|
|
||||||
SCM 2 Test UI
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Navigation;
|
|
||||||
BIN
scm-ui/src/images/blib.jpg
Normal file
BIN
scm-ui/src/images/blib.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
scm-ui/src/images/logo.png
Normal file
BIN
scm-ui/src/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user