2023-02-14 10:14:28 +01:00
|
|
|
import { MetaFunction, LoaderArgs, LinksFunction } from '@remix-run/node';
|
|
|
|
|
import { json } from '@remix-run/node';
|
2023-02-11 03:14:14 +01:00
|
|
|
import {
|
|
|
|
|
Links,
|
|
|
|
|
LiveReload,
|
|
|
|
|
Meta,
|
|
|
|
|
Outlet,
|
|
|
|
|
Scripts,
|
|
|
|
|
ScrollRestoration,
|
2023-02-14 10:14:28 +01:00
|
|
|
Link,
|
2023-02-11 03:14:14 +01:00
|
|
|
useCatch,
|
|
|
|
|
useMatches,
|
2023-02-14 10:14:28 +01:00
|
|
|
useTransition,
|
|
|
|
|
Form,
|
|
|
|
|
useLocation
|
|
|
|
|
} from '@remix-run/react';
|
|
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
import {
|
|
|
|
|
MantineProvider,
|
|
|
|
|
createEmotionCache,
|
|
|
|
|
useMantineTheme,
|
|
|
|
|
ColorScheme,
|
|
|
|
|
ColorSchemeProvider,
|
|
|
|
|
AppShell,
|
|
|
|
|
Navbar,
|
|
|
|
|
Header,
|
|
|
|
|
Text,
|
|
|
|
|
MediaQuery,
|
|
|
|
|
Burger,
|
|
|
|
|
ActionIcon,
|
|
|
|
|
useMantineColorScheme,
|
|
|
|
|
Button,
|
|
|
|
|
Box,
|
|
|
|
|
Group,
|
|
|
|
|
UnstyledButton,
|
2023-02-18 21:54:57 +01:00
|
|
|
ThemeIcon,
|
2023-02-23 01:20:18 +01:00
|
|
|
NavLink,
|
2023-02-23 14:17:29 +01:00
|
|
|
Menu,
|
|
|
|
|
Badge
|
2023-02-14 10:14:28 +01:00
|
|
|
} from '@mantine/core';
|
|
|
|
|
import { useColorScheme, useLocalStorage } from '@mantine/hooks';
|
|
|
|
|
import { StylesPlaceholder } from '@mantine/remix';
|
|
|
|
|
import {
|
|
|
|
|
NavigationProgress,
|
|
|
|
|
startNavigationProgress,
|
|
|
|
|
completeNavigationProgress
|
|
|
|
|
} from '@mantine/nprogress';
|
2023-02-23 14:17:29 +01:00
|
|
|
import type { User as UserType } from './models/user.server';
|
2023-02-14 10:14:28 +01:00
|
|
|
import { getUser } from './session.server';
|
|
|
|
|
import {
|
|
|
|
|
Sun,
|
|
|
|
|
Moon,
|
|
|
|
|
Clock,
|
|
|
|
|
LogOut,
|
|
|
|
|
LogIn,
|
|
|
|
|
Home,
|
|
|
|
|
Briefcase,
|
|
|
|
|
BarChart2,
|
|
|
|
|
FileText,
|
2023-02-23 01:20:18 +01:00
|
|
|
Upload,
|
|
|
|
|
Settings,
|
|
|
|
|
Lock,
|
|
|
|
|
User,
|
|
|
|
|
Users
|
2023-02-14 10:14:28 +01:00
|
|
|
} from 'react-feather';
|
|
|
|
|
import { NotificationsProvider } from '@mantine/notifications';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
export const meta: MetaFunction = () => ({
|
|
|
|
|
charset: 'utf-8',
|
2023-02-14 10:14:28 +01:00
|
|
|
title: 'WorkTimer',
|
|
|
|
|
description:
|
|
|
|
|
'WorkTimer is a time tracking app. Helps you track your time spent on projects.',
|
2023-02-11 03:14:14 +01:00
|
|
|
viewport: 'width=device-width,initial-scale=1'
|
2023-02-14 10:14:28 +01:00
|
|
|
});
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
export let links: LinksFunction = () => {
|
2023-02-14 10:14:28 +01:00
|
|
|
return [];
|
|
|
|
|
};
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
export async function loader({ request }: LoaderArgs) {
|
|
|
|
|
return json({
|
|
|
|
|
user: await getUser(request)
|
2023-02-14 10:14:28 +01:00
|
|
|
});
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
createEmotionCache({ key: 'mantine' });
|
|
|
|
|
|
2023-02-11 03:14:14 +01:00
|
|
|
export default function App() {
|
2023-02-14 10:14:28 +01:00
|
|
|
let transition = useTransition();
|
2023-02-11 03:14:14 +01:00
|
|
|
useEffect(() => {
|
2023-02-14 10:14:28 +01:00
|
|
|
if (transition.state === 'idle') completeNavigationProgress();
|
|
|
|
|
else startNavigationProgress();
|
|
|
|
|
}, [transition.state]);
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Document>
|
|
|
|
|
<Layout>
|
|
|
|
|
<Outlet />
|
|
|
|
|
</Layout>
|
|
|
|
|
</Document>
|
2023-02-14 10:14:28 +01:00
|
|
|
);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
function Document({
|
|
|
|
|
children,
|
|
|
|
|
title
|
|
|
|
|
}: {
|
|
|
|
|
children: React.ReactNode;
|
|
|
|
|
title?: string;
|
|
|
|
|
}) {
|
|
|
|
|
const preferredColorScheme = useColorScheme();
|
|
|
|
|
const toggleColorScheme = (value?: ColorScheme) => {
|
|
|
|
|
setColorScheme(value || (colorScheme === 'dark' ? 'light' : 'dark'));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const [colorScheme, setColorScheme] = useLocalStorage<ColorScheme>({
|
|
|
|
|
key: 'mantine-color-scheme',
|
|
|
|
|
defaultValue: preferredColorScheme,
|
|
|
|
|
getInitialValueInEffect: true
|
|
|
|
|
});
|
|
|
|
|
|
2023-02-11 03:14:14 +01:00
|
|
|
return (
|
2023-02-14 10:14:28 +01:00
|
|
|
<ColorSchemeProvider
|
|
|
|
|
colorScheme={colorScheme}
|
|
|
|
|
toggleColorScheme={toggleColorScheme}
|
|
|
|
|
>
|
|
|
|
|
<MantineProvider
|
|
|
|
|
theme={{ colorScheme }}
|
|
|
|
|
withGlobalStyles
|
|
|
|
|
withNormalizeCSS
|
|
|
|
|
>
|
|
|
|
|
<NotificationsProvider>
|
|
|
|
|
<html lang="en">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charSet="utf-8" />
|
2023-02-20 15:00:29 +01:00
|
|
|
<meta
|
|
|
|
|
httpEquiv="Content-Type"
|
|
|
|
|
content="text/html; charset=utf-8"
|
|
|
|
|
/>
|
|
|
|
|
<meta name="application-name" content="Work Timer" />
|
2023-02-18 20:37:37 +01:00
|
|
|
<meta
|
|
|
|
|
name="viewport"
|
|
|
|
|
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, user-scalable=no, viewport-fit=cover"
|
|
|
|
|
/>
|
|
|
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
|
|
|
<meta
|
|
|
|
|
name="apple-mobile-web-app-status-bar-style"
|
|
|
|
|
content="default"
|
|
|
|
|
/>
|
|
|
|
|
<meta name="format-detection" content="telephone=no" />
|
|
|
|
|
<meta name="mobile-web-app-capable" content="yes" />
|
2023-02-20 15:00:29 +01:00
|
|
|
|
|
|
|
|
<link rel="icon" href="/favicon.ico" />
|
|
|
|
|
<link
|
|
|
|
|
rel="apple-touch-icon"
|
|
|
|
|
sizes="180x180"
|
|
|
|
|
href={`/apple-touch-icon.png`}
|
|
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="32x32"
|
2023-02-20 15:01:51 +01:00
|
|
|
href={`/favicon-32x32.png`}
|
2023-02-20 15:00:29 +01:00
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="16x16"
|
2023-02-20 15:01:51 +01:00
|
|
|
href={`/favicon-32x32.png`}
|
2023-02-20 15:00:29 +01:00
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="512x512"
|
2023-02-20 15:01:51 +01:00
|
|
|
href={`/android-chrome-512x512.png`}
|
2023-02-20 15:00:29 +01:00
|
|
|
/>
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
sizes="192x192"
|
2023-02-20 15:01:51 +01:00
|
|
|
href={`/android-chrome-192x192.png`}
|
2023-02-20 15:00:29 +01:00
|
|
|
/>
|
|
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
{title ? <title>{title}</title> : null}
|
2023-02-20 15:00:29 +01:00
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
<StylesPlaceholder />
|
|
|
|
|
<Meta />
|
|
|
|
|
<Links />
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<NavigationProgress />
|
|
|
|
|
{children}
|
|
|
|
|
<ScrollRestoration />
|
|
|
|
|
<Scripts />
|
|
|
|
|
{process.env.NODE_ENV === 'development' && <LiveReload />}
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|
|
|
|
|
</NotificationsProvider>
|
|
|
|
|
</MantineProvider>
|
|
|
|
|
</ColorSchemeProvider>
|
|
|
|
|
);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function CatchBoundary() {
|
2023-02-14 10:14:28 +01:00
|
|
|
let caught = useCatch();
|
2023-02-11 03:14:14 +01:00
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
let message;
|
2023-02-11 03:14:14 +01:00
|
|
|
switch (caught.status) {
|
|
|
|
|
case 401:
|
2023-02-14 10:14:28 +01:00
|
|
|
message =
|
|
|
|
|
'Oops! Looks like you tried to visit a page that you do not have access to.';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
break;
|
2023-02-11 03:14:14 +01:00
|
|
|
case 404:
|
2023-02-14 10:14:28 +01:00
|
|
|
message =
|
|
|
|
|
'Oops! Looks like you tried to visit a page that does not exist.';
|
2023-02-11 03:14:14 +01:00
|
|
|
|
2023-02-14 10:14:28 +01:00
|
|
|
break;
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
default:
|
2023-02-14 10:14:28 +01:00
|
|
|
throw new Error(caught.data || caught.statusText);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Document title={`${caught.status} ${caught.statusText}`}>
|
|
|
|
|
<Layout>
|
|
|
|
|
<div>
|
|
|
|
|
<h1>
|
|
|
|
|
{caught.status}: {caught.statusText}
|
|
|
|
|
</h1>
|
|
|
|
|
<p>{message}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Layout>
|
|
|
|
|
</Document>
|
2023-02-14 10:14:28 +01:00
|
|
|
);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Layout({ children }: React.PropsWithChildren<{}>) {
|
2023-02-23 14:17:29 +01:00
|
|
|
let user = useMatches().find((m) => m.id === 'root')?.data?.user as
|
|
|
|
|
| UserType
|
|
|
|
|
| undefined;
|
2023-02-14 10:14:28 +01:00
|
|
|
const [opened, setOpened] = useState(false);
|
|
|
|
|
const location = useLocation();
|
|
|
|
|
const theme = useMantineTheme();
|
|
|
|
|
|
|
|
|
|
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setOpened(false);
|
|
|
|
|
}, [location]);
|
2023-02-11 03:14:14 +01:00
|
|
|
|
|
|
|
|
return (
|
2023-02-14 10:14:28 +01:00
|
|
|
<AppShell
|
|
|
|
|
padding="md"
|
|
|
|
|
navbarOffsetBreakpoint="sm"
|
|
|
|
|
asideOffsetBreakpoint="sm"
|
|
|
|
|
navbar={
|
|
|
|
|
user && (
|
|
|
|
|
<Navbar
|
|
|
|
|
p="xs"
|
|
|
|
|
hiddenBreakpoint="sm"
|
|
|
|
|
hidden={!opened}
|
|
|
|
|
width={{ sm: 200, lg: 250 }}
|
|
|
|
|
>
|
|
|
|
|
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
|
|
|
|
|
<Navbar.Section>
|
|
|
|
|
<Box
|
|
|
|
|
sx={(theme) => ({
|
|
|
|
|
paddingLeft: theme.spacing.xs,
|
|
|
|
|
paddingRight: theme.spacing.xs,
|
|
|
|
|
paddingBottom: theme.spacing.lg,
|
|
|
|
|
borderBottom: `1px solid ${
|
|
|
|
|
theme.colorScheme === 'dark'
|
|
|
|
|
? theme.colors.dark[4]
|
|
|
|
|
: theme.colors.gray[2]
|
|
|
|
|
}`
|
|
|
|
|
})}
|
|
|
|
|
>
|
2023-02-23 01:20:18 +01:00
|
|
|
<Group position="left" align="center">
|
2023-02-14 10:14:28 +01:00
|
|
|
<ActionIcon
|
|
|
|
|
variant="default"
|
|
|
|
|
onClick={() => toggleColorScheme()}
|
|
|
|
|
size={30}
|
|
|
|
|
title="Toggle color scheme"
|
|
|
|
|
>
|
|
|
|
|
{colorScheme === 'dark' ? (
|
|
|
|
|
<Sun size={16} />
|
|
|
|
|
) : (
|
|
|
|
|
<Moon size={16} />
|
|
|
|
|
)}
|
|
|
|
|
</ActionIcon>
|
2023-02-23 01:20:18 +01:00
|
|
|
<Text size="sm">Toggle theme</Text>
|
2023-02-14 10:14:28 +01:00
|
|
|
</Group>
|
|
|
|
|
</Box>
|
|
|
|
|
</Navbar.Section>
|
|
|
|
|
</MediaQuery>
|
|
|
|
|
{user && (
|
2023-02-23 14:17:29 +01:00
|
|
|
<Navbar.Section mt="md" grow={!user.admin}>
|
2023-02-18 21:54:57 +01:00
|
|
|
<NavLink
|
2023-02-14 10:14:28 +01:00
|
|
|
component={Link}
|
|
|
|
|
to="/"
|
2023-02-18 21:54:57 +01:00
|
|
|
label="Home"
|
|
|
|
|
icon={
|
2023-02-14 10:14:28 +01:00
|
|
|
<ThemeIcon variant="light">
|
|
|
|
|
<Home size={16} />
|
|
|
|
|
</ThemeIcon>
|
2023-02-18 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
active={location.pathname === '/'}
|
|
|
|
|
/>
|
|
|
|
|
<NavLink
|
2023-02-14 10:14:28 +01:00
|
|
|
component={Link}
|
|
|
|
|
to="/time-entries"
|
2023-02-18 21:54:57 +01:00
|
|
|
label="Time entries"
|
|
|
|
|
icon={
|
2023-02-14 10:14:28 +01:00
|
|
|
<ThemeIcon variant="light">
|
|
|
|
|
<Clock size={16} />
|
|
|
|
|
</ThemeIcon>
|
2023-02-18 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
variant="light"
|
|
|
|
|
active={location.pathname.includes('/time-entries')}
|
|
|
|
|
/>
|
|
|
|
|
<NavLink
|
2023-02-14 10:14:28 +01:00
|
|
|
component={Link}
|
|
|
|
|
to="/projects"
|
2023-02-18 21:54:57 +01:00
|
|
|
label="Projects"
|
|
|
|
|
icon={
|
2023-02-14 10:14:28 +01:00
|
|
|
<ThemeIcon variant="light">
|
|
|
|
|
<Briefcase size={16} />
|
|
|
|
|
</ThemeIcon>
|
2023-02-18 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
variant="light"
|
|
|
|
|
active={location.pathname.includes('/projects')}
|
|
|
|
|
/>
|
|
|
|
|
<NavLink
|
2023-02-14 10:14:28 +01:00
|
|
|
component={Link}
|
2023-02-18 23:48:21 +01:00
|
|
|
to="/reports"
|
|
|
|
|
label="Reports"
|
2023-02-18 21:54:57 +01:00
|
|
|
icon={
|
2023-02-14 10:14:28 +01:00
|
|
|
<ThemeIcon variant="light">
|
2023-02-18 23:48:21 +01:00
|
|
|
<BarChart2 size={16} />
|
2023-02-14 10:14:28 +01:00
|
|
|
</ThemeIcon>
|
2023-02-18 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
variant="light"
|
|
|
|
|
active={location.pathname.includes('/report')}
|
|
|
|
|
/>
|
|
|
|
|
<NavLink
|
2023-02-14 10:14:28 +01:00
|
|
|
component={Link}
|
2023-02-18 20:37:37 +01:00
|
|
|
to="/importexport"
|
2023-02-18 21:54:57 +01:00
|
|
|
label="Import/Export"
|
|
|
|
|
icon={
|
2023-02-14 10:14:28 +01:00
|
|
|
<ThemeIcon variant="light">
|
|
|
|
|
<Upload size={16} />
|
|
|
|
|
</ThemeIcon>
|
2023-02-18 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
variant="light"
|
|
|
|
|
active={location.pathname.includes('/importexport')}
|
|
|
|
|
/>
|
2023-02-14 10:14:28 +01:00
|
|
|
</Navbar.Section>
|
|
|
|
|
)}
|
2023-02-23 14:17:29 +01:00
|
|
|
{!!user?.admin && (
|
|
|
|
|
<Navbar.Section
|
|
|
|
|
grow
|
|
|
|
|
sx={{
|
|
|
|
|
marginTop: 16,
|
|
|
|
|
paddingTop: 16,
|
|
|
|
|
borderTop: `1px solid ${
|
|
|
|
|
theme.colorScheme === 'dark'
|
|
|
|
|
? theme.colors.dark[4]
|
|
|
|
|
: theme.colors.gray[2]
|
|
|
|
|
}`
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<NavLink
|
|
|
|
|
component={Link}
|
|
|
|
|
to="/users"
|
|
|
|
|
label="Users"
|
|
|
|
|
icon={
|
|
|
|
|
<ThemeIcon variant="light">
|
|
|
|
|
<Users size={16} />
|
|
|
|
|
</ThemeIcon>
|
|
|
|
|
}
|
|
|
|
|
rightSection={<Badge variant="light">ADMIN</Badge>}
|
|
|
|
|
variant="light"
|
|
|
|
|
active={location.pathname.includes('/users')}
|
|
|
|
|
/>
|
|
|
|
|
</Navbar.Section>
|
|
|
|
|
)}
|
2023-02-14 10:14:28 +01:00
|
|
|
{user && (
|
2023-02-23 12:18:12 +01:00
|
|
|
<Navbar.Section mt="lg">
|
2023-02-23 01:20:18 +01:00
|
|
|
<Menu shadow="md" width={200}>
|
|
|
|
|
<Menu.Target>
|
|
|
|
|
<UnstyledButton w="100%" title="Account / Logout" p="xs">
|
|
|
|
|
<Group w="100%">
|
|
|
|
|
<ThemeIcon
|
|
|
|
|
variant="light"
|
|
|
|
|
sx={{
|
|
|
|
|
flexShrink: 1
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<User size={16} />
|
|
|
|
|
</ThemeIcon>
|
|
|
|
|
<div>
|
|
|
|
|
<Text
|
|
|
|
|
sx={{
|
|
|
|
|
flex: 1
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{user.email.split('@')[0]}
|
|
|
|
|
</Text>
|
|
|
|
|
<Text size="xs" color="dimmed">
|
|
|
|
|
{user.email.split('@')[1]}
|
|
|
|
|
</Text>
|
|
|
|
|
</div>
|
|
|
|
|
</Group>
|
|
|
|
|
</UnstyledButton>
|
|
|
|
|
</Menu.Target>
|
|
|
|
|
|
|
|
|
|
<Menu.Dropdown>
|
|
|
|
|
<Menu.Label>Account</Menu.Label>
|
|
|
|
|
<Menu.Item
|
|
|
|
|
icon={<Settings size={14} />}
|
|
|
|
|
component={Link}
|
|
|
|
|
to="/account"
|
|
|
|
|
>
|
|
|
|
|
Settings
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
<Form action="/logout" method="post" noValidate>
|
|
|
|
|
<Menu.Item icon={<LogOut size={14} />} type="submit">
|
|
|
|
|
Logout
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
</Form>
|
|
|
|
|
</Menu.Dropdown>
|
|
|
|
|
</Menu>
|
2023-02-14 10:14:28 +01:00
|
|
|
</Navbar.Section>
|
|
|
|
|
)}
|
|
|
|
|
</Navbar>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
header={
|
|
|
|
|
<Header height={{ base: 50, md: 70 }} p="md">
|
|
|
|
|
<div
|
|
|
|
|
style={{ display: 'flex', alignItems: 'center', height: '100%' }}
|
|
|
|
|
>
|
|
|
|
|
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
|
|
|
|
|
<Burger
|
|
|
|
|
opened={opened}
|
|
|
|
|
onClick={() => setOpened((o) => !o)}
|
|
|
|
|
size="sm"
|
|
|
|
|
color={theme.colors.gray[6]}
|
|
|
|
|
mr="xl"
|
|
|
|
|
/>
|
|
|
|
|
</MediaQuery>
|
|
|
|
|
|
|
|
|
|
<Text
|
|
|
|
|
component={Link}
|
|
|
|
|
to="/"
|
|
|
|
|
style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ThemeIcon
|
|
|
|
|
variant="light"
|
|
|
|
|
size={24}
|
|
|
|
|
style={{ marginRight: theme.spacing.sm }}
|
|
|
|
|
>
|
|
|
|
|
<Clock />
|
|
|
|
|
</ThemeIcon>
|
|
|
|
|
<span>WorkTimer</span>
|
|
|
|
|
</Text>
|
|
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
marginLeft: 'auto'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<MediaQuery
|
|
|
|
|
smallerThan="sm"
|
|
|
|
|
styles={user ? { display: 'none' } : {}}
|
|
|
|
|
>
|
|
|
|
|
<ActionIcon
|
|
|
|
|
variant="light"
|
|
|
|
|
onClick={() =>
|
|
|
|
|
toggleColorScheme(colorScheme === 'dark' ? 'light' : 'dark')
|
|
|
|
|
}
|
|
|
|
|
title="Toggle color scheme"
|
|
|
|
|
>
|
|
|
|
|
{colorScheme === 'dark' ? (
|
|
|
|
|
<Sun size={18} />
|
|
|
|
|
) : (
|
|
|
|
|
<Moon size={18} />
|
|
|
|
|
)}
|
|
|
|
|
</ActionIcon>
|
|
|
|
|
</MediaQuery>
|
|
|
|
|
{!user && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="light"
|
|
|
|
|
ml="sm"
|
|
|
|
|
leftIcon={<LogIn />}
|
|
|
|
|
component={Link}
|
|
|
|
|
to="/login"
|
|
|
|
|
>
|
2023-02-21 19:06:00 +01:00
|
|
|
Sign in
|
2023-02-14 10:14:28 +01:00
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Header>
|
|
|
|
|
}
|
|
|
|
|
styles={(theme) => ({
|
|
|
|
|
main: {
|
|
|
|
|
backgroundColor:
|
|
|
|
|
theme.colorScheme === 'dark'
|
|
|
|
|
? theme.colors.dark[8]
|
|
|
|
|
: theme.colors.gray[0]
|
|
|
|
|
}
|
|
|
|
|
})}
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</AppShell>
|
|
|
|
|
);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ErrorBoundary({ error }: { error: Error }) {
|
2023-02-14 10:14:28 +01:00
|
|
|
console.error(error);
|
2023-02-11 03:14:14 +01:00
|
|
|
return (
|
|
|
|
|
<Document title="Error!">
|
|
|
|
|
<Layout>
|
|
|
|
|
<div>
|
|
|
|
|
<h1>There was an error</h1>
|
|
|
|
|
<p>{error.message}</p>
|
|
|
|
|
<hr />
|
2023-02-14 10:14:28 +01:00
|
|
|
<p>
|
|
|
|
|
Hey, developer, you should replace this with what you want your
|
|
|
|
|
users to see.
|
|
|
|
|
</p>
|
2023-02-11 03:14:14 +01:00
|
|
|
</div>
|
|
|
|
|
</Layout>
|
|
|
|
|
</Document>
|
2023-02-14 10:14:28 +01:00
|
|
|
);
|
2023-02-11 03:14:14 +01:00
|
|
|
}
|