Skip to content

Commit

Permalink
schedules fixed, starts work on #34
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkrins committed Apr 12, 2024
1 parent 3831e74 commit 04ccdb1
Show file tree
Hide file tree
Showing 18 changed files with 595 additions and 482 deletions.
33 changes: 12 additions & 21 deletions client/src/components/Layout/Navbar.tsx
Expand Up @@ -4,7 +4,6 @@ import classes from './Navbar.module.css';
import { useDisclosure, useMediaQuery } from '@mantine/hooks';
import Header from './Header';
import { useContext, useEffect } from 'react';
import useAPI from '../../hooks/useAPI';
import SchemaContext from '../../providers/SchemaContext2';
import NewSchema from './NewSchema';
import AppContext from '../../providers/AppContext';
Expand Down Expand Up @@ -40,21 +39,13 @@ function Link( { link, active, onClick }: { link: Link, active?: boolean, onClic
}

export default function Navbar({ closeNav }: { closeNav(): void }) {
const { logout, username, version, nav, changeNav } = useContext(AppContext);
const { logout, username, group, version, nav, changeNav, schemas, creatingSchema, refreshSchemas } = useContext(AppContext);
const {loadSchema, loading, loaders, ...schema} = useContext(SchemaContext);
const isMobile = useMediaQuery(`(max-width: ${em(750)})`);
const [opened, { open, close }] = useDisclosure(false);
const [showingStatus, { open: showStatus, close: closeStatus }] = useDisclosure(false);

const { data: schemas = [], loading: creating, fetch: refresh } = useAPI({
url: "/schema",
default: [],
preserve: true,
fetch: true,
mutate: (schemas: Schema[]) => schemas.map(s=>({label: s.name})),
});

useEffect(()=>{ refresh(); },[ schema.name ]);
useEffect(()=>{ if (version) refreshSchemas(); },[ schema.name ]);

const navigate = (link: string) =>{ changeNav(link); if (isMobile) { closeNav(); } }

Expand All @@ -73,11 +64,11 @@ export default function Navbar({ closeNav }: { closeNav(): void }) {
>
<Center><Status resultant={false} /></Center>
</Modal>}
{opened&&<NewSchema opened={opened} close={close} refresh={refresh} />}
{opened&&<NewSchema opened={opened} close={close} refresh={refreshSchemas} />}
{!isMobile&&<Box className={`${classes.section} ${classes.header}`} ><Header version={version} /></Box>}
<Box pt="xs" className={classes.section}>
<Box className={classes.links}>
{commonLinks.map((link) => <Link key={link.label} onClick={()=>navigate(link.label)} active={nav===link.label} link={link} />)}
{commonLinks.filter(l=>l.label==="Users"?group==="admin":true).map((link) => <Link key={link.label} onClick={()=>navigate(link.label)} active={nav===link.label} link={link} />)}
</Box>
</Box>
{schema.valid&&<Box pt="xs" className={classes.section}>
Expand All @@ -89,24 +80,24 @@ export default function Navbar({ closeNav }: { closeNav(): void }) {
<Group className={classes.collectionsHeader} justify="space-between">
<Text size="xs" fw={500} c="dimmed">Schemas</Text>
<Tooltip label="Create schema" withArrow position="right">
<ActionIcon onClick={open} loading={creating} variant="default" size={18}>
<ActionIcon onClick={open} loading={creatingSchema} variant="default" size={18}>
<IconPlus style={{ width: rem(12), height: rem(12) }} stroke={1.5} />
</ActionIcon>
</Tooltip>
</Group>
<Box className={classes.links}>
{schemas.map((link) => {
const active = (schema.name)===link.label;
{schemas.map(name => {
const active = (schema.name)===name;
return (
<Button key={link.label} fullWidth variant="subtle" size="xs"
<Button key={name} fullWidth variant="subtle" size="xs"
styles={{ inner: { justifyContent: 'space-between' }, label: { fontWeight: 400 } }}
classNames={{ root: classes.link }}
onClick={()=>active?undefined:loadSchema(link.label)}
loading={loaders[link.label]}
onClick={()=>active?undefined:loadSchema(name)}
loading={loaders[name]}
disabled={loading}
rightSection={active?<IconX onClick={()=>loadSchema(undefined)} style={{ width: rem(12), height: rem(12), cursor: 'pointer' }} stroke={1.5} />:undefined}
data-active={active?true:undefined}
>{link.label}</Button>
>{name}</Button>
)})}
</Box>
</Box>
Expand Down Expand Up @@ -150,7 +141,7 @@ export default function Navbar({ closeNav }: { closeNav(): void }) {
<IconUser style={{ width: rem(20), height: rem(20) }} stroke={1.5} />
<div style={{ flex: 1 }}>
{!username?<Loader size="xs" type="dots" />:<Text size="sm" fw={500}>{username}</Text>}
<Text c="dimmed" size="xs">administrator</Text>
<Text c="dimmed" size="xs">{group}</Text>
</div>
<IconChevronRight style={{ width: rem(14), height: rem(14) }} stroke={1.5} />
</Group>
Expand Down
8 changes: 5 additions & 3 deletions client/src/components/Rules/Editor/Settings.tsx
@@ -1,4 +1,4 @@
import { ActionIcon, Box, Grid, Group, Select, TextInput, Text, Switch, Textarea, Modal, Divider } from "@mantine/core";
import { ActionIcon, Box, Grid, Group, Select, TextInput, Text, Switch, Textarea, Modal, Fieldset } from "@mantine/core";
import { UseFormReturnType } from "@mantine/form";
import { IconGripVertical, IconKey, IconPlus, IconSettings, IconTable, IconTag, IconTrash } from "@tabler/icons-react";
import SelectConnector from "../../Common/SelectConnector";
Expand Down Expand Up @@ -36,9 +36,11 @@ function Config( { opened, close, form }: { opened?: string, close: ()=>void, f
{...form.getInputProps(`config.${opened}.oto`, { type: 'checkbox' })} />
<Switch label="Case Sensitive" mb="xs" description={<Text inline size="xs" c="orange">Warning: Setting this on any secondary also effects the primary.</Text>}
{...form.getInputProps(`config.${opened}.case`, { type: 'checkbox' })} />
{connector.provider.Config&&<Divider my="xs" label={`${connector.provider.id} options`} labelPosition="left" />}
</>}
{(opened&&connector.provider.Config)&&<connector.provider.Config form={form} name={opened} />}
{(opened&&connector.provider.Config)&&
<Fieldset legend={`${connector.provider.id} options`}>
<connector.provider.Config form={form} name={opened} />
</Fieldset>}
</>}
</Modal>
)
Expand Down
86 changes: 0 additions & 86 deletions client/src/components/Schedules/AddSchedule.tsx

This file was deleted.

93 changes: 93 additions & 0 deletions client/src/components/Schedules/Editor.tsx
@@ -0,0 +1,93 @@
import { Box, Center, Loader, MultiSelect, SegmentedControl, Select, TextInput, Text, Button, Group, Alert, useMantineTheme } from "@mantine/core";
import { useContext, useEffect } from "react";
import AppContext from "../../providers/AppContext";
import { useForm } from "@mantine/form";
import useAPI from "../../hooks/useAPI";
import { IconAlertCircle, IconCalendar, IconFileSignal } from "@tabler/icons-react";
import cronstrue from 'cronstrue';

export default function Editor( { editing, adding, close, refresh }: { editing: schedule, close(): void, adding?: boolean, refresh(): void } ) {
const theme = useMantineTheme();
const { schemas } = useContext(AppContext);
const form = useForm<schedule>({ initialValues: editing });

const { data: rules, setData: setRules, loading, error: e1 } = useAPI<string[]>({
url: `/schema/${form.values.schema}`,
default: [],
check: ()=> !form.values.schema,
monitor: form.values.schema,
mutate: (schema: Schema) => (schema.rules||[]).map(r=>r.name),
});

const { post, put, loading: sending, error: e2 } = useAPI({
url: '/schedule',
data: form.values,
form,
noError: true,
then: () => { refresh(); close(); }
});

const error = e1||e2;

useEffect(() => {
if (form.values.schema===editing.schema) return;
form.setFieldValue('rules', []);
if (!form.values.schema) setRules([]);
}, [ form.values.schema ]);

useEffect(() => {
if (form.values.type===editing.type) return;
form.setFieldValue('value', '');
}, [ form.values.type ]);

const useCron = form.values.type==="cron"||form.values.type==="test";
const cron = useCron && cronstrue.toString(form.values.value, { throwExceptionOnParseError: false });
const invalidCron = (cron||"").includes("An error occured when generating the expression description");
const noValue = form.values.value==='';
const inValid = !form.values.schema || (useCron ? invalidCron : noValue);

return (
<Box>
<Select
label="Target Schema" required clearable
placeholder="Pick schema"
data={schemas}
{...form.getInputProps('schema')}
/>
<MultiSelect mt="xs"
label="Rules" clearable
description={<>Specify which rules will be run on the schema. Leave blank to run all.<br/>Disabled rules will not be run.</>}
placeholder={(form.values.rules||[]).length<=0?"All rules":"Pick rules"}
disabled={rules.length<=0}
data={rules} rightSection={loading?<Loader size="xs" />:undefined}
{...form.getInputProps('rules')}
/>
<SegmentedControl mt="xs" fullWidth
{...form.getInputProps('type')}
defaultValue="cron"
data={[
{ label: <Center><IconCalendar size={18} stroke={1.5} color={theme.colors["lime"][6]} /><Text size="xs" ml={5} >Schedule</Text></Center>, value: 'cron' },
{ label: <Center><IconFileSignal size={18} stroke={1.5} color={theme.colors["blue"][6]} /><Text size="xs" ml={5} >Monitor</Text></Center>, value: 'monitor' },
]}
/>
{useCron ?
<TextInput mt="xs"
label="CRON Expression"
placeholder={'0 * * * MON-FRI'} required
description={noValue?
<>Enter a <a href="https://croner.56k.guru/usage/pattern/" target="_blank">CRON</a> expression to run the schedule.</>: invalidCron ?
<>Invalid <a href="https://croner.56k.guru/usage/pattern/" target="_blank">CRON</a> syntax.</> : cron}
{...form.getInputProps('value')}
error={noValue?false:(invalidCron && cron)}
/>:
<TextInput mt="xs"
label="File Path"
placeholder="D:/watchme.csv" required
description="Schedule will run when a change is detected within this file."
{...form.getInputProps('value')}
/>}
{error&&<Alert mt="xs" icon={<IconAlertCircle size={32} />} color="red">{error}</Alert>}
<Group justify="right" mt="md"><Button loading={sending} disabled={inValid} onClick={()=>adding?post():put()} variant="light" type="submit" >{adding?'Add':'Save'}</Button></Group>
</Box>
)
}

0 comments on commit 04ccdb1

Please sign in to comment.