🚀 Patch v0.3.1

Patch v0.3.1
This commit is contained in:
Thomas Camlong
2022-05-16 23:31:20 +02:00
committed by ajnart
19 changed files with 214 additions and 289 deletions

View File

@@ -4,8 +4,12 @@ name: Master docker CI
on: on:
push: push:
branches: [master] branches: [master]
paths-ignore:
- '.github/**'
- '**.md'
tags: tags:
- v* - v*
workflow_dispatch: workflow_dispatch:
env: env:
@@ -110,6 +114,6 @@ jobs:
with: with:
platforms: linux/amd64,linux/arm64,linux/arm/v7 platforms: linux/amd64,linux/arm64,linux/arm/v7
context: . context: .
push: ${{ github.event_name != 'pull_request' }} push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

View File

@@ -6,7 +6,13 @@ name: Development CI
on: on:
push: push:
branches: [dev] branches: [dev]
paths-ignore:
- '.github/**'
- '**.md'
pull_request: pull_request:
paths-ignore:
- '.github/**'
- '**.md'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
tags: tags:
@@ -25,13 +31,17 @@ jobs:
yarn_install_and_build: yarn_install_and_build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Setup - name: Setup
uses: actions/setup-node@v3 uses: actions/setup-node@v3
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Get yarn cache directory path - name: Get yarn cache directory path
id: yarn-cache-dir-path id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)" run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn cache - name: Yarn cache
uses: actions/cache@v3 uses: actions/cache@v3
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
@@ -39,6 +49,7 @@ jobs:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }} path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn- restore-keys: ${{ runner.os }}-yarn-
- name: Nextjs cache - name: Nextjs cache
uses: actions/cache@v2 uses: actions/cache@v2
with: with:
@@ -50,8 +61,10 @@ jobs:
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
# If source files changed but packages didn't, rebuild from a prior cache. # If source files changed but packages didn't, rebuild from a prior cache.
restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
- run: yarn install --frozen-lockfile - run: yarn install --frozen-lockfile
- run: yarn build - run: yarn build
- name: Cache build output - name: Cache build output
uses: actions/cache@v2 uses: actions/cache@v2
id: restore-build id: restore-build
@@ -72,8 +85,10 @@ jobs:
packages: write packages: write
contents: read contents: read
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
- uses: actions/cache@v2 - uses: actions/cache@v2
id: restore-build id: restore-build
with: with:
@@ -85,6 +100,7 @@ jobs:
./.next/standalone/ ./.next/standalone/
./packages.json ./packages.json
key: ${{ github.sha }} key: ${{ github.sha }}
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v4 uses: docker/metadata-action@v4
@@ -95,11 +111,15 @@ jobs:
tags: | tags: |
type=ref,event=pr type=ref,event=pr
tpye=raw,value=dev,priority=1 tpye=raw,value=dev,priority=1
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v2 uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
- name: Login to GHCR - name: Login to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v2 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
@@ -111,6 +131,6 @@ jobs:
with: with:
platforms: linux/amd64,linux/arm64,linux/arm/v7 platforms: linux/amd64,linux/arm64,linux/arm/v7
context: . context: .
push: true push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

3
.gitignore vendored
View File

@@ -35,4 +35,5 @@ yarn-error.log*
*.tsbuildinfo *.tsbuildinfo
# storybook # storybook
storybook-static storybook-static
data/configs

View File

@@ -1,19 +1,13 @@
FROM node:16-alpine FROM node:16-alpine
WORKDIR /app WORKDIR /app
ENV NODE_ENV production ENV NODE_ENV production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY /next.config.js ./ COPY /next.config.js ./
COPY /public ./public COPY /public ./public
COPY /package.json ./package.json COPY /package.json ./package.json
# Automatically leverage output traces to reduce image size. https://nextjs.org/docs/advanced-features/output-file-tracing
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY /.next/standalone ./ COPY /.next/standalone ./
COPY /.next/static ./.next/static COPY /.next/static ./.next/static
EXPOSE 7575 EXPOSE 7575
ENV PORT 7575 ENV PORT 7575
VOLUME /app/data/configs VOLUME /app/data/configs
CMD ["node", "server.js"] CMD ["node", "server.js"]

View File

@@ -9,9 +9,7 @@
<img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/ajnart/homarr?label=Downloads%20"></a> <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/ajnart/homarr?label=Downloads%20"></a>
</p> </p>
<p align="center"> <p align="center">
<a href="">
<img align="end" width=600 src="https://user-images.githubusercontent.com/49837342/168315259-b778c816-10fe-44db-bd25-3eea6f31b233.png" /> <img align="end" width=600 src="https://user-images.githubusercontent.com/49837342/168315259-b778c816-10fe-44db-bd25-3eea6f31b233.png" />
<a/>
</p> </p>
<p align = "center"> <p align = "center">
A homepage for <i>your</i> server. A homepage for <i>your</i> server.

View File

@@ -1,12 +1,26 @@
{ {
"name": "config", "name": "config",
"services": [], "services": [
{
"type": "Other",
"name": "YouTube",
"icon": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/youtube.png",
"url": "https://youtube.com/"
},
{
"type": "Other",
"name": "YouTube ",
"icon": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/youtube.png",
"url": "https://youtube.com/"
}
],
"settings": { "settings": {
"searchBar": true, "searchBar": true,
"searchUrl": "https://duckduckgo.com/?q=", "searchUrl": "Custom",
"enabledModules": [ "enabledModules": [
"Date", "Date",
"Calendar" "Calendar",
"Weather"
] ]
} }
} }

View File

@@ -1,2 +1,2 @@
export const REPO_URL = 'ajnart/homarr'; export const REPO_URL = 'ajnart/homarr';
export const CURRENT_VERSION = 'v0.3.0'; export const CURRENT_VERSION = 'v0.3.1';

View File

@@ -12,6 +12,7 @@ import {
LoadingOverlay, LoadingOverlay,
ActionIcon, ActionIcon,
Tooltip, Tooltip,
Title,
} from '@mantine/core'; } from '@mantine/core';
import { useForm } from '@mantine/form'; import { useForm } from '@mantine/form';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
@@ -28,9 +29,9 @@ export function AddItemShelfButton(props: any) {
<Modal <Modal
size="xl" size="xl"
radius="md" radius="md"
title={<Title order={3}>Add service</Title>}
opened={props.opened || opened} opened={props.opened || opened}
onClose={() => setOpened(false)} onClose={() => setOpened(false)}
title="Add a service"
> >
<AddAppShelfItemForm setOpened={setOpened} /> <AddAppShelfItemForm setOpened={setOpened} />
</Modal> </Modal>

View File

@@ -1,30 +1,21 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { Text, AspectRatio, SimpleGrid, Card, Image, useMantineTheme } from '@mantine/core'; import { Text, AspectRatio, Card, Image, useMantineTheme, Center, Grid } from '@mantine/core';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
import { serviceItem } from '../../tools/types'; import { serviceItem } from '../../tools/types';
import AppShelfMenu from './AppShelfMenu'; import AppShelfMenu from './AppShelfMenu';
const AppShelf = () => { const AppShelf = (props: any) => {
const { config } = useConfig(); const { config } = useConfig();
return ( return (
<SimpleGrid <Grid gutter="xl" align="center">
cols={7}
spacing="xl"
breakpoints={[
{ maxWidth: 2400, cols: 6, spacing: 'xl' },
{ maxWidth: 1800, cols: 5, spacing: 'xl' },
{ maxWidth: 1500, cols: 4, spacing: 'lg' },
{ maxWidth: 800, cols: 3, spacing: 'md' },
{ maxWidth: 400, cols: 3, spacing: 'sm' },
{ maxWidth: 400, cols: 2, spacing: 'sm' },
]}
>
{config.services.map((service) => ( {config.services.map((service) => (
<AppShelfItem key={service.name} service={service} /> <Grid.Col span={6} xl={2} xs={4} sm={3} md={3}>
<AppShelfItem key={service.name} service={service} />
</Grid.Col>
))} ))}
</SimpleGrid> </Grid>
); );
}; };
@@ -42,16 +33,9 @@ export function AppShelfItem(props: any) {
setHovering(false); setHovering(false);
}} }}
> >
<Card <Card withBorder radius="lg" shadow="md">
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],
}}
radius="md"
>
<Card.Section> <Card.Section>
<Text mt="sm" align="center" lineClamp={1} weight={500}> <Text mt="sm" align="center" lineClamp={1} weight={550}>
{service.name} {service.name}
</Text> </Text>
<motion.div <motion.div
@@ -68,34 +52,35 @@ export function AppShelfItem(props: any) {
<AppShelfMenu service={service} /> <AppShelfMenu service={service} />
</motion.div> </motion.div>
</Card.Section> </Card.Section>
<Card.Section> <Center>
<AspectRatio <Card.Section>
ratio={3 / 5} <AspectRatio
m="xl" ratio={3 / 5}
style={{ m="xl"
width: 150, style={{
height: 90, width: 150,
}} height: 90,
>
<motion.i
whileHover={{
cursor: 'pointer',
scale: 1.1,
}} }}
> >
<Image <motion.i
style={{ whileHover={{
maxWidth: 80, cursor: 'pointer',
scale: 1.1,
}} }}
fit="contain" >
onClick={() => { <Image
window.open(service.url); width={80}
}} height={80}
src={service.icon} src={service.icon}
/> fit="contain"
</motion.i> onClick={() => {
</AspectRatio> window.open(service.url);
</Card.Section> }}
/>
</motion.i>
</AspectRatio>
</Card.Section>
</Center>
</Card> </Card>
</motion.div> </motion.div>
); );

View File

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { createStyles, Switch, Group, useMantineColorScheme } from '@mantine/core'; import { createStyles, Switch, Group, useMantineColorScheme, Kbd } from '@mantine/core';
import { Sun, MoonStars } from 'tabler-icons-react'; import { Sun, MoonStars } from 'tabler-icons-react';
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
@@ -40,6 +40,9 @@ export function ColorSchemeSwitch() {
<Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" /> <Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" />
</div> </div>
Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode
<Group spacing={2}>
<Kbd>Ctrl</Kbd>+<Kbd>J</Kbd>
</Group>
</Group> </Group>
); );
} }

View File

@@ -1,18 +1,40 @@
import { TextInput, Text, Popover, Box } from '@mantine/core'; import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core';
import { useForm } from '@mantine/hooks'; import { useForm, useHotkeys } from '@mantine/hooks';
import { useState } from 'react'; import { useRef, useState } from 'react';
import { Search, BrandYoutube, Download } from 'tabler-icons-react'; import { Search, BrandYoutube, Download } from 'tabler-icons-react';
import { useConfig } from '../../tools/state'; import { useConfig } from '../../tools/state';
const useStyles = createStyles((theme) => ({
hide: {
[theme.fn.smallerThan('sm')]: {
display: 'none',
},
display: 'flex',
alignItems: 'center',
},
}));
export default function SearchBar(props: any) { export default function SearchBar(props: any) {
const { config, setConfig } = useConfig(); const { config, setConfig } = useConfig();
const [opened, setOpened] = useState(false); const [opened, setOpened] = useState(false);
const [icon, setIcon] = useState(<Search />); const [icon, setIcon] = useState(<Search />);
const querryUrl = config.settings.searchUrl || 'https://www.google.com/search?q='; const queryUrl = config.settings.searchUrl || 'https://www.google.com/search?q=';
const textInput: any = useRef(null);
useHotkeys([['ctrl+K', () => textInput.current.focus()]]);
const { classes, cx } = useStyles();
const theme = useMantineTheme();
const rightSection = (
<div className={classes.hide}>
<Kbd>Ctrl</Kbd>
<span style={{ margin: '0 5px' }}>+</span>
<Kbd>K</Kbd>
</div>
);
const form = useForm({ const form = useForm({
initialValues: { initialValues: {
querry: '', query: '',
}, },
}); });
@@ -21,70 +43,66 @@ export default function SearchBar(props: any) {
} }
return ( return (
<Box <form
mb={"xl"} onChange={() => {
style={{ // If query contains !yt or !t add "Searching on YouTube" or "Searching torrent"
width: '100%', const query = form.values.query.trim();
const isYoutube = query.startsWith('!yt');
const isTorrent = query.startsWith('!t');
if (isYoutube) {
setIcon(<BrandYoutube size={22} />);
} else if (isTorrent) {
setIcon(<Download size={22} />);
} else {
setIcon(<Search size={22} />);
}
}} }}
onSubmit={form.onSubmit((values) => {
// Find if query is prefixed by !yt or !t
const query = values.query.trim();
const isYoutube = query.startsWith('!yt');
const isTorrent = query.startsWith('!t');
if (isYoutube) {
window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`);
} else if (isTorrent) {
window.open(`https://bitsearch.to/search?q=${query.substring(3)}`);
} else {
window.open(`${queryUrl}${values.query}`);
}
})}
> >
<form <Popover
onChange={() => { opened={opened}
// If querry contains !yt or !t add "Searching on YouTube" or "Searching torrent" position="bottom"
const querry = form.values.querry.trim(); placement="start"
const isYoutube = querry.startsWith('!yt'); width={260}
const isTorrent = querry.startsWith('!t'); withArrow
if (isYoutube) { radius="md"
setIcon(<BrandYoutube size={22} />); trapFocus={false}
} else if (isTorrent) { transition="pop-bottom-right"
setIcon(<Download size={22} />); onFocusCapture={() => setOpened(true)}
} else { onBlurCapture={() => setOpened(false)}
setIcon(<Search size={22} />); target={
} <TextInput
}} variant="filled"
onSubmit={form.onSubmit((values) => { icon={icon}
// Find if querry is prefixed by !yt or !t ref={textInput}
const querry = values.querry.trim(); rightSectionWidth={90}
const isYoutube = querry.startsWith('!yt'); rightSection={rightSection}
const isTorrent = querry.startsWith('!t'); radius="md"
if (isYoutube) { size="md"
window.open(`https://www.youtube.com/results?search_query=${querry.substring(3)}`); styles={{ rightSection: { pointerEvents: 'none' } }}
} else if (isTorrent) { placeholder="Search the web..."
window.open(`https://bitsearch.to/search?q=${querry.substring(3)}`); {...props}
} else { {...form.getInputProps('query')}
window.open(`${querryUrl}${values.querry}`); />
} }
})}
> >
<Popover <Text>
opened={opened} tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube
style={{ or for a Torrent respectively.
width: '100%', </Text>
}} </Popover>
position="bottom" </form>
placement="start"
withArrow
trapFocus={false}
transition="pop-top-left"
onFocusCapture={() => setOpened(true)}
onBlurCapture={() => setOpened(false)}
target={
<TextInput
variant="filled"
color="blue"
icon={icon}
radius="md"
size="md"
placeholder="Search the web"
{...props}
{...form.getInputProps('querry')}
/>
}
>
<Text>
tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube or for a Torrent respectively.
</Text>
</Popover>
</form>
</Box>
); );
} }

View File

@@ -74,8 +74,8 @@ function SettingsMenu(props: any) {
/> />
{searchUrl === 'Custom' && ( {searchUrl === 'Custom' && (
<TextInput <TextInput
label="Querry URL" label="Query URL"
placeholder="Custom querry url" placeholder="Custom query url"
value={customSearchUrl} value={customSearchUrl}
onChange={(event) => { onChange={(event) => {
setCustomSearchUrl(event.currentTarget.value); setCustomSearchUrl(event.currentTarget.value);
@@ -142,7 +142,8 @@ export function SettingsMenuButton(props: any) {
return ( return (
<> <>
<Modal <Modal
size="md" size="xl"
radius="md"
title={<Title order={3}>Settings</Title>} title={<Title order={3}>Settings</Title>}
opened={props.opened || opened} opened={props.opened || opened}
onClose={() => setOpened(false)} onClose={() => setOpened(false)}

View File

@@ -1,8 +1,9 @@
import { Aside as MantineAside, Group } from '@mantine/core'; import { Aside as MantineAside, Group } from '@mantine/core';
import { DateModule } from '../modules';
import { CalendarModule } from '../modules/calendar/CalendarModule'; import { CalendarModule } from '../modules/calendar/CalendarModule';
import ModuleWrapper from '../modules/moduleWrapper'; import ModuleWrapper from '../modules/moduleWrapper';
export default function Aside() { export default function Aside(props: any) {
return ( return (
<MantineAside <MantineAside
hiddenBreakpoint="md" hiddenBreakpoint="md"
@@ -14,8 +15,9 @@ export default function Aside() {
base: 'auto', base: 'auto',
}} }}
> >
<Group mt="sm" direction="column"> <Group mt="sm" grow direction="column">
<ModuleWrapper module={CalendarModule} /> <ModuleWrapper module={CalendarModule} />
<ModuleWrapper module={DateModule} />
</Group> </Group>
</MantineAside> </MantineAside>
); );

View File

@@ -54,24 +54,12 @@ export function Footer({ links }: FooterCenteredProps) {
</Anchor> </Anchor>
)); ));
return ( return (
<FooterComponent height="auto" style={{ border: 'none' }}> <FooterComponent p={5} height="auto" style={{ border: 'none', position: 'fixed', bottom: 0, right: 0 }}>
<Group <Group position="right" mr="xs" mb="xs">
sx={{ <ActionIcon<'a'> component="a" href="https://github.com/ajnart/homarr" size="lg">
position: 'fixed', <BrandGithub size={18} />
bottom: 0, </ActionIcon>
right: 15,
}}
direction="row"
align="center"
mb={15}
>
<Group className={classes.links}>{items}</Group>
<Group spacing="xs" position="right" noWrap>
<ActionIcon<'a'> component="a" href="https://github.com/ajnart/homarr" size="lg">
<BrandGithub size={18} />
</ActionIcon>
</Group>
<Text <Text
style={{ style={{
fontSize: '0.90rem', fontSize: '0.90rem',

View File

@@ -1,117 +1,35 @@
import React from 'react'; import React from 'react';
import { createStyles, Header as Head, Group, Drawer, Center } from '@mantine/core'; import { createStyles, Header as Head, Group, Box } from '@mantine/core';
import { useBooleanToggle } from '@mantine/hooks';
import { NextLink } from '@mantine/next';
import { Logo } from './Logo'; import { Logo } from './Logo';
import CalendarComponent from '../modules/calendar/CalendarModule'; import SearchBar from '../SearchBar/SearchBar';
import { SettingsMenuButton } from '../Settings/SettingsMenu';
import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem'; import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem';
import { SettingsMenuButton } from '../Settings/SettingsMenu';
const HEADER_HEIGHT = 60; const HEADER_HEIGHT = 60;
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
root: { hide: {
position: 'relative', [theme.fn.smallerThan('xs')]: {
zIndex: 1,
},
dropdown: {
position: 'absolute',
top: HEADER_HEIGHT,
left: 0,
right: 0,
zIndex: 0,
borderTopRightRadius: 0,
borderTopLeftRadius: 0,
borderTopWidth: 0,
overflow: 'hidden',
[theme.fn.largerThan('md')]: {
display: 'none', display: 'none',
}, },
}, },
header: {
display: 'flex',
height: '100%',
},
links: {
[theme.fn.smallerThan('md')]: {
display: 'none',
},
},
burger: {
[theme.fn.largerThan('md')]: {
display: 'none',
},
},
link: {
display: 'block',
lineHeight: 1,
padding: '8px 12px',
borderRadius: theme.radius.sm,
textDecoration: 'none',
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7],
fontSize: theme.fontSizes.sm,
fontWeight: 500,
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
},
[theme.fn.smallerThan('sm')]: {
borderRadius: 0,
padding: theme.spacing.md,
},
},
linkActive: {
'&, &:hover': {
backgroundColor:
theme.colorScheme === 'dark'
? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25)
: theme.colors[theme.primaryColor][0],
color: theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 3 : 7],
},
},
})); }));
interface HeaderResponsiveProps { export function Header(props: any) {
links: { link: string; label: string }[];
}
export function Header({ links }: HeaderResponsiveProps) {
const [opened, toggleOpened] = useBooleanToggle(false);
const { classes, cx } = useStyles(); const { classes, cx } = useStyles();
return ( return (
<Head height={HEADER_HEIGHT}> <Head height="auto">
<Group direction="row" align="center" position="apart" className={classes.header} mx="xl"> <Group m="xs" position="apart">
<NextLink style={{ textDecoration: 'none' }} href="/"> <Box className={classes.hide}>
<Logo style={{ fontSize: 22 }} /> <Logo style={{ fontSize: 22 }} />
</NextLink> </Box>
<Group> <Group noWrap>
<SearchBar />
<SettingsMenuButton /> <SettingsMenuButton />
<AddItemShelfButton /> <AddItemShelfButton />
</Group> </Group>
</Group> </Group>
<Drawer
opened={opened}
overlayOpacity={0.55}
overlayBlur={3}
onClose={() => toggleOpened()}
position="right"
>
{opened ?? (
<Center>
<CalendarComponent />
</Center>
)}
</Drawer>
</Head> </Head>
); );
} }

View File

@@ -1,36 +1,28 @@
import { AppShell, Center, createStyles } from '@mantine/core'; import { AppShell, createStyles } from '@mantine/core';
import { Header } from './Header'; import { Header } from './Header';
import { Footer } from './Footer'; import { Footer } from './Footer';
import Aside from './Aside'; import Aside from './Aside';
import Navbar from './Navbar';
const useStyles = createStyles((theme) => ({ const useStyles = createStyles((theme) => ({
main: { main: {},
[theme.fn.largerThan('md')]: {
maxWidth: 1500,
},
},
})); }));
export default function Layout({ children, style }: any) { export default function Layout({ children, style }: any) {
const { classes, cx } = useStyles(); const { classes, cx } = useStyles();
return ( return (
<AppShell <AppShell
navbar={<Navbar />}
aside={<Aside />} aside={<Aside />}
header={<Header links={[]} />} header={<Header />}
footer={<Footer links={[]} />} footer={<Footer links={[]} />}
> >
<Center> <main
<main className={cx(classes.main)}
className={cx(classes.main)} style={{
style={{ ...style,
...style, }}
}} >
> {children}
{children} </main>
</main>
</Center>
</AppShell> </AppShell>
); );
} }

View File

@@ -14,7 +14,7 @@ export default function Navbar() {
base: 'auto', base: 'auto',
}} }}
> >
<Group mt="sm" direction="column"> <Group mt="sm" direction="column" align="center">
<ModuleWrapper module={DateModule} /> <ModuleWrapper module={DateModule} />
</Group> </Group>
</MantineNavbar> </MantineNavbar>

View File

@@ -13,16 +13,7 @@ export default function ModuleWrapper(props: any) {
return null; return null;
} }
return ( return (
<Card <Card hidden={!isShown} mx="sm" withBorder radius="lg" shadow="sm">
hidden={!isShown}
mx="sm"
radius="lg"
shadow="sm"
style={{
// Make background color of the card depend on the theme
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : 'white',
}}
>
<module.component /> <module.component />
</Card> </Card>
); );

View File

@@ -1,4 +1,3 @@
import { Group } from '@mantine/core';
import { getCookie, setCookies } from 'cookies-next'; import { getCookie, setCookies } from 'cookies-next';
import { GetServerSidePropsContext } from 'next'; import { GetServerSidePropsContext } from 'next';
import path from 'path'; import path from 'path';
@@ -6,7 +5,6 @@ import fs from 'fs';
import { useEffect } from 'react'; import { useEffect } from 'react';
import AppShelf from '../components/AppShelf/AppShelf'; import AppShelf from '../components/AppShelf/AppShelf';
import LoadConfigComponent from '../components/Config/LoadConfig'; import LoadConfigComponent from '../components/Config/LoadConfig';
import SearchBar from '../components/SearchBar/SearchBar';
import { Config } from '../tools/types'; import { Config } from '../tools/types';
import { useConfig } from '../tools/state'; import { useConfig } from '../tools/state';
@@ -54,10 +52,7 @@ export default function HomePage(props: any) {
}, [initialConfig]); }, [initialConfig]);
return ( return (
<> <>
<SearchBar /> <AppShelf />
<Group align="start" position="apart" noWrap>
<AppShelf />
</Group>
<LoadConfigComponent /> <LoadConfigComponent />
</> </>
); );