feature/1161_login_page_UI у FE_dev пре 3 година
| @@ -2,8 +2,14 @@ | |||
| <html lang="en"> | |||
| <head> | |||
| <meta charset="utf-8" /> | |||
| <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> | |||
| <link rel="icon" href="hrcenter.png" /> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |||
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | |||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | |||
| <link | |||
| href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap" | |||
| rel="stylesheet" | |||
| /> | |||
| <meta name="theme-color" content="#000000" /> | |||
| <meta | |||
| name="description" | |||
| @@ -26,6 +26,10 @@ html { | |||
| } | |||
| } | |||
| body{ | |||
| background-color: $grayFD !important; | |||
| overflow-x: hidden; | |||
| } | |||
| html, | |||
| body, | |||
| #root { | |||
| @@ -1,7 +1,7 @@ | |||
| @function pxToRem($target, $context: $base-font-size) { | |||
| @return ($target / $context) * 1rem; | |||
| @return calc($target / $context) * 1rem; | |||
| } | |||
| @function pxToRemMd($target, $context: $base-font-size-md) { | |||
| @return ($target / $context) * 1rem; | |||
| @return calc($target / $context) * 1rem; | |||
| } | |||
| @@ -1,10 +1,10 @@ | |||
| .l-page { | |||
| @include flex-column; | |||
| flex: 1 1 auto; | |||
| padding-bottom: 7rem; | |||
| // padding-bottom: 7rem; | |||
| position:relative; | |||
| @include media-below($bp-xl) { | |||
| padding-bottom: 4rem; | |||
| // padding-bottom: 4rem; | |||
| } | |||
| } | |||
| @@ -1,3 +1,4 @@ | |||
| @import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap"); | |||
| body, | |||
| div, | |||
| dl, | |||
| @@ -53,5 +54,25 @@ h3, | |||
| h4, | |||
| h5, | |||
| h6 { | |||
| font-weight: 500; | |||
| font-weight: 600 !important; | |||
| } | |||
| .text-end { | |||
| width: 100%; | |||
| text-align: end; | |||
| } | |||
| h5 { | |||
| // imported from figma | |||
| //styleName: Heading; | |||
| font-family: Source Sans Pro !important; | |||
| font-size: 24px !important; | |||
| font-weight: 600 !important; | |||
| line-height: 32px !important; | |||
| letter-spacing: 0.02em !important; | |||
| text-align: center !important; | |||
| } | |||
| .text-black{ | |||
| color: $mainBlack; | |||
| } | |||
| @@ -1,7 +1,18 @@ | |||
| @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro&display=swap'); | |||
| $base-font-size: 16px; | |||
| $base-font-size-md: 14px; | |||
| $font-family: 'Avenir Next'; | |||
| $font-family: "Source Sans Pro"; | |||
| // $font-family: "Open Sans"; | |||
| // Colors from Figma | |||
| $diligPurple: #90278f; | |||
| $mainBlue: #226cb0; | |||
| $googleGray: #f1f1f1; // color of the google auth btn | |||
| $googleGray-light: #f5f5f5; | |||
| $googleGray-dark: #e9e9e9; | |||
| $mainBlack: #272727; | |||
| $grayFD: #FDFDFD; | |||
| // Colors | |||
| $color-primary: #024f86; | |||
| @@ -50,8 +61,8 @@ $box-shadow: 0 3px 8px 0 rgba(112, 120, 135, 0.24); | |||
| $account-dropdown-shadow: 0 6px 38px 0 rgba(112, 120, 135, 0.56); | |||
| // Border Radius | |||
| $border-radius: 4px; | |||
| $border-radius-md: 8px; | |||
| $border-radius: 9px; | |||
| $border-radius-md: 16px; | |||
| // Breakpoints | |||
| $bp-xxs: 325px; | |||
| @@ -1,6 +1,6 @@ | |||
| .c-auth-card { | |||
| max-width: pxToRem(624px); | |||
| width: 100%; | |||
| max-width: pxToRem(289px); | |||
| width: 100%; | |||
| box-shadow: $box-shadow; | |||
| border-radius: $border-radius; | |||
| border: 1px solid $color-primary-light; | |||
| @@ -20,4 +20,4 @@ | |||
| line-height: 1.22; | |||
| color: $dark-blue; | |||
| font-weight: bold; | |||
| } | |||
| } | |||
| @@ -2,13 +2,13 @@ | |||
| @include outline-none; | |||
| @include button-clear; | |||
| @include flex-center; | |||
| font-size: pxToRem(18px); | |||
| font-size: pxToRem(12px) !important; | |||
| line-height: 1.35; | |||
| padding: pxToRem(8px) pxToRem(8px); | |||
| border-radius: $border-radius; | |||
| padding: pxToRem(13px) pxToRem(8px) !important; | |||
| border-radius: $border-radius !important; | |||
| box-shadow: $button-shadow-pressed; | |||
| color: inherit; | |||
| font-weight: 600; | |||
| font-weight: 600 !important; | |||
| letter-spacing: 0; | |||
| text-align: center; | |||
| text-transform: uppercase; | |||
| @@ -25,9 +25,8 @@ | |||
| } | |||
| &.c-btn--primary { | |||
| background-color: $color-primary; | |||
| background-color: $mainBlue; | |||
| color: $white; | |||
| border: 1px solid $color-primary; | |||
| &:disabled { | |||
| &:hover { | |||
| @@ -47,6 +46,29 @@ | |||
| box-shadow: $button-shadow-pressed; | |||
| } | |||
| } | |||
| &.c-btn--gray { | |||
| background-color: $googleGray; | |||
| color: $black-5; | |||
| &:disabled { | |||
| &:hover { | |||
| background-color: $googleGray; | |||
| box-shadow: none; | |||
| } | |||
| } | |||
| &:hover { | |||
| background-color: $googleGray-light; | |||
| box-shadow: $button-shadow-hover; | |||
| } | |||
| &:focus, | |||
| &:active { | |||
| background-color: $googleGray-dark; | |||
| box-shadow: $button-shadow-pressed; | |||
| } | |||
| } | |||
| &.c-btn--primary-outlined { | |||
| background-color: transparent; | |||
| @@ -1,3 +1,49 @@ | |||
| .c-login-container { | |||
| position: relative; | |||
| min-height: 100vh; | |||
| display: flex !important; | |||
| justify-content: center; | |||
| align-items: center; | |||
| padding: 0; | |||
| overflow-x: hidden; | |||
| // input[type="text"], input[type='password'] { | |||
| // width: 100%; | |||
| // border-radius: $border-radius; | |||
| // } | |||
| .rounded-input{ | |||
| border-radius: $border-radius-md !important; | |||
| } | |||
| } | |||
| .login-logo { | |||
| width: pxToRem(150px); | |||
| height: pxToRem(150px); | |||
| border-radius: 12px; | |||
| } | |||
| .l-t-rectangle, | |||
| .r-b-rectangle { | |||
| position: absolute; | |||
| width: 180px; | |||
| height: 180px; | |||
| filter: blur(155px); | |||
| border-radius: 18px; | |||
| // transform: rotate(-15deg); | |||
| } | |||
| .l-t-rectangle { | |||
| left: -60px; | |||
| top: 0px; | |||
| background: $diligPurple; | |||
| } | |||
| .r-b-rectangle { | |||
| right: -60px; | |||
| bottom: 0px; | |||
| background: $mainBlue; | |||
| } | |||
| .c-login { | |||
| &.c-login--user { | |||
| .c-login__form { | |||
| @@ -0,0 +1,20 @@ | |||
| .full-rounded{ | |||
| height: 40px; | |||
| width: 40px; | |||
| font-size: 16px; | |||
| @include flex-center; | |||
| border-radius: 50%; | |||
| background-color: $mainBlue; | |||
| color: white; | |||
| margin-right: 50px; | |||
| } | |||
| // smooth transition from app-bg color to navbar bg-color | |||
| .navLinks-cont{ | |||
| background: linear-gradient(90deg, rgba(255,255,255,0.25) 15%, white); | |||
| } | |||
| .search-Icon{ | |||
| margin-right: 50px; | |||
| cursor: pointer; | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| .hr{ | |||
| height: 2px; | |||
| background-color: $grey-12; | |||
| width: 115px; | |||
| margin: 5px 10px; | |||
| } | |||
| .hr-mid{ | |||
| padding: 2.5px 7.5px; | |||
| } | |||
| @@ -7,7 +7,7 @@ const Auth = ({ children }) => { | |||
| return ( | |||
| <div className="c-auth"> | |||
| <h1 className="c-auth__title">{t(`login.welcome`)}</h1> | |||
| <h1 className="c-auth__title">{t(`app.title`)}</h1> | |||
| {children} | |||
| </div> | |||
| ); | |||
| @@ -1,160 +1,198 @@ | |||
| import React, { useState, useMemo, useContext } from 'react'; | |||
| import React, { useState, useMemo, useContext } from "react"; | |||
| import { | |||
| AppBar, | |||
| Badge, | |||
| Box, | |||
| IconButton, | |||
| Toolbar, | |||
| Typography, | |||
| List, | |||
| ListItem, | |||
| ListItemButton, | |||
| ListItemIcon, | |||
| ListItemText, | |||
| useMediaQuery, | |||
| } from '@mui/material'; | |||
| import { useTheme } from '@mui/system'; | |||
| import MenuOutlinedIcon from '@mui/icons-material/MenuOutlined'; | |||
| import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket'; | |||
| import Brightness4Icon from '@mui/icons-material/Brightness4'; | |||
| import Brightness7Icon from '@mui/icons-material/Brightness7'; | |||
| import MenuList from './MenuListComponent'; | |||
| import Drawer from './DrawerComponent'; | |||
| import { ColorModeContext } from '../../context/ColorModeContext'; | |||
| AppBar, | |||
| // Badge, | |||
| Box, | |||
| IconButton, | |||
| Toolbar, | |||
| Typography, | |||
| List, | |||
| ListItem, | |||
| ListItemButton, | |||
| ListItemIcon, | |||
| ListItemText, | |||
| useMediaQuery, | |||
| } from "@mui/material"; | |||
| import HrLogo from "../../assets/images/hrcenter.png"; | |||
| import searchIcon from "../../assets/images/Vector.png"; | |||
| import { useTheme } from "@mui/system"; | |||
| import MenuOutlinedIcon from "@mui/icons-material/MenuOutlined"; | |||
| // import ShoppingBasketIcon from "@mui/icons-material/ShoppingBasket"; | |||
| import Brightness4Icon from "@mui/icons-material/Brightness4"; | |||
| import Brightness7Icon from "@mui/icons-material/Brightness7"; | |||
| // import MenuList from "./MenuListComponent"; | |||
| import Drawer from "./DrawerComponent"; | |||
| import { ColorModeContext } from "../../context/ColorModeContext"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const NavbarComponent = () => { | |||
| const [openDrawer, setOpenDrawer] = useState(false); | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down('sm')); | |||
| const toggleColorMode = useContext(ColorModeContext); | |||
| const navItems = [ | |||
| "ads", | |||
| "selectionFlow", | |||
| "candidates", | |||
| "planer", | |||
| "patterns", | |||
| "stats", | |||
| "users", | |||
| ]; | |||
| const [openDrawer, setOpenDrawer] = useState(false); | |||
| const theme = useTheme(); | |||
| const matches = useMediaQuery(theme.breakpoints.down("sm")); | |||
| const toggleColorMode = useContext(ColorModeContext); | |||
| const handleToggleDrawer = () => { | |||
| setOpenDrawer(!openDrawer); | |||
| }; | |||
| const { t } = useTranslation(); | |||
| const drawerContent = useMemo( | |||
| () => ( | |||
| <List> | |||
| <ListItemButton divider onClick={handleToggleDrawer}> | |||
| <ListItemIcon> | |||
| <ListItemText>Link 1</ListItemText> | |||
| </ListItemIcon> | |||
| </ListItemButton> | |||
| <ListItem divider onClick={handleToggleDrawer}> | |||
| <ListItemIcon> | |||
| <ListItemText>Link 2</ListItemText> | |||
| </ListItemIcon> | |||
| </ListItem> | |||
| <ListItem divider onClick={handleToggleDrawer}> | |||
| <ListItemText>Link 3</ListItemText> | |||
| </ListItem> | |||
| <ListItem divider> | |||
| <IconButton onClick={toggleColorMode}> | |||
| <ListItemText>Toggle {theme.palette.mode} mode</ListItemText> | |||
| {theme.palette.mode === 'dark' ? ( | |||
| <Brightness7Icon /> | |||
| ) : ( | |||
| <Brightness4Icon /> | |||
| )} | |||
| </IconButton> | |||
| </ListItem> | |||
| </List> | |||
| ), | |||
| [handleToggleDrawer] | |||
| ); | |||
| const handleToggleDrawer = () => { | |||
| setOpenDrawer(!openDrawer); | |||
| }; | |||
| return ( | |||
| <AppBar | |||
| elevation={2} | |||
| sx={{ backgroundColor: 'background.default', position: 'relative' }} | |||
| > | |||
| <Toolbar> | |||
| <Box | |||
| component="div" | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| alignItems: 'center', | |||
| width: '100%', | |||
| }} | |||
| > | |||
| {matches ? ( | |||
| <Drawer | |||
| open={openDrawer} | |||
| toggleOpen={handleToggleDrawer} | |||
| content={drawerContent} | |||
| /> | |||
| ) : ( | |||
| <Box sx={{ display: 'flex' }}> | |||
| <Typography | |||
| variant="h6" | |||
| sx={{ | |||
| marginRight: 3, | |||
| cursor: 'pointer', | |||
| color: 'text.primary', | |||
| }} | |||
| > | |||
| Link 1 | |||
| </Typography> | |||
| <Typography | |||
| variant="body1" | |||
| sx={{ | |||
| marginRight: 3, | |||
| cursor: 'pointer', | |||
| color: 'text.primary', | |||
| }} | |||
| > | |||
| Link 2 | |||
| </Typography> | |||
| <Typography | |||
| variant="subtitle1" | |||
| sx={{ | |||
| marginRight: 3, | |||
| cursor: 'pointer', | |||
| color: 'text.primary', | |||
| }} | |||
| > | |||
| Link 3 | |||
| </Typography> | |||
| </Box> | |||
| )} | |||
| <Box> | |||
| <MenuList /> | |||
| </Box> | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'center', | |||
| alignItems: 'center', | |||
| }} | |||
| > | |||
| {matches ? ( | |||
| <Box> | |||
| <IconButton onClick={handleToggleDrawer}> | |||
| <MenuOutlinedIcon /> | |||
| </IconButton> | |||
| </Box> | |||
| ) : ( | |||
| <Box> | |||
| <IconButton> | |||
| <Badge badgeContent={3} color="primary"> | |||
| <ShoppingBasketIcon color="action" /> | |||
| </Badge> | |||
| </IconButton> | |||
| <IconButton sx={{ ml: 1 }} onClick={toggleColorMode}> | |||
| {theme.palette.mode === 'dark' ? ( | |||
| <Brightness7Icon /> | |||
| ) : ( | |||
| <Brightness4Icon /> | |||
| )} | |||
| </IconButton> | |||
| </Box> | |||
| )} | |||
| </Box> | |||
| </Box> | |||
| </Toolbar> | |||
| </AppBar> | |||
| ); | |||
| const drawerContent = useMemo( | |||
| () => ( | |||
| <List> | |||
| {navItems.map((n) => ( | |||
| <ListItemButton key={n} divider onClick={handleToggleDrawer}> | |||
| <ListItemIcon> | |||
| <ListItemText | |||
| variant="body1" | |||
| sx={{ | |||
| marginRight: "50px", | |||
| cursor: "pointer", | |||
| textAlign: "right", | |||
| lineHeight: "20.11px", | |||
| fontWeight: "400", | |||
| color: "text.primary", | |||
| marginY: "0", | |||
| padding: "0", | |||
| }} | |||
| className="text-black" | |||
| > | |||
| {t("nav." + n)} | |||
| </ListItemText> | |||
| </ListItemIcon> | |||
| </ListItemButton> | |||
| ))} | |||
| <ListItem divider> | |||
| <IconButton onClick={toggleColorMode}> | |||
| <ListItemText>Toggle {theme.palette.mode} mode</ListItemText> | |||
| {theme.palette.mode === "dark" ? ( | |||
| <Brightness7Icon /> | |||
| ) : ( | |||
| <Brightness4Icon /> | |||
| )} | |||
| </IconButton> | |||
| </ListItem> | |||
| </List> | |||
| ), | |||
| [handleToggleDrawer] | |||
| ); | |||
| return ( | |||
| <AppBar | |||
| elevation={0} | |||
| sx={{ | |||
| backgroundColor: "transparent", | |||
| position: "relative", | |||
| width: "100%", | |||
| padding: "0", | |||
| }} | |||
| > | |||
| <Toolbar | |||
| sx={{ | |||
| padding: "0px", | |||
| margin: "0px", | |||
| width: "100%", | |||
| }} | |||
| > | |||
| <Box | |||
| component="div" | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "space-between", | |||
| alignItems: "center", | |||
| width: "100%", | |||
| padding: "0px", | |||
| margin: "0px", | |||
| // md: {paddingX: "72px"} | |||
| // paddingX: "72px" | |||
| }} | |||
| > | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| justifyContent: "center", | |||
| alignItems: "center", | |||
| }} | |||
| > | |||
| {matches ? ( | |||
| <Box> | |||
| <IconButton onClick={handleToggleDrawer}> | |||
| <MenuOutlinedIcon /> | |||
| </IconButton> | |||
| </Box> | |||
| ) : ( | |||
| <Box> | |||
| {/* <IconButton onClick={handleToggleDrawer}> | |||
| <MenuOutlinedIcon /> | |||
| </IconButton> */} | |||
| <img | |||
| style={{ height: "37px", width: "37px", marginLeft: "72px" }} | |||
| src={HrLogo} | |||
| /> | |||
| </Box> | |||
| )} | |||
| </Box> | |||
| {matches ? ( | |||
| <Drawer | |||
| open={openDrawer} | |||
| toggleOpen={handleToggleDrawer} | |||
| content={drawerContent} | |||
| /> | |||
| ) : ( | |||
| <Box | |||
| sx={{ | |||
| display: "flex", | |||
| alignItems: "center", | |||
| paddingRight: "20px", | |||
| justifyContent: "flex-end", | |||
| height: "81px", | |||
| width: "1076px", | |||
| borderBottom: "1px solid #F4F4F4", | |||
| boxSizing: "border-box", | |||
| }} | |||
| className="navLinks-cont" | |||
| > | |||
| {navItems.map((n) => ( | |||
| <Typography | |||
| variant="body1" | |||
| key={n} | |||
| sx={{ | |||
| marginRight: "50px", | |||
| cursor: "pointer", | |||
| textAlign: "right", | |||
| lineHeight: "20.11px", | |||
| fontWeight: "400", | |||
| // color: "text.primary", | |||
| marginY: "0", | |||
| padding: "0", | |||
| }} | |||
| className="text-black" | |||
| > | |||
| {t("nav." + n)} | |||
| </Typography> | |||
| ))} | |||
| <div className="search-Icon"> | |||
| <img src={searchIcon} /> | |||
| </div> | |||
| <div className="full-rounded">DR</div> | |||
| </Box> | |||
| )} | |||
| {/* <Box> | |||
| <MenuList /> | |||
| </Box> */} | |||
| </Box> | |||
| </Toolbar> | |||
| </AppBar> | |||
| ); | |||
| }; | |||
| export default NavbarComponent; | |||
| @@ -3,16 +3,20 @@ import i18n from 'i18next'; | |||
| import { initReactI18next } from 'react-i18next'; | |||
| import enTranslations from './resources/en'; | |||
| import rsTranslations from './resources/rs'; | |||
| i18n.use(initReactI18next).init({ | |||
| lng: 'en', | |||
| lng: 'rs', | |||
| fallbackLng: 'en', | |||
| debug: false, | |||
| supportedLngs: ['en'], | |||
| supportedLngs: ['en','rs'], | |||
| resources: { | |||
| en: { | |||
| translation: enTranslations, | |||
| }, | |||
| rs:{ | |||
| translation: rsTranslations, | |||
| }, | |||
| }, | |||
| interpolation: { | |||
| format: (value, format) => { | |||
| @@ -1,6 +1,6 @@ | |||
| export default { | |||
| app: { | |||
| title: 'React template' | |||
| title: 'HR Center' | |||
| }, | |||
| refresh: { | |||
| title: 'Are you active?', | |||
| @@ -15,6 +15,7 @@ export default { | |||
| continue: 'Continue', | |||
| labelUsername: 'Username', | |||
| labelPassword: 'Password', | |||
| or: 'or', | |||
| next: 'Next', | |||
| nextPage: 'Next page', | |||
| previousPage: 'Previous page', | |||
| @@ -40,7 +41,7 @@ export default { | |||
| }, | |||
| }, | |||
| login: { | |||
| welcome: 'React template', | |||
| welcome: 'Welcome!', | |||
| dontHaveAccount: "Don't have an account? ", | |||
| emailFormat: 'Invalid email address format.', | |||
| emailRequired: 'An email or username is required.', | |||
| @@ -57,6 +58,7 @@ export default { | |||
| forgotYourPassword: 'Forgot your password?', | |||
| forgotPasswordEmail:'Email', | |||
| useDifferentEmail: 'Use different email address or username', | |||
| signInWithGoogle: 'Continue with google' | |||
| }, | |||
| password: { | |||
| weak: 'weak', | |||
| @@ -89,5 +91,14 @@ export default { | |||
| apiErrors:{ | |||
| ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty", | |||
| UsernameDoesNotExist: "Username does not exist" | |||
| }, | |||
| nav:{ | |||
| ads: 'Ads', | |||
| selectionFlow: 'Selection flow', | |||
| candidates: 'Candidates', | |||
| planer: 'Planer', | |||
| patterns: 'Patterns', | |||
| stats: 'Stats', | |||
| users: 'Users' | |||
| } | |||
| }; | |||
| @@ -0,0 +1,110 @@ | |||
| export default { | |||
| app: { | |||
| title: 'HR Centar' | |||
| }, | |||
| refresh: { | |||
| // title: 'Are you active?', | |||
| // cta: | |||
| // "You were registered as not active, please confirm that you are active in the next minute, if you don't you will be logged out.", | |||
| }, | |||
| common: { | |||
| // close: 'Close', | |||
| // trademark: 'TM', | |||
| // search: 'Search', | |||
| // error: 'Error', | |||
| // continue: 'Continue', | |||
| labelUsername: 'Korisničko ime', | |||
| labelPassword: 'Šifra', | |||
| or: 'ili', | |||
| // next: 'Next', | |||
| // nextPage: 'Next page', | |||
| // previousPage: 'Previous page', | |||
| // back: 'Back', | |||
| // goBack: 'Go Back', | |||
| // ok: 'Ok', | |||
| // done: 'Done', | |||
| // confirm: 'Confirm', | |||
| // printDownload: 'Print/Download', | |||
| // cancel: 'Cancel', | |||
| // remove: 'Remove', | |||
| // invite: 'Invite', | |||
| // save: 'Save', | |||
| // complete: 'Complete', | |||
| // download: 'Download', | |||
| // yes: 'Yes', | |||
| // no: 'No', | |||
| // to: 'to', | |||
| // select: 'Select...', | |||
| // none: 'None', | |||
| // date: { | |||
| // range: '{{start}} to {{end}}', | |||
| // }, | |||
| }, | |||
| login: { | |||
| welcome: 'Dobrodošli!', | |||
| // dontHaveAccount: "Don't have an account? ", | |||
| // emailFormat: 'Invalid email address format.', | |||
| // emailRequired: 'An email or username is required.', | |||
| // noUsers: 'There are no users with that email.', | |||
| // passwordStrength: 'Your password is {{strength}}.', | |||
| // passwordLength: 'Your password contain between 8 and 50 characters.', | |||
| // signUpRecommendation: 'Sign up', | |||
| // email: 'Please enter your email address or username to log in:', | |||
| logInTitle: 'Prijavi se', | |||
| logIn: 'Prijavi se', | |||
| // signUp: 'Sign Up', | |||
| usernameRequired: 'Potrebno je uneti korisničko ime.', | |||
| passwordRequired: 'Potrebno je uneti šifru.', | |||
| forgotYourPassword: 'Zaboravio/la si šifru?', | |||
| // _useDifferentEmail: 'Use different email address or username', | |||
| // get useDifferentEmail() { | |||
| // return this._useDifferentEmail; | |||
| // }, | |||
| // set useDifferentEmail(value) { | |||
| // this._useDifferentEmail = value; | |||
| // }, | |||
| signInWithGoogle: 'Prijava putem Google-a' | |||
| }, | |||
| // password: { | |||
| // weak: 'weak', | |||
| // average: 'average', | |||
| // good: 'good', | |||
| // strong: 'strong', | |||
| // }, | |||
| // forgotPassword: { | |||
| // title: 'Forgot Password', | |||
| // label: 'Send email', | |||
| // emailRequired: 'An email is required.', | |||
| // emailFormat: 'Invalid email address format.', | |||
| // forgotPassword: { | |||
| // title: 'Forgot Password', | |||
| // subtitle: | |||
| // 'Please answer the security question to gain access to your account:', | |||
| // label: 'Reset Password', | |||
| // }, | |||
| // }, | |||
| // notFound: { | |||
| // text: "We're sorry but we couldn't find the page you were looking for.", | |||
| // goBack: 'Go back to homepage', | |||
| // }, | |||
| // errorPage: { | |||
| // text: | |||
| // "We're sorry, an internal server error came up. Please be patient or try again later.", | |||
| // goBack: 'Go back to homepage', | |||
| // logout: 'Logout', | |||
| // }, | |||
| // apiErrors:{ | |||
| // ClientIpAddressIsNullOrEmpty:"Client Ip address is null or empty", | |||
| // UsernameDoesNotExist: "Username does not exist" | |||
| // }, | |||
| nav:{ | |||
| ads: 'Oglasi', | |||
| selectionFlow: 'Tok Selekcije', | |||
| candidates: 'Kandidati', | |||
| planer: 'Planer', | |||
| patterns: 'Šabloni', | |||
| stats: 'Statistika', | |||
| users: 'Korisnici' | |||
| } | |||
| }; | |||
| @@ -1,4 +1,4 @@ | |||
| body { | |||
| /* body { | |||
| margin: 0; | |||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', | |||
| 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', | |||
| @@ -10,4 +10,4 @@ body { | |||
| code { | |||
| font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', | |||
| monospace; | |||
| } | |||
| } */ | |||
| @@ -17,6 +17,17 @@ | |||
| @import './assets/styles/components/forgot-password'; | |||
| @import './assets/styles/components/input'; | |||
| @import './assets/styles/components/error-page'; | |||
| @import './assets/styles/components/rules'; | |||
| @import './assets/styles/components/nav'; | |||
| @import './assets/styles/layout'; | |||
| @import './assets/styles/overwrite'; | |||
| @import './assets/styles/utility'; | |||
| .flex-center{ | |||
| @include flex-center; | |||
| } | |||
| // ================= mui overrides | |||
| .css-122f4x8-MuiToolbar-root { | |||
| padding: 0 !important; | |||
| } | |||
| @@ -1,17 +1,19 @@ | |||
| import React from 'react'; | |||
| import { Box, Grid } from '@mui/material'; | |||
| import Navbar from '../../components/MUI/NavbarComponent'; | |||
| import Modals from '../../components/MUI/Examples/ModalsExample'; | |||
| import DataGrid from '../../components/MUI/Examples/DataGridExample'; | |||
| import PagingSortingFiltering from '../../components/MUI/Examples/PagingSortingFilteringExample'; | |||
| import PagingSortingFilteringServerSide from '../../components/MUI/Examples/PagingSortingFilteringExampleServerSide'; | |||
| import RandomDataProvider from '../../context/RandomDataContext'; | |||
| import React from "react"; | |||
| // import { Box, Grid } from '@mui/material'; | |||
| import Navbar from "../../components/MUI/NavbarComponent"; | |||
| // import Modals from '../../components/MUI/Examples/ModalsExample'; | |||
| // import DataGrid from '../../components/MUI/Examples/DataGridExample'; | |||
| // import PagingSortingFiltering from '../../components/MUI/Examples/PagingSortingFilteringExample'; | |||
| // import PagingSortingFilteringServerSide from '../../components/MUI/Examples/PagingSortingFilteringExampleServerSide'; | |||
| // import RandomDataProvider from '../../context/RandomDataContext'; | |||
| const HomePage = () => { | |||
| return ( | |||
| <> | |||
| <Navbar /> | |||
| <Box sx={{ mt: 4, mx: 4 }}> | |||
| return ( | |||
| <div> | |||
| <Navbar /> | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| {/* <Box sx={{ mt: 4, mx: 4 }}> | |||
| <Grid container spacing={2} justifyContent="center"> | |||
| <Grid item xs={12} md={3}> | |||
| <Modals /> | |||
| @@ -22,16 +24,16 @@ const HomePage = () => { | |||
| <Grid item xs={12} md={9}> | |||
| <PagingSortingFiltering /> | |||
| </Grid> | |||
| <Grid item xs={12} md={9}> | |||
| {/* Move to higher components? */} | |||
| <RandomDataProvider> | |||
| <Grid item xs={12} md={9}> */} | |||
| {/* Move to higher components? */} | |||
| {/* <RandomDataProvider> | |||
| <PagingSortingFilteringServerSide /> | |||
| </RandomDataProvider> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </> | |||
| ); | |||
| </Box> */} | |||
| </div> | |||
| ); | |||
| }; | |||
| export default HomePage; | |||
| @@ -11,6 +11,7 @@ import Button from '../../components/Button/Button'; | |||
| import TextField from '../../components/InputFields/TextField'; | |||
| import Auth from '../../components/Auth/Auth'; | |||
| import AuthCard from '../../components/AuthCards/AuthCard'; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| @@ -1,201 +1,219 @@ | |||
| /* eslint-disable */ | |||
| import React, { useState } from 'react'; | |||
| import PropTypes from 'prop-types'; | |||
| import { useFormik } from 'formik'; | |||
| import { useDispatch, useSelector } from 'react-redux'; | |||
| import { NavLink } from 'react-router-dom'; | |||
| import * as Yup from 'yup'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import i18next from 'i18next'; | |||
| import React, { useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { useFormik } from "formik"; | |||
| import { useDispatch, useSelector } from "react-redux"; | |||
| import { NavLink } from "react-router-dom"; | |||
| import * as Yup from "yup"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import HrLogo from "../../assets/images/hrcenter.png"; | |||
| import DiligLogo from "../../assets/images/logo_horizontal_black.png"; | |||
| import googleLogo from "../../assets/images/google1.png"; | |||
| import i18next from "i18next"; | |||
| import { | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from '../../store/actions/login/loginActions'; | |||
| import { selectLoginError } from '../../store/selectors/loginSelectors'; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from '../../constants/pages'; | |||
| clearLoginErrors, | |||
| fetchUser, | |||
| } from "../../store/actions/login/loginActions"; | |||
| import { selectLoginError } from "../../store/selectors/loginSelectors"; | |||
| import { FORGOT_PASSWORD_PAGE, HOME_PAGE } from "../../constants/pages"; | |||
| import { | |||
| Box, | |||
| Button, | |||
| Container, | |||
| Grid, | |||
| IconButton, | |||
| InputAdornment, | |||
| Link, | |||
| TextField, | |||
| Typography, | |||
| } from '@mui/material'; | |||
| import { Visibility, VisibilityOff } from '@mui/icons-material'; | |||
| import Backdrop from '../../components/MUI/BackdropComponent'; | |||
| import ErrorMessage from '../../components/MUI/ErrorMessageComponent'; | |||
| import { selectIsLoadingByActionType } from '../../store/selectors/loadingSelectors'; | |||
| import { LOGIN_USER_LOADING } from '../../store/actions/login/loginActionConstants'; | |||
| Box, | |||
| Button, | |||
| Container, | |||
| Grid, | |||
| IconButton, | |||
| InputAdornment, | |||
| Link, | |||
| TextField, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { Visibility, VisibilityOff } from "@mui/icons-material"; | |||
| import Backdrop from "../../components/MUI/BackdropComponent"; | |||
| import ErrorMessage from "../../components/MUI/ErrorMessageComponent"; | |||
| import { selectIsLoadingByActionType } from "../../store/selectors/loadingSelectors"; | |||
| import { LOGIN_USER_LOADING } from "../../store/actions/login/loginActionConstants"; | |||
| import { fontSize } from "@mui/system"; | |||
| const LoginPage = ({ history }) => { | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const error = useSelector(selectLoginError); | |||
| const dispatch = useDispatch(); | |||
| const { t } = useTranslation(); | |||
| const error = useSelector(selectLoginError); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| const [showPassword, setShowPassword] = useState(false); | |||
| const handleClickShowPassword = () => setShowPassword(!showPassword); | |||
| const handleMouseDownPassword = () => setShowPassword(!showPassword); | |||
| // When user refreshes page | |||
| // useEffect(() => { | |||
| // function redirectClient() { | |||
| // if (!tokens.RefreshToken && !tokens.JwtToken) { | |||
| // return; | |||
| // } | |||
| // } | |||
| // When user refreshes page | |||
| // useEffect(() => { | |||
| // function redirectClient() { | |||
| // if (!tokens.RefreshToken && !tokens.JwtToken) { | |||
| // return; | |||
| // } | |||
| // } | |||
| // redirectClient(); | |||
| // }, [history, tokens]); | |||
| // redirectClient(); | |||
| // }, [history, tokens]); | |||
| const isLoading = useSelector( | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING) | |||
| ); | |||
| const isLoading = useSelector( | |||
| selectIsLoadingByActionType(LOGIN_USER_LOADING) | |||
| ); | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleApiResponseSuccess = () => { | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| state: { | |||
| from: history.location.pathname, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| const { username: Username, password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const handleSubmit = (values) => { | |||
| const { username: Username, password: Password } = values; | |||
| dispatch(clearLoginErrors()); | |||
| dispatch( | |||
| fetchUser({ | |||
| Username, | |||
| Password, | |||
| handleApiResponseSuccess, | |||
| }) | |||
| ); | |||
| }; | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| username: '', | |||
| password: '', | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| username: Yup.string().required(t('login.usernameRequired')), | |||
| password: Yup.string().required(t('login.passwordRequired')), | |||
| }), | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| username: "", | |||
| password: "", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| username: Yup.string().required(t("login.usernameRequired")), | |||
| password: Yup.string().required(t("login.passwordRequired")), | |||
| }), | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| return ( | |||
| <Container component="main" maxWidth="md"> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 32, | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| alignItems: 'center', | |||
| }} | |||
| > | |||
| <Typography component="h1" variant="h5"> | |||
| {t('login.logInTitle')} | |||
| </Typography> | |||
| {error && <ErrorMessage error={error} />} | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: 'relative', mt: 1, p: 1 }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={isLoading} /> | |||
| <TextField | |||
| name="username" | |||
| label={t('common.labelUsername')} | |||
| margin="normal" | |||
| value={formik.values.username} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.username && Boolean(formik.errors.username)} | |||
| helperText={formik.touched.username && formik.errors.username} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| name="password" | |||
| label={t('common.labelPassword')} | |||
| margin="normal" | |||
| type={showPassword ? 'text' : 'password'} | |||
| value={formik.values.password} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <InputAdornment position="end"> | |||
| <IconButton | |||
| onClick={handleClickShowPassword} | |||
| onMouseDown={handleMouseDownPassword} | |||
| > | |||
| {showPassword ? <Visibility /> : <VisibilityOff />} | |||
| </IconButton> | |||
| </InputAdornment> | |||
| ), | |||
| }} | |||
| /> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| > | |||
| {t('login.logIn')} | |||
| </Button> | |||
| <Grid container> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: 'center', md: 'left' } }} | |||
| > | |||
| <Link | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t('login.forgotYourPassword')} | |||
| </Link> | |||
| </Grid> | |||
| <Grid | |||
| item | |||
| xs={12} | |||
| md={6} | |||
| sx={{ textAlign: { xs: 'center', md: 'right' } }} | |||
| > | |||
| <Link | |||
| to="#" | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| > | |||
| {t('login.dontHaveAccount')} | |||
| </Link> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| return ( | |||
| <Container | |||
| component="main" | |||
| maxWidth="xl" | |||
| className="c-login-container" | |||
| fullwidth="true" | |||
| > | |||
| <div className="l-t-rectangle"></div> | |||
| <div className="r-b-rectangle"></div> | |||
| <Box | |||
| sx={{ | |||
| marginTop: 2, | |||
| width: 289, | |||
| height: 684, | |||
| display: "flex", | |||
| flexDirection: "column", | |||
| alignItems: "center", | |||
| }} | |||
| > | |||
| <img src={HrLogo} className="login-logo" /> | |||
| <Typography variant="h5" sx={{ m: 2, mt: 3 }}> | |||
| {t("login.welcome")} | |||
| </Typography> | |||
| {error && <ErrorMessage error={error} />} | |||
| <Box | |||
| component="form" | |||
| onSubmit={formik.handleSubmit} | |||
| sx={{ position: "relative" }} | |||
| > | |||
| <Backdrop position="absolute" isLoading={isLoading} /> | |||
| {/* <label>Username:</label><br></br> | |||
| <input type='text' value={formik.values.username} | |||
| onChange={formik.handleChange}/> */} | |||
| <TextField | |||
| name="username" | |||
| label={t("common.labelUsername")} | |||
| margin="normal" | |||
| value={formik.values.username} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.username && Boolean(formik.errors.username)} | |||
| helperText={formik.touched.username && formik.errors.username} | |||
| autoFocus | |||
| fullWidth | |||
| /> | |||
| <TextField | |||
| className="rounded-input" | |||
| name="password" | |||
| label={t("common.labelPassword")} | |||
| margin="normal" | |||
| type={showPassword ? "text" : "password"} | |||
| value={formik.values.password} | |||
| onChange={formik.handleChange} | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <InputAdornment position="end"> | |||
| <IconButton | |||
| onClick={handleClickShowPassword} | |||
| onMouseDown={handleMouseDownPassword} | |||
| > | |||
| {showPassword ? <Visibility /> : <VisibilityOff />} | |||
| </IconButton> | |||
| </InputAdornment> | |||
| ), | |||
| }} | |||
| /> | |||
| <Grid container> | |||
| <Grid item sm={12} sx={{ textAlign: "end" }}> | |||
| <Link | |||
| to={FORGOT_PASSWORD_PAGE} | |||
| component={NavLink} | |||
| variant="body2" | |||
| underline="hover" | |||
| className="text-end" | |||
| sx={{ fontSize: 12 }} | |||
| > | |||
| {t("login.forgotYourPassword")} | |||
| </Link> | |||
| </Grid> | |||
| </Grid> | |||
| <Button | |||
| type="submit" | |||
| variant="contained" | |||
| sx={{ mt: 3, mb: 2 }} | |||
| fullWidth | |||
| className="c-btn c-btn--primary" | |||
| > | |||
| {t("login.logIn")} | |||
| </Button> | |||
| <div className="flex-center"> | |||
| <div className="hr hr-s"></div> | |||
| <div className="hr-mid">{t("common.or")}</div> | |||
| <div className="hr hr-e"></div> | |||
| </div> | |||
| <Button | |||
| className="c-btn c-btn--gray flex-center" | |||
| sx={{ width: "100%", mt: 2 }} | |||
| > | |||
| <img src={googleLogo} style={{ marginRight: "15px" }} /> | |||
| <Typography sx={{ m: 0, p: 0 }} variant="buttonText"> | |||
| {t("login.signInWithGoogle")} | |||
| </Typography> | |||
| </Button> | |||
| <div className="flex-center"> | |||
| <img src={DiligLogo} style={{ margin: "70px auto 0px auto" }} /> | |||
| </div> | |||
| </Box> | |||
| </Box> | |||
| </Container> | |||
| ); | |||
| }; | |||
| LoginPage.propTypes = { | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| history: PropTypes.shape({ | |||
| replace: PropTypes.func, | |||
| push: PropTypes.func, | |||
| location: PropTypes.shape({ | |||
| pathname: PropTypes.string, | |||
| }), | |||
| }), | |||
| }; | |||
| export default LoginPage; | |||