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
+
+
+
+
+
+
+ );
+};
+
+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;
+};