diff --git a/src/components/app-router.jsx b/src/components/app-router.jsx new file mode 100644 index 0000000..2f90437 --- /dev/null +++ b/src/components/app-router.jsx @@ -0,0 +1,24 @@ +import { Route, Routes } from 'react-router-dom'; +import NotFound from '../pages/errors/404'; +import Home from '../pages/home'; +import Planning from '../pages/planning'; +import Playlists from '../pages/playlists'; +import Playlist from '../pages/playlist'; +import Files from '../pages/files'; +import Authentication from '../pages/auth'; + +const AppRouter = () => { + return ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ); +}; + +export default AppRouter; diff --git a/src/components/layout.jsx b/src/components/layout.jsx new file mode 100644 index 0000000..d85f341 --- /dev/null +++ b/src/components/layout.jsx @@ -0,0 +1,16 @@ +import { AppShell } from '@mantine/core'; +import HeaderSearch from './header'; +import AppRouter from './app-router'; +import { BrowserRouter } from 'react-router-dom'; + +const Layout = () => { + return ( + + }> + + + + ); +}; + +export default Layout; diff --git a/src/components/toggle-colorscheme.jsx b/src/components/toggle-colorscheme.jsx new file mode 100644 index 0000000..602315f --- /dev/null +++ b/src/components/toggle-colorscheme.jsx @@ -0,0 +1,32 @@ +import { Switch, Group, useMantineTheme, createStyles } from '@mantine/core'; +import { IconSun, IconMoonStars } from '@tabler/icons-react'; +import { useColorSchemeToggle } from '../tools/color-scheme-toggle' + +const useStyles = createStyles((theme) => ({ + group: { + [theme.fn.smallerThan('xs')]: { + display: 'none', + }, + }, +})); + +const SwitchToggle = () => { + const { classes } = useStyles(); + const [ colorScheme, toggleColorScheme ] = useColorSchemeToggle(); + + const theme = useMantineTheme() + + return ( + + } + offLabel={} + /> + + ); +}; + +export default SwitchToggle; diff --git a/src/pages/auth.jsx b/src/pages/auth.jsx new file mode 100644 index 0000000..1a653ba --- /dev/null +++ b/src/pages/auth.jsx @@ -0,0 +1,67 @@ +import { TextInput, PasswordInput, Checkbox, Anchor, Paper, Title, Container, Group, Button } from '@mantine/core'; +import { isNotEmpty, useForm } from '@mantine/form'; +import { useAuth } from '../tools/auth-provider'; +import { useNavigate } from 'react-router-dom'; +import { useEffect } from 'react'; + +const Authentication = () => { + const { setRole } = useAuth(); + const navigate = useNavigate(); + const form = useForm({ + initialValues: { + name: '', + password: '', + remember: false, + }, + validate: { + name: isNotEmpty('Name is required'), + password: isNotEmpty('Password is required'), + }, + }); + + useEffect(() => { + localStorage.setItem('role', form.values.name); + }, [form.values.name]); + + const handleLogin = (e) => { + e.preventDefault(); + if (form.validate().hasErrors) return; + setRole(form.values.name); + navigate('/'); + }; + + return ( + + ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })} + > + Connect to signage + + + +
+ + + + + + Forgot password(s)? + + + + +
+
+ ); +}; + +export default Authentication; diff --git a/src/pages/errors/404.jsx b/src/pages/errors/404.jsx new file mode 100644 index 0000000..ab5c731 --- /dev/null +++ b/src/pages/errors/404.jsx @@ -0,0 +1,63 @@ +import { createStyles, Title, Text, Button, Container, Group, rem } from '@mantine/core'; +import { useNavigate } from 'react-router-dom'; + +const useStyles = createStyles((theme) => ({ + root: { + paddingTop: rem(80), + paddingBottom: rem(80), + }, + + label: { + textAlign: 'center', + fontWeight: 900, + fontSize: rem(220), + lineHeight: 1, + marginBottom: `calc(${theme.spacing.xl} * 1.5)`, + color: theme.colorScheme === 'dark' ? theme.colors.dark[4] : theme.colors.gray[2], + + [theme.fn.smallerThan('sm')]: { + fontSize: rem(120), + }, + }, + + title: { + fontFamily: `Greycliff CF, ${theme.fontFamily}`, + textAlign: 'center', + fontWeight: 900, + fontSize: rem(38), + + [theme.fn.smallerThan('sm')]: { + fontSize: rem(32), + }, + }, + + description: { + maxWidth: rem(500), + margin: 'auto', + marginTop: theme.spacing.xl, + marginBottom: `calc(${theme.spacing.xl} * 1.5)`, + }, +})); + +const NotFound = () => { + const { classes } = useStyles(); + const navigate = useNavigate(); + + return ( + +
404
+ You have found a secret place. + + Unfortunately, this is only a 404 page. You may have mistyped the address, or the page has been moved to + another URL. + + + + +
+ ); +}; + +export default NotFound; diff --git a/src/tools/auth-provider.js b/src/tools/auth-provider.js new file mode 100644 index 0000000..38e931b --- /dev/null +++ b/src/tools/auth-provider.js @@ -0,0 +1,19 @@ +import { createContext, useState, useContext } from 'react'; + +const AuthContext = createContext(); + +const AuthProvider = ({ children }) => { + const [role, setRole] = useState(localStorage.getItem('role') ?? 'user'); + + return {children}; +}; + +const logout = () => { + localStorage.removeItem('role'); + window.location.href = '/auth'; +}; + +const useAuth = () => useContext(AuthContext); + +export default AuthContext; +export { AuthProvider, logout, useAuth }; diff --git a/src/tools/color-scheme-toggle.jsx b/src/tools/color-scheme-toggle.jsx new file mode 100644 index 0000000..3f3904a --- /dev/null +++ b/src/tools/color-scheme-toggle.jsx @@ -0,0 +1,15 @@ +import { useLocalStorage, useColorScheme } from '@mantine/hooks'; + +const useColorSchemeToggle = function(){ + const defaultColorScheme = useColorScheme() + const [colorScheme, setColorScheme] = useLocalStorage({ + key: 'mantine-color-scheme', + defaultValue: defaultColorScheme, + getInitialValueInEffect: true, + }); + const toggleColorScheme = () => + setColorScheme((colorScheme === 'dark' ? 'light' : 'dark')); + return [ colorScheme, toggleColorScheme ]; +}; + +export { useColorSchemeToggle }; diff --git a/src/tools/grant-access.jsx b/src/tools/grant-access.jsx new file mode 100644 index 0000000..1e1291d --- /dev/null +++ b/src/tools/grant-access.jsx @@ -0,0 +1,8 @@ +import { useAuth } from './auth-provider'; + +const GrantAccess = ({ roles, children }) => { + const { role } = useAuth(); + return roles.includes(role) ? children : null; +}; + +export default GrantAccess; diff --git a/src/tools/timeUtil.js b/src/tools/timeUtil.js new file mode 100644 index 0000000..86d0e64 --- /dev/null +++ b/src/tools/timeUtil.js @@ -0,0 +1,16 @@ +export const parseTime = (preparationTime) => { + let res = ''; + let hours = Math.floor(preparationTime / 3600); + res += hours > 0 ? `${hours}h` : ''; + + let min = Math.floor((preparationTime % 3600) / 60); + if (min > 0 && res != '') res += ' '; + res += min > 0 ? `${min}m` : ''; + + let sec = Math.floor((preparationTime % 3600) % 60); + if (sec > 0 && res != '') res += ' '; + res += sec > 0 ? `${sec}s` : ''; + + if (res == '') res = '0s'; + return res; +};