Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0d86e2914 | ||
|
|
5d80d36be3 | ||
|
|
72f19d450c | ||
|
|
6024391414 | ||
|
|
905f445641 | ||
|
|
2462671700 | ||
|
|
0c0f8247d8 | ||
|
|
fa45eb3b3b | ||
|
|
b8ce86d783 | ||
|
|
cc58fbe263 | ||
|
|
50448c9fb6 | ||
|
|
0f1a948682 | ||
|
|
8f71c898a2 | ||
|
|
7be22833b0 | ||
|
|
43c0465e52 |
@@ -18,6 +18,7 @@ import { useState } from 'react';
|
|||||||
import { Apps } from 'tabler-icons-react';
|
import { Apps } from 'tabler-icons-react';
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../tools/state';
|
||||||
import { ServiceTypeList } from '../../tools/types';
|
import { ServiceTypeList } from '../../tools/types';
|
||||||
|
import { AppShelfItemWrapper } from './AppShelfItemWrapper';
|
||||||
|
|
||||||
export default function AddItemShelfItem(props: any) {
|
export default function AddItemShelfItem(props: any) {
|
||||||
const { addService } = useConfig();
|
const { addService } = useConfig();
|
||||||
@@ -34,30 +35,39 @@ export default function AddItemShelfItem(props: any) {
|
|||||||
>
|
>
|
||||||
<AddAppShelfItemForm setOpened={setOpened} />
|
<AddAppShelfItemForm setOpened={setOpened} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<AspectRatio
|
<AppShelfItemWrapper>
|
||||||
style={{
|
<Card.Section>
|
||||||
minHeight: 120,
|
<Group position="center" mx="lg">
|
||||||
minWidth: 120,
|
<Text
|
||||||
}}
|
// TODO: #1 Remove this hack to get the text to be centered.
|
||||||
ratio={4 / 3}
|
ml={15}
|
||||||
>
|
style={{
|
||||||
<Card
|
alignSelf: 'center',
|
||||||
style={{
|
alignContent: 'center',
|
||||||
backgroundColor:
|
alignItems: 'center',
|
||||||
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
justifyContent: 'center',
|
||||||
width: 200,
|
justifyItems: 'center',
|
||||||
height: 180,
|
}}
|
||||||
}}
|
mt="sm"
|
||||||
radius="md"
|
weight={500}
|
||||||
>
|
>
|
||||||
<Group direction="column" position="center">
|
Add a service
|
||||||
<motion.div whileHover={{ scale: 1.2 }}>
|
</Text>
|
||||||
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
|
|
||||||
</motion.div>
|
|
||||||
<Text>Add Service</Text>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Card>
|
</Card.Section>
|
||||||
</AspectRatio>
|
<Card.Section>
|
||||||
|
<AspectRatio ratio={5 / 3} m="xl">
|
||||||
|
<motion.i
|
||||||
|
whileHover={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
scale: 1.1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Apps style={{ cursor: 'pointer' }} onClick={() => setOpened(true)} size={60} />
|
||||||
|
</motion.i>
|
||||||
|
</AspectRatio>
|
||||||
|
</Card.Section>
|
||||||
|
</AppShelfItemWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { motion } from 'framer-motion';
|
|||||||
import {
|
import {
|
||||||
Text,
|
Text,
|
||||||
AspectRatio,
|
AspectRatio,
|
||||||
createStyles,
|
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
Card,
|
Card,
|
||||||
useMantineTheme,
|
useMantineTheme,
|
||||||
@@ -11,20 +10,12 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Space,
|
Space,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import AppShelfMenu from './AppShelfMenu';
|
|
||||||
import AddItemShelfItem from './AddAppShelfItem';
|
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../tools/state';
|
||||||
import { pingQbittorrent } from '../../tools/api';
|
import { pingQbittorrent } from '../../tools/api';
|
||||||
import { serviceItem } from '../../tools/types';
|
import { serviceItem } from '../../tools/types';
|
||||||
|
import AddItemShelfItem from './AddAppShelfItem';
|
||||||
const useStyles = createStyles((theme) => ({
|
import { AppShelfItemWrapper } from './AppShelfItemWrapper';
|
||||||
main: {
|
import AppShelfMenu from './AppShelfMenu';
|
||||||
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[1],
|
|
||||||
//TODO: #3 Fix this temporary fix and make the width and height dynamic / responsive
|
|
||||||
width: 200,
|
|
||||||
height: 180,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const AppShelf = (props: any) => {
|
const AppShelf = (props: any) => {
|
||||||
const { config, addService, removeService, setConfig } = useConfig();
|
const { config, addService, removeService, setConfig } = useConfig();
|
||||||
@@ -58,7 +49,6 @@ export function AppShelfItem(props: any) {
|
|||||||
const { service }: { service: serviceItem } = props;
|
const { service }: { service: serviceItem } = props;
|
||||||
const theme = useMantineTheme();
|
const theme = useMantineTheme();
|
||||||
const { removeService } = useConfig();
|
const { removeService } = useConfig();
|
||||||
const { classes } = useStyles();
|
|
||||||
const [hovering, setHovering] = useState(false);
|
const [hovering, setHovering] = useState(false);
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -70,13 +60,7 @@ export function AppShelfItem(props: any) {
|
|||||||
setHovering(false);
|
setHovering(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Card
|
<AppShelfItemWrapper hovering={hovering}>
|
||||||
className={classes.main}
|
|
||||||
style={{
|
|
||||||
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
|
|
||||||
}}
|
|
||||||
radius="md"
|
|
||||||
>
|
|
||||||
<Card.Section>
|
<Card.Section>
|
||||||
<Group position="apart" mx="lg">
|
<Group position="apart" mx="lg">
|
||||||
<Space />
|
<Space />
|
||||||
@@ -128,7 +112,7 @@ export function AppShelfItem(props: any) {
|
|||||||
</motion.i>
|
</motion.i>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
</Card.Section>
|
</Card.Section>
|
||||||
</Card>
|
</AppShelfItemWrapper>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
21
components/AppShelf/AppShelfItemWrapper.tsx
Normal file
21
components/AppShelf/AppShelfItemWrapper.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { useMantineTheme, Card } from '@mantine/core';
|
||||||
|
|
||||||
|
export function AppShelfItemWrapper(props: any) {
|
||||||
|
const { children, hovering } = props;
|
||||||
|
const theme = useMantineTheme();
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)',
|
||||||
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
|
||||||
|
|
||||||
|
//TODO: #3 Fix this temporary fix and make the width and height dynamic / responsive
|
||||||
|
width: 200,
|
||||||
|
height: 180,
|
||||||
|
}}
|
||||||
|
radius="md"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
components/ColorSchemeToggle/ColorSchemeSwitch.tsx
Normal file
45
components/ColorSchemeToggle/ColorSchemeSwitch.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createStyles, Switch, Group, useMantineColorScheme } from '@mantine/core';
|
||||||
|
import { Sun, MoonStars } from 'tabler-icons-react';
|
||||||
|
|
||||||
|
const useStyles = createStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
position: 'relative',
|
||||||
|
'& *': {
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
icon: {
|
||||||
|
pointerEvents: 'none',
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1,
|
||||||
|
top: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
iconLight: {
|
||||||
|
left: 4,
|
||||||
|
color: theme.white,
|
||||||
|
},
|
||||||
|
|
||||||
|
iconDark: {
|
||||||
|
right: 4,
|
||||||
|
color: theme.colors.gray[6],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export function ColorSchemeSwitch() {
|
||||||
|
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
|
||||||
|
const { classes, cx } = useStyles();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group>
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Sun className={cx(classes.icon, classes.iconLight)} size={18} />
|
||||||
|
<MoonStars className={cx(classes.icon, classes.iconDark)} size={18} />
|
||||||
|
<Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" />
|
||||||
|
</div>
|
||||||
|
Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,13 +8,16 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { useColorScheme } from '@mantine/hooks';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Settings as SettingsIcon } from 'tabler-icons-react';
|
import { Settings as SettingsIcon } from 'tabler-icons-react';
|
||||||
import { useConfig } from '../../tools/state';
|
import { useConfig } from '../../tools/state';
|
||||||
|
import { ColorSchemeSwitch } from '../ColorSchemeToggle/ColorSchemeSwitch';
|
||||||
import SaveConfigComponent from '../Config/SaveConfig';
|
import SaveConfigComponent from '../Config/SaveConfig';
|
||||||
|
|
||||||
function SettingsMenu(props: any) {
|
function SettingsMenu(props: any) {
|
||||||
const { config, setConfig } = useConfig();
|
const { config, setConfig } = useConfig();
|
||||||
|
const colorScheme = useColorScheme();
|
||||||
const matches = [
|
const matches = [
|
||||||
{ label: 'Google', value: 'https://google.com/search?q=' },
|
{ label: 'Google', value: 'https://google.com/search?q=' },
|
||||||
{ label: 'DuckDuckGo', value: 'https://duckduckgo.com/?q=' },
|
{ label: 'DuckDuckGo', value: 'https://duckduckgo.com/?q=' },
|
||||||
@@ -46,6 +49,7 @@ function SettingsMenu(props: any) {
|
|||||||
</Group>
|
</Group>
|
||||||
<Group direction="column">
|
<Group direction="column">
|
||||||
<Switch
|
<Switch
|
||||||
|
size="md"
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setConfig({
|
setConfig({
|
||||||
...config,
|
...config,
|
||||||
@@ -59,6 +63,7 @@ function SettingsMenu(props: any) {
|
|||||||
label="Enable search bar"
|
label="Enable search bar"
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
<ColorSchemeSwitch />
|
||||||
<SaveConfigComponent />
|
<SaveConfigComponent />
|
||||||
<Text
|
<Text
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
18
components/layout/Aside.tsx
Normal file
18
components/layout/Aside.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Aside as MantineAside } from '@mantine/core';
|
||||||
|
import { CalendarModule } from '../modules/calendar/CalendarModule';
|
||||||
|
import ModuleWrapper from '../modules/moduleWrapper';
|
||||||
|
|
||||||
|
export default function Aside() {
|
||||||
|
return (
|
||||||
|
<MantineAside
|
||||||
|
height="100%"
|
||||||
|
hiddenBreakpoint="md"
|
||||||
|
hidden
|
||||||
|
width={{
|
||||||
|
base: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModuleWrapper module={CalendarModule} />
|
||||||
|
</MantineAside>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
import { useBooleanToggle } from '@mantine/hooks';
|
import { useBooleanToggle } from '@mantine/hooks';
|
||||||
import { NextLink } from '@mantine/next';
|
import { NextLink } from '@mantine/next';
|
||||||
import { Logo } from './Logo';
|
import { Logo } from './Logo';
|
||||||
import { ColorSchemeToggle } from '../ColorSchemeToggle/ColorSchemeToggle';
|
|
||||||
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
import { SettingsMenuButton } from '../Settings/SettingsMenu';
|
||||||
import CalendarComponent from '../modules/calendar/CalendarModule';
|
import CalendarComponent from '../modules/calendar/CalendarModule';
|
||||||
|
|
||||||
@@ -119,7 +118,6 @@ export function Header({ links }: HeaderResponsiveProps) {
|
|||||||
<Head height={HEADER_HEIGHT} mb={10} className={classes.root}>
|
<Head height={HEADER_HEIGHT} mb={10} className={classes.root}>
|
||||||
<Container className={classes.header}>
|
<Container className={classes.header}>
|
||||||
<Group>
|
<Group>
|
||||||
<ColorSchemeToggle />
|
|
||||||
<NextLink style={{ textDecoration: 'none' }} href="/">
|
<NextLink style={{ textDecoration: 'none' }} href="/">
|
||||||
<Logo style={{ fontSize: 22 }} />
|
<Logo style={{ fontSize: 22 }} />
|
||||||
</NextLink>
|
</NextLink>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { AppShell, Aside, Center, createStyles } from '@mantine/core';
|
import { AppShell, Center, createStyles } from '@mantine/core';
|
||||||
import { Header } from './Header';
|
import { Header } from './Header';
|
||||||
import { Footer } from './Footer';
|
import { Footer } from './Footer';
|
||||||
import CalendarComponent from '../modules/calendar/CalendarModule';
|
import Aside from './Aside';
|
||||||
|
import Navbar from './Navbar';
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
main: {
|
main: {
|
||||||
@@ -15,18 +16,8 @@ export default function Layout({ children, style }: any) {
|
|||||||
const { classes, cx } = useStyles();
|
const { classes, cx } = useStyles();
|
||||||
return (
|
return (
|
||||||
<AppShell
|
<AppShell
|
||||||
aside={
|
navbar={<Navbar />}
|
||||||
<Aside
|
aside={<Aside />}
|
||||||
height="auto"
|
|
||||||
hiddenBreakpoint="md"
|
|
||||||
hidden
|
|
||||||
width={{
|
|
||||||
base: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CalendarComponent />
|
|
||||||
</Aside>
|
|
||||||
}
|
|
||||||
header={<Header links={[]} />}
|
header={<Header links={[]} />}
|
||||||
footer={<Footer links={[]} />}
|
footer={<Footer links={[]} />}
|
||||||
>
|
>
|
||||||
|
|||||||
18
components/layout/Navbar.tsx
Normal file
18
components/layout/Navbar.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Navbar as MantineNavbar } from '@mantine/core';
|
||||||
|
import { DateModule } from '../modules/date/DateModule';
|
||||||
|
import ModuleWrapper from '../modules/moduleWrapper';
|
||||||
|
|
||||||
|
export default function Navbar() {
|
||||||
|
return (
|
||||||
|
<MantineNavbar
|
||||||
|
height="100%"
|
||||||
|
hiddenBreakpoint="md"
|
||||||
|
hidden
|
||||||
|
width={{
|
||||||
|
base: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ModuleWrapper module={DateModule} />
|
||||||
|
</MantineNavbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
/* eslint-disable react/no-children-prop */
|
/* eslint-disable react/no-children-prop */
|
||||||
import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core';
|
import { Popover, Box, ScrollArea, Divider, Indicator } from '@mantine/core';
|
||||||
import { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Calendar } from '@mantine/dates';
|
import { Calendar } from '@mantine/dates';
|
||||||
import { CalendarIcon } from '@modulz/radix-icons';
|
import { CalendarIcon } from '@modulz/radix-icons';
|
||||||
import { RadarrMediaDisplay, SonarrMediaDisplay } from './MediaDisplay';
|
import { RadarrMediaDisplay, SonarrMediaDisplay } from './MediaDisplay';
|
||||||
import { useConfig } from '../../../tools/state';
|
import { useConfig } from '../../../tools/state';
|
||||||
import { MHPModule } from '../modules';
|
import { IModule } from '../modules';
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const CalendarModule: MHPModule = {
|
export const CalendarModule: IModule = {
|
||||||
title: 'Calendar',
|
title: 'Calendar',
|
||||||
description:
|
description:
|
||||||
'A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.',
|
'A calendar module for displaying upcoming releases. It interacts with the Sonarr and Radarr API.',
|
||||||
@@ -94,12 +93,8 @@ function DayComponent(props: any) {
|
|||||||
setOpened(true);
|
setOpened(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{radarrFiltered.length > 0 && (
|
{radarrFiltered.length > 0 && <Indicator size={7} color="yellow" children={null} />}
|
||||||
<Indicator size={7} color="yellow" children={null} />
|
{sonarrFiltered.length > 0 && <Indicator size={7} offset={8} color="blue" children={null} />}
|
||||||
)}
|
|
||||||
{sonarrFiltered.length > 0 && (
|
|
||||||
<Indicator size={7} offset={8} color="blue" children={null} />
|
|
||||||
)}
|
|
||||||
<Popover
|
<Popover
|
||||||
position="left"
|
position="left"
|
||||||
width={700}
|
width={700}
|
||||||
@@ -109,25 +104,21 @@ function DayComponent(props: any) {
|
|||||||
target={` ${day}`}
|
target={` ${day}`}
|
||||||
>
|
>
|
||||||
<ScrollArea style={{ height: 400 }}>
|
<ScrollArea style={{ height: 400 }}>
|
||||||
{sonarrFiltered.map((media: any, index: number) => {
|
{sonarrFiltered.map((media: any, index: number) => (
|
||||||
return (
|
<React.Fragment key={index}>
|
||||||
<React.Fragment key={index}>
|
<SonarrMediaDisplay media={media} />
|
||||||
<SonarrMediaDisplay media={media} />
|
{index < sonarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
||||||
{index < sonarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
</React.Fragment>
|
||||||
</React.Fragment>
|
))}
|
||||||
);
|
|
||||||
})}
|
|
||||||
{radarrFiltered.length > 0 && sonarrFiltered.length > 0 && (
|
{radarrFiltered.length > 0 && sonarrFiltered.length > 0 && (
|
||||||
<Divider variant="dashed" my="xl" />
|
<Divider variant="dashed" my="xl" />
|
||||||
)}
|
)}
|
||||||
{radarrFiltered.map((media: any, index: number) => {
|
{radarrFiltered.map((media: any, index: number) => (
|
||||||
return (
|
<React.Fragment key={index}>
|
||||||
<React.Fragment key={index}>
|
<RadarrMediaDisplay media={media} />
|
||||||
<RadarrMediaDisplay media={media} />
|
{index < radarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
||||||
{index < radarrFiltered.length - 1 && <Divider variant="dashed" my="xl" />}
|
</React.Fragment>
|
||||||
</React.Fragment>
|
))}
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</Popover>
|
</Popover>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
7
components/modules/date/DateModule.story.tsx
Normal file
7
components/modules/date/DateModule.story.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import DateComponent from './DateModule';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Date module',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = (args: any) => <DateComponent {...args} />;
|
||||||
41
components/modules/date/DateModule.tsx
Normal file
41
components/modules/date/DateModule.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Group, Text, Title } from '@mantine/core';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Clock } from 'tabler-icons-react';
|
||||||
|
import { IModule } from '../modules';
|
||||||
|
|
||||||
|
export const DateModule: IModule = {
|
||||||
|
title: 'Date',
|
||||||
|
description: 'Show the current time and date in a card',
|
||||||
|
icon: Clock,
|
||||||
|
component: DateComponent,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DateComponent(props: any) {
|
||||||
|
const [date, setDate] = useState(new Date());
|
||||||
|
const hours = date.getHours();
|
||||||
|
const minutes = date.getMinutes();
|
||||||
|
|
||||||
|
// Change date on minute change
|
||||||
|
// Note: Using 10 000ms instead of 1000ms to chill a little :)
|
||||||
|
useEffect(() => {
|
||||||
|
setInterval(() => {
|
||||||
|
setDate(new Date());
|
||||||
|
}, 10000);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group p="sm" direction="column">
|
||||||
|
<Title>
|
||||||
|
{hours < 10 ? `0${hours}` : hours}:{minutes < 10 ? `0${minutes}` : minutes}
|
||||||
|
</Title>
|
||||||
|
<Text size="xl">
|
||||||
|
{
|
||||||
|
// Use dayjs to format the date
|
||||||
|
// https://day.js.org/en/getting-started/installation/
|
||||||
|
dayjs(date).format('dddd, MMMM D YYYY')
|
||||||
|
}
|
||||||
|
</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
20
components/modules/moduleWrapper.tsx
Normal file
20
components/modules/moduleWrapper.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Card, useMantineTheme } from '@mantine/core';
|
||||||
|
import { IModule } from './modules';
|
||||||
|
|
||||||
|
export default function ModuleWrapper(props: any) {
|
||||||
|
const { module }: { module: IModule } = props;
|
||||||
|
const theme = useMantineTheme();
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
mx="sm"
|
||||||
|
radius="lg"
|
||||||
|
shadow="sm"
|
||||||
|
style={{
|
||||||
|
// Make background color of the card depend on the theme
|
||||||
|
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : 'white',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<module.component />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Each module should have its own interface and call the following function:
|
// Each module should have its own interface and call the following function:
|
||||||
// TODO: Add a function to register a module
|
// TODO: Add a function to register a module
|
||||||
// Note: Maybe use context to keep track of the modules
|
// Note: Maybe use context to keep track of the modules
|
||||||
export interface MHPModule {
|
export interface IModule {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { MantineProvider, ColorScheme, ColorSchemeProvider } from '@mantine/core
|
|||||||
import { NotificationsProvider } from '@mantine/notifications';
|
import { NotificationsProvider } from '@mantine/notifications';
|
||||||
import Layout from '../components/layout/Layout';
|
import Layout from '../components/layout/Layout';
|
||||||
import { ConfigProvider } from '../tools/state';
|
import { ConfigProvider } from '../tools/state';
|
||||||
|
import { theme } from '../tools/theme';
|
||||||
|
|
||||||
export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
||||||
const { Component, pageProps } = props;
|
const { Component, pageProps } = props;
|
||||||
@@ -27,7 +28,14 @@ export default function App(props: AppProps & { colorScheme: ColorScheme }) {
|
|||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
|
||||||
<MantineProvider theme={{ colorScheme }} withGlobalStyles withNormalizeCSS>
|
<MantineProvider
|
||||||
|
theme={{
|
||||||
|
...theme,
|
||||||
|
colorScheme,
|
||||||
|
}}
|
||||||
|
withGlobalStyles
|
||||||
|
withNormalizeCSS
|
||||||
|
>
|
||||||
<NotificationsProvider position="top-right">
|
<NotificationsProvider position="top-right">
|
||||||
<ConfigProvider>
|
<ConfigProvider>
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|||||||
3
tools/theme.ts
Normal file
3
tools/theme.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import { MantineProviderProps } from '@mantine/core';
|
||||||
|
|
||||||
|
export const theme: MantineProviderProps['theme'] = {};
|
||||||
@@ -8853,16 +8853,11 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@^1.1.1, minimist@^1.2.5, minimist@^1.2.6:
|
minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.5:
|
||||||
version "1.2.6"
|
version "1.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@~1.2.5:
|
|
||||||
version "1.2.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
|
||||||
|
|
||||||
minipass-collect@^1.0.2:
|
minipass-collect@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
|
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
|
||||||
|
|||||||
Reference in New Issue
Block a user