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",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bulma": "^0.7.1",
|
||||
"classnames": "^2.2.5",
|
||||
"history": "^4.7.2",
|
||||
"react": "^16.4.1",
|
||||
@@ -19,8 +20,12 @@
|
||||
"redux-thunk": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"build-css": "node-sass-chokidar --include-path ./src --include-path ./node_modules src/ -o src/",
|
||||
"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",
|
||||
"eject": "react-scripts eject",
|
||||
"flow": "flow"
|
||||
@@ -35,6 +40,8 @@
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"flow-bin": "^0.75.0",
|
||||
"flow-typed": "^2.5.1",
|
||||
"node-sass-chokidar": "^1.3.0",
|
||||
"npm-run-all": "^4.1.3",
|
||||
"prettier": "^1.13.7",
|
||||
"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.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>SCM-Manager</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "SCM-Manager",
|
||||
"name": "SCM-Manager",
|
||||
"icons": [
|
||||
{
|
||||
"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 Navigation from "./Navigation";
|
||||
import Main from "./Main";
|
||||
import Login from "./Login";
|
||||
import { getIsAuthenticated } from "../modules/login";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
|
||||
import "./App.css";
|
||||
import Header from "../components/Header";
|
||||
import PrimaryNavigation from "../components/PrimaryNavigation";
|
||||
|
||||
type Props = {
|
||||
login: boolean,
|
||||
username: string,
|
||||
@@ -18,26 +21,28 @@ class App extends Component<Props> {
|
||||
this.props.getAuthState();
|
||||
}
|
||||
render() {
|
||||
const { login, username, loading } = this.props;
|
||||
const { login, loading } = this.props;
|
||||
|
||||
let content;
|
||||
let navigation;
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
content = <div>Loading...</div>;
|
||||
} else if (!login) {
|
||||
return (
|
||||
<div>
|
||||
<Login />
|
||||
</div>
|
||||
);
|
||||
content = <Login />;
|
||||
} else {
|
||||
content = <Main />;
|
||||
navigation = <PrimaryNavigation />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<h2>Welcome, {username}!</h2>
|
||||
<Navigation />
|
||||
<Main />
|
||||
<Header>{navigation}</Header>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
|
||||
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 { 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 = {
|
||||
wrapper: {
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
height: "10em"
|
||||
spacing: {
|
||||
paddingTop: "5rem"
|
||||
},
|
||||
login: {
|
||||
margin: "auto",
|
||||
textAlign: "center"
|
||||
avatar: {
|
||||
marginTop: "-70px",
|
||||
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: "" };
|
||||
}
|
||||
|
||||
handleUsernameChange(event: SyntheticInputEvent<HTMLInputElement>) {
|
||||
this.setState({ username: event.target.value });
|
||||
}
|
||||
handleUsernameChange = (value: string) => {
|
||||
this.setState({ username: value });
|
||||
};
|
||||
|
||||
handlePasswordChange(event: SyntheticInputEvent<HTMLInputElement>) {
|
||||
this.setState({ password: event.target.value });
|
||||
}
|
||||
handlePasswordChange = (value: string) => {
|
||||
this.setState({ password: value });
|
||||
};
|
||||
|
||||
handleSubmit(event: Event) {
|
||||
event.preventDefault();
|
||||
@@ -48,24 +63,37 @@ class Login extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.login}>
|
||||
You need to log in! ...
|
||||
<section className="hero is-fullheight">
|
||||
<div className={classes.spacing}>
|
||||
<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)}>
|
||||
<input
|
||||
type="text"
|
||||
defaultValue="Username"
|
||||
onChange={this.handleUsernameChange.bind(this)}
|
||||
<InputField
|
||||
placeholder="Your Username"
|
||||
onChange={this.handleUsernameChange}
|
||||
/>
|
||||
<input
|
||||
<InputField
|
||||
placeholder="Your Password"
|
||||
type="password"
|
||||
defaultValue="Password"
|
||||
onChange={this.handlePasswordChange.bind(this)}
|
||||
onChange={this.handlePasswordChange}
|
||||
/>
|
||||
<input type="submit" value="Login" />
|
||||
<SubmitButton value="Login" fullWidth={true} />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
//@flow
|
||||
import React from 'react';
|
||||
import injectSheet from 'react-jss';
|
||||
import classNames from 'classnames';
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import classNames from "classnames";
|
||||
|
||||
import { Route, withRouter } from 'react-router';
|
||||
import { Route, withRouter } from "react-router";
|
||||
|
||||
import Repositories from '../repositories/containers/Repositories';
|
||||
import Users from '../users/containers/Users';
|
||||
import {Switch} from 'react-router-dom';
|
||||
import Repositories from "../repositories/containers/Repositories";
|
||||
import Users from "../users/containers/Users";
|
||||
import { Switch } from "react-router-dom";
|
||||
|
||||
const styles = {
|
||||
content: {
|
||||
paddingTop: '60px'
|
||||
},
|
||||
paddingTop: "60px"
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
classes: any
|
||||
}
|
||||
};
|
||||
|
||||
class Main extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classNames('container', classes.content)}>
|
||||
<div className={classNames("container", classes.content)}>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Repositories} />
|
||||
<Route exact path="/users" component={Users} />
|
||||
<Route path="/users" component={Users} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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