diff --git a/services/idp/Makefile b/services/idp/Makefile index 1844f39da..b9b291934 100644 --- a/services/idp/Makefile +++ b/services/idp/Makefile @@ -30,9 +30,7 @@ ci-go-generate: # CI runs ci-node-generate automatically before this target ci-node-generate: assets .PHONY: assets -assets: i18n \ - ui-images \ - yarn-build \ +assets: yarn-build \ assets/identifier/static \ assets/identifier/static/logo.svg \ assets/identifier/static/favicon.ico diff --git a/services/idp/package.json b/services/idp/package.json index 3902e36ce..baf2e2e59 100644 --- a/services/idp/package.json +++ b/services/idp/package.json @@ -23,6 +23,7 @@ "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", "i18next-resources-to-backend": "^1.0.0", + "kpop": "https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz", "query-string": "^7.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/services/idp/ui/public/index.html b/services/idp/ui/public/index.html index 7d5cb2ca4..8dccae78b 100644 --- a/services/idp/ui/public/index.html +++ b/services/idp/ui/public/index.html @@ -3,18 +3,16 @@ - + + - Sign in to your account + Sign in - ownCloud -
-
-
-
+
diff --git a/services/idp/ui/src/App.jsx b/services/idp/ui/src/App.jsx index bf97168f6..e7db6e6cc 100644 --- a/services/idp/ui/src/App.jsx +++ b/services/idp/ui/src/App.jsx @@ -1,15 +1,14 @@ import React, { Suspense, lazy } from 'react'; import { MuiThemeProvider } from '@material-ui/core/styles'; -import { - CssBaseline, - } from '@material-ui/core'; +import { defaultTheme as theme } from 'kpop/es/theme'; +import 'kpop/static/css/base.css'; +import 'kpop/static/css/scrollbar.css'; import './App.css'; -import './fancy-background.css'; + import Spinner from './components/Spinner'; import * as version from './version'; -import theme from './theme'; const LazyMain = lazy(() => import(/* webpackChunkName: "identifier-main" */ './Main')); @@ -18,7 +17,6 @@ console.info(`Kopano Identifier build version: ${version.build}`); // eslint-dis const App = () => { return ( - }> diff --git a/services/idp/ui/src/Main.jsx b/services/idp/ui/src/Main.jsx index 22bbf9a67..58c44753f 100644 --- a/services/idp/ui/src/Main.jsx +++ b/services/idp/ui/src/Main.jsx @@ -8,24 +8,14 @@ import { withStyles } from '@material-ui/core/styles'; import Routes from './Routes'; -const styles = () => ({ - root: { - position: 'relative', - display: 'flex', - flex: 1 - } -}); - class Main extends PureComponent { render() { const { classes, hello, pathPrefix } = this.props; return ( -
- - - -
+ + + ); } @@ -54,4 +44,4 @@ const mapStateToProps = (state) => { }; }; -export default connect(mapStateToProps)(withStyles(styles)(Main)); +export default connect(mapStateToProps)(Main); diff --git a/services/idp/ui/src/app.css b/services/idp/ui/src/app.css new file mode 100644 index 000000000..1a7f1d5ff --- /dev/null +++ b/services/idp/ui/src/app.css @@ -0,0 +1,157 @@ +/* additional css on top of kpop */ +body { + font-family: 'Open Sans', sans-serif; +} + +strong { + font-weight: 600; +} + +.oc-login-bg { + background-image: url(./images/background.jpg); + background-size: cover; + background-repeat: no-repeat; + background-position: center; + z-index: 0; +} + +#loader { + /* NOTE(longsleep): White here needed because of the background image */ + color: white; + text-shadow: #000 0 0 1px; +} + +.oc-logo { + position: absolute; + top: -130px; + left: 50%; + height: 80px; + transform: translateX(-50%); +} + +.oc-progress { + /* Needs to be important to overwrite material-ui */ + background-color: rgba(78, 133, 200, 0.8) !important; + height: 4px; + width: 100px; +} + +.oc-progress > div { + /* Needs to be important to overwrite material-ui */ + background-color: #4a76ac !important; +} + +.oc-input { + background-color: #042047; + border: 1px solid rgba(78, 133, 200, 0.8); + border-radius: 3px; + color: rgba(255, 255, 255, 0.8); + height: 40px; + width: 300px; + padding: 16px; + box-sizing: border-box; + font-size: 1rem; +} + +.oc-label { + color: #fff; + display: inline-block; + margin-bottom: 5px; +} + +.oc-input.error { + outline: none; + border: 1px solid #fe4600; +} + +.MuiTypography-colorError { + color: #fe4600 !important; +} + +.oc-input:focus { + outline: none; + border: 1px solid #fff; +} + +.oc-input::placeholder { + color: rgba(78, 133, 200, 0.8); +} + +.oc-input + .oc-input { + margin-top: 15px; +} + +.MuiTouchRipple-root { + display: none !important; +} + +.oc-button { + /* Needs to be important to overwrite material-ui */ + font-size: 1.0625rem !important; +} + +.oc-button-primary { + /* Needs to be important to overwrite material-ui */ + background-color: #4a76ac !important; + border: 1px solid transparent !important; +} + +.oc-button-primary:hover, +.oc-button-primary:focus { + /* Needs to be important to overwrite material-ui */ + background-color: #4a76ac !important; + border: 1px solid white !important; +} + +.oc-checkbox-dark svg { + /* Needs to be important to overwrite material-ui */ + fill: white !important; +} + +.oc-footer-message { + color: white; + padding: 10px; + font-size: 0.8rem; +} + +@media only screen and (max-width: 768px) { + .oc-logo { + height: 60px; + top: -90px; + } +} + +/* Helpers */ +.oc-mt-l { + margin-top: 30px !important; +} + +.oc-mb-m { + margin-bottom: 20px !important; +} + +.oc-light { + color: #fff !important; +} + +.oc-login-form div:not(:last-of-type) { + margin-bottom: 15px; +} + + +/* + * Special SR classes + * Used to hide an element visually, but keeping it accessible for accessibility tools. + */ +.oc-invisible-sr { + border: 0 !important; + clip: rect(1px, 1px, 1px, 1px) !important; + height: 1px !important; + overflow: hidden !important; + padding: 0 !important; + /* Need to make sure we override any existing styles. */ + position: absolute !important; + top: 0; + white-space: nowrap; + width: 1px !important; +} diff --git a/services/idp/ui/src/components/Loading.jsx b/services/idp/ui/src/components/Loading.jsx index 8412d215c..b1d2d78c7 100644 --- a/services/idp/ui/src/components/Loading.jsx +++ b/services/idp/ui/src/components/Loading.jsx @@ -4,7 +4,6 @@ import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import LinearProgress from '@material-ui/core/LinearProgress'; import Grid from '@material-ui/core/Grid'; import Typography from '@material-ui/core/Typography'; @@ -14,47 +13,28 @@ import renderIf from 'render-if'; import { retryHello } from '../actions/common'; import { ErrorMessage } from '../errors'; -const styles = theme => ({ - root: { - flexGrow: 1, - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0 - }, - progress: { - height: '4px', - width: '100px' - }, - button: { - marginTop: theme.spacing(5) - } -}); - class Loading extends React.PureComponent { render() { const { classes, error, t } = this.props; return ( - {renderIf(error === null)(() => ( - + ))} {renderIf(error !== null)(() => (
- + {t("konnect.loading.error.headline", "Failed to connect to server")} - +
))}
-
); } @@ -90,4 +69,4 @@ const mapStateToProps = (state) => { }; }; -export default connect(mapStateToProps)(withTranslation()(withStyles(styles)(Loading))); +export default connect(mapStateToProps)(withTranslation()(Loading)); diff --git a/services/idp/ui/src/components/ResponsiveScreen.jsx b/services/idp/ui/src/components/ResponsiveScreen.jsx index 761942006..b05878f7c 100644 --- a/services/idp/ui/src/components/ResponsiveScreen.jsx +++ b/services/idp/ui/src/components/ResponsiveScreen.jsx @@ -2,6 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; +import { Trans } from 'react-i18next'; + import { withStyles } from '@material-ui/core/styles'; import Grid from '@material-ui/core/Grid'; import DialogActions from '@material-ui/core/DialogActions'; @@ -18,25 +20,21 @@ const styles = theme => ({ flex: 1, }, content: { - paddingTop: 24, - paddingBottom: 12, - minHeight: 350, - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(2), - position: 'relative' - }, - dialog: { - maxWidth: 440, - }, - logo: { - height: 24, + position: 'relative', + width: '100%' }, actions: { marginTop: -40, - minHeight: 45, justifyContent: 'flex-start', paddingLeft: theme.spacing(3), paddingRight: theme.spacing(3) + }, + wrapper: { + width: '100%', + maxWidth: 300, + display: 'flex', + flex: 1, + alignItems: 'center' } }); @@ -56,26 +54,22 @@ const ResponsiveScreen = (props) => { const bannerLogoSrc = branding?.bannerLogo ? branding.bannerLogo : Logo; const logo = withoutLogo ? null : - ; + ownCloud Logo; const content = loading ? : (withoutPadding ? children : {children}); return ( - - -
- {logo} - {content} +
+
+ {logo} + {content} +
- {!loading && } - +
+ ownCloud - a safe home for all your data +
); }; diff --git a/services/idp/ui/src/components/TextInput.js b/services/idp/ui/src/components/TextInput.js new file mode 100644 index 000000000..f8f116428 --- /dev/null +++ b/services/idp/ui/src/components/TextInput.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + + +const TextInput = (props) => { + const label = props.label; + const extraClassName = props.extraClassName; + + delete props.label; + delete props.extraClassName; + + return ( +
+ + +
); +}; + +TextInput.propTypes = { + placeholder: PropTypes.object, + label: PropTypes.object, + id: PropTypes.string, + extraClassName: props.string, +} + +export default TextInput; diff --git a/services/idp/ui/src/containers/Login/Login.jsx b/services/idp/ui/src/containers/Login/Login.jsx index 438153adf..bc7dc4a36 100644 --- a/services/idp/ui/src/containers/Login/Login.jsx +++ b/services/idp/ui/src/containers/Login/Login.jsx @@ -15,14 +15,12 @@ import Typography from '@material-ui/core/Typography'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; +import TextInput from '../../components/TextInput' + import { updateInput, executeLogonIfFormValid, advanceLogonFlow } from '../../actions/login'; import { ErrorMessage } from '../../errors'; const styles = theme => ({ - button: { - margin: theme.spacing(1), - minWidth: 100 - }, buttonProgress: { color: green[500], position: 'absolute', @@ -36,14 +34,12 @@ const styles = theme => ({ }, wrapper: { position: 'relative', - display: 'inline-block' - }, - slideContainer: { - overflowX: 'hidden', + width: '100%', + textAlign: 'center' }, message: { - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2) + marginTop: 5, + marginBottom: 5 } }); @@ -61,6 +57,29 @@ function Login(props) { } = props; const { t } = useTranslation(); + const loginFailed = errors.http; + const hasError = errors.http || errors.username || errors.password; + const errorMessage = errors.http + ? + : (errors.username + ? + : ); + const extraPropsUsername = { + "aria-invalid" : (errors.username || errors.http) ? 'true' : 'false' + }; + const extraPropsPassword = { + "aria-invalid" : (errors.password || errors.http) ? 'true' : 'false', + }; + + if(errors.username || errors.http){ + extraPropsUsername['extraClassName'] = 'error'; + extraPropsUsername['aria-describedby'] = 'oc-login-error-message'; + } + + if(errors.password || errors.http){ + extraPropsPassword['extraClassName'] = 'error'; + extraPropsPassword['aria-describedby'] = 'oc-login-error-message'; + } useEffect(() => { if (hello && hello.state && history.action !== 'PUSH') { @@ -106,62 +125,48 @@ function Login(props) { }, [hello, t]); return ( - - - {t("konnect.login.headline", "Sign in")} - - -
this.logon(event)}> - } - fullWidth - margin="dense" - autoFocus - inputProps={{ - autoCapitalize: 'off', - spellCheck: 'false' - }} - value={username} - onChange={handleChange('username')} - autoComplete="kopano-account username" - /> - } - fullWidth - margin="dense" - onChange={handleChange('password')} - autoComplete="kopano-account current-password" - /> - +
+

Login

+ handleNextClick(event)}> + + + {hasError && {errorMessage}}
+
{loading && }
- - - {renderIf(errors.http)(() => ( - - - - ))} - - {hello?.details?.branding?.signinPageText && {hello.details.branding.signinPageText}} - +
); } diff --git a/services/idp/ui/src/fancy-background.css b/services/idp/ui/src/fancy-background.css index a4f650d38..67691eaac 100644 --- a/services/idp/ui/src/fancy-background.css +++ b/services/idp/ui/src/fancy-background.css @@ -12,6 +12,6 @@ background-size: cover; background-repeat: no-repeat; background-position: center; - background-image: url(./images/loginscreen-bg.svg); + background-image: url(./images/background.jpg); z-index: 0; } diff --git a/services/idp/ui/src/images/background.jpg b/services/idp/ui/src/images/background.jpg new file mode 100644 index 000000000..11121f427 Binary files /dev/null and b/services/idp/ui/src/images/background.jpg differ diff --git a/services/idp/ui/src/index.tsx b/services/idp/ui/src/index.tsx index 3e0f9690e..0d163ba23 100644 --- a/services/idp/ui/src/index.tsx +++ b/services/idp/ui/src/index.tsx @@ -2,16 +2,13 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; - import './i18n'; import App from './App'; import store from './store'; +import './app.css'; + ReactDOM.render( diff --git a/services/idp/ui/src/theme.js b/services/idp/ui/src/theme.js index d86783a4c..f0d944c79 100644 --- a/services/idp/ui/src/theme.js +++ b/services/idp/ui/src/theme.js @@ -28,7 +28,7 @@ const theme = createMuiTheme({ tonalOffset: 0.2, }, typography: { - fontSize: 12, + fontSize: 16, useNextVariants: true, button: { textTransform: 'none', diff --git a/services/idp/yarn.lock b/services/idp/yarn.lock index b00acced7..272cddb18 100644 --- a/services/idp/yarn.lock +++ b/services/idp/yarn.lock @@ -6133,6 +6133,23 @@ __metadata: languageName: node linkType: hard +"cldr@npm:^5.2.0": + version: 5.8.0 + resolution: "cldr@npm:5.8.0" + dependencies: + escodegen: ^2.0.0 + esprima: ^4.0.1 + memoizeasync: ^1.1.0 + passerror: ^1.1.1 + pegjs: ^0.10.0 + seq: ^0.3.5 + unicoderegexp: ^0.4.1 + xmldom: ^0.4.0 + xpath: ^0.0.32 + checksum: 614f7867382c6958b8440523911dac8f4f664c64916da391ee666060af03e17d664f5adff93dd3ec1cf7386be2bf1cbb1e934fce896d71243efe2229139a1aa3 + languageName: node + linkType: hard + "cldr@npm:^7.1.1": version: 7.2.0 resolution: "cldr@npm:7.2.0" @@ -6672,6 +6689,15 @@ __metadata: languageName: node linkType: hard +"crc32@npm:^0.2.2": + version: 0.2.2 + resolution: "crc32@npm:0.2.2" + bin: + crc32: ./bin/runner.js + checksum: e4301b6bfbc081dc44a02246657581a8df27c42528bfb4879d0c93afca68040ffd4d87eb8b483df04ee8dd4ad6538844512169156be69b1a4cec23c48b81881d + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -9697,6 +9723,13 @@ __metadata: languageName: node linkType: hard +"hsv-rgb@npm:^1.0.0": + version: 1.0.0 + resolution: "hsv-rgb@npm:1.0.0" + checksum: eb1f616bd43dceb9ed322167d8007747c075e9429cfbf81721a524d0454c68140b4593a5f97d0b215d61e2ddca722edc856734fcc3e10f37fc4b770ca526eb38 + languageName: node + linkType: hard + "html-encoding-sniffer@npm:^2.0.1": version: 2.0.1 resolution: "html-encoding-sniffer@npm:2.0.1" @@ -10056,6 +10089,7 @@ __metadata: i18next-http-backend: ^1.3.2 i18next-parser: ^5.4.0 i18next-resources-to-backend: ^1.0.0 + kpop: "https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz" query-string: ^7.1.1 react: ^17.0.2 react-dev-utils: ^11.0.4 @@ -10903,6 +10937,13 @@ __metadata: languageName: node linkType: hard +"iso-639-1@npm:^2.0.5": + version: 2.1.15 + resolution: "iso-639-1@npm:2.1.15" + checksum: a201530819d33e9ce077b02c786d67b35be5d823be27b5aacc18d880e580e559e39ec5055f8e2abcdc210f46618a643bf3c74e6fe6e8255fe62059682750e595 + languageName: node + linkType: hard + "isobject@npm:^2.0.0": version: 2.1.0 resolution: "isobject@npm:2.1.0" @@ -11893,6 +11934,28 @@ __metadata: languageName: node linkType: hard +"kpop@https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz": + version: 2.2.0 + resolution: "kpop@https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz" + dependencies: + cldr: ^5.2.0 + crc32: ^0.2.2 + hsv-rgb: ^1.0.0 + iso-639-1: ^2.0.5 + peerDependencies: + "@gluejs/glue": ^0.3.0 + "@material-ui/core": ^4.8.0 + "@material-ui/icons": ^4.5.1 + notistack: ^0.8.8 + oidc-client: ^1.9.1 + react: ^16.8.0 + react-dom: ^16.8.0 + react-intl: ^2.6.0 + render-if: ^0.1.1 + checksum: 37e981756f26f396f0d1a6e9acb87eb4a4dc6ad6272f84860a236da27cea152e029daffb81e04307eba366cf4279d18a19cba50ddfdcc38d66d060593caa50ce + languageName: node + linkType: hard + "language-subtag-registry@npm:~0.3.2": version: 0.3.21 resolution: "language-subtag-registry@npm:0.3.21" @@ -19060,6 +19123,13 @@ __metadata: languageName: node linkType: hard +"xmldom@npm:^0.4.0": + version: 0.4.0 + resolution: "xmldom@npm:0.4.0" + checksum: ea975a7bd87109542ce24b19a70e50a3d64d20c757affa1a6c8cf906cbb39f0071a1bea9016beb5532388bc61dcbefddb92f3db2197dceee2b4c46343348ab3c + languageName: node + linkType: hard + "xpath@npm:^0.0.32": version: 0.0.32 resolution: "xpath@npm:0.0.32"