Skip to content

Commit

Permalink
wip: automation UI
Browse files Browse the repository at this point in the history
  • Loading branch information
cpvalente committed Nov 25, 2024
1 parent de2b60f commit cd34149
Show file tree
Hide file tree
Showing 12 changed files with 481 additions and 4 deletions.
2 changes: 2 additions & 0 deletions apps/client/src/features/app-settings/AppSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ErrorBoundary } from '@sentry/react';
import { useKeyDown } from '../../common/hooks/useKeyDown';

import AboutPanel from './panel/about-panel/AboutPanel';
import AutomationsPanel from './panel/automations-panel/AutomationsPanel';
import ClientControlPanel from './panel/client-control-panel/ClientControlPanel';
import FeatureSettingsPanel from './panel/feature-settings-panel/FeatureSettingsPanel';
import GeneralPanel from './panel/general-panel/GeneralPanel';
Expand Down Expand Up @@ -31,6 +32,7 @@ export default function AppSettings() {
{panel === 'feature_settings' && <FeatureSettingsPanel location={location} />}
{panel === 'sources' && <SourcesPanel />}
{panel === 'integrations' && <IntegrationsPanel location={location} />}
{panel === 'automations' && <AutomationsPanel location={location} />}
{panel === 'client_control' && <ClientControlPanel />}
{panel === 'about' && <AboutPanel />}
{panel === 'network' && <NetworkLogPanel location={location} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,15 @@ export function Card({ children, className, ...props }: { children: ReactNode }
}

export function Table({ className, children }: { className?: string; children: ReactNode }) {
const classes = cx([style.table, className]);
return (
<div className={style.pad}>
<table className={classes}>{children}</table>
<table className={cx([style.table, className])}>{children}</table>
</div>
);
}

export function ListGroup({ children }: { children: ReactNode }) {
return <ul className={style.listGroup}>{children}</ul>;
export function ListGroup({ className, children }: { className?: string; children: ReactNode }) {
return <ul className={cx([style.listGroup, className])}>{children}</ul>;
}

export function ListItem({ children }: { children: ReactNode }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.cardCollapsed {
display: flex;
gap: 1rem;
}

.append {
position: absolute;
display: flex;
align-items: center;
gap: 1rem;
top: -0.75rem;
background-color: $gray-900;
left: 0;
left: 50%;
height: 2rem; // match button height
transform: translateX(-50%);
padding-inline: 1rem;
border-radius: 99px;
font-size: calc(1rem - 2px);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { PropsWithChildren, ReactNode } from 'react';

import { cx } from '../../../../common/utils/styleUtils';
import * as Panel from '../../panel-utils/PanelUtils';

import style from './AutomationCard.module.scss';

interface AutomationCardProps {
append: ReactNode;
className?: string;
}

export default function AutomationCard(props: PropsWithChildren<AutomationCardProps>) {
const { append, className, children } = props;

return (
<Panel.Card className={cx([style.card, className])}>
{children}
<div className={style.append}>{append}</div>
</Panel.Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.cardItems {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.fullWidth {
flex: 1;
align-items: center;
}

.cardCollapsed {
background-color: $gray-1350;
display: flex;
justify-content: space-between;
}

.cardExpanded {
display: flex;
flex-direction: column;
gap: 2rem;
}

.threeCols {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}

.filterRow{
display: grid;
grid-template-columns: 1fr 1fr 1fr auto;
gap: 1rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { PropsWithChildren, useState } from 'react';
import { Button, ButtonGroup, IconButton, Input, Select } from '@chakra-ui/react';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import { IoChevronDown } from '@react-icons/all-files/io5/IoChevronDown';
import { IoTrash } from '@react-icons/all-files/io5/IoTrash';

import AutomationCard from './AutomationCard';
import { cycles } from './automationUtils';

import style from './AutomationItem.module.scss';

interface AutomationCardProps {
title: string;
trigger: string;
}

export default function AutomationItem(props: AutomationCardProps) {
const { title, trigger } = props;
const [expanded, setExpanded] = useState(false);

return (
<li className={style.cardItems}>
<AutomationCard append='Automation title' className={style.cardCollapsed}>
<Input className={style.fullWidth} size='sm' />
<IconButton
size='sm'
variant='ontime-subtle'
icon={<IoChevronDown />}
aria-label='Edit entry'
onClick={() => setExpanded((prev) => !prev)}
/>
<IconButton
size='sm'
variant='ontime-subtle'
icon={<IoTrash />}
color='#FA5656'
aria-label='Delete entry'
onClick={() => setExpanded((prev) => !prev)}
/>
</AutomationCard>
{expanded && <AutomationCardExpanded title={title} trigger={trigger} />}
</li>
);
}

function AutomationCardExpanded(props: PropsWithChildren<AutomationCardProps>) {
const { title, trigger } = props;

Check failure on line 47 in apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.tsx

View workflow job for this annotation

GitHub Actions / unit-test

'title' is assigned a value but never used. Allowed unused vars must match /^_/u

Check failure on line 47 in apps/client/src/features/app-settings/panel/automations-panel/AutomationItem.tsx

View workflow job for this annotation

GitHub Actions / unit-test

'trigger' is assigned a value but never used. Allowed unused vars must match /^_/u
const [triggerRole, setTriggerRole] = useState('all');

return (
<>
<AutomationCard append='Trigger on'>
<Select size='sm' variant='ontime'>
{cycles.map((cycle) => (
<option key={cycle.id} value={cycle.value}>
{cycle.label}
</option>
))}
</Select>
</AutomationCard>

<AutomationCard
append={
<>
If
<ButtonGroup isAttached>
<Button
size='sm'
variant={triggerRole === 'all' ? 'ontime-filled' : 'ontime-subtle'}
onClick={() => setTriggerRole('all')}
>
All
</Button>
<Button
size='sm'
variant={triggerRole === 'any' ? 'ontime-filled' : 'ontime-subtle'}
onClick={() => setTriggerRole('any')}
>
Any
</Button>
</ButtonGroup>
match
</>
}
>
<div className={style.filterRow}>
<Select size='sm' variant='ontime' placeholder='Select a field' value={undefined}>
<option value=''>Title</option>
<option value=''>Cue</option>
<option value=''>Custom field</option>
</Select>
<Select size='sm' variant='ontime' placeholder='Select a matcher' value={undefined}>
<option value=''>equals</option>
<option value=''>not equals</option>
<option value=''>contains</option>
<option value=''>greater than</option>
<option value=''>less than</option>
</Select>
<Input size='sm' variant='ontime-filled' />
<IconButton
size='sm'
variant='ontime-subtle'
icon={<IoTrash />}
aria-label='Delete entry'
onClick={() => undefined}
/>
</div>
<IconButton
size='sm'
variant='ontime-subtle'
icon={<IoAdd />}
aria-label='Add entry'
onClick={() => undefined}
/>
</AutomationCard>

<AutomationCard append='Then send'>
<Select />
<div>
<Input />
<Input />
</div>
<IconButton
size='sm'
variant='ontime-subtle'
icon={<IoAdd />}
aria-label='Add entry'
onClick={() => undefined}
/>
</AutomationCard>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.list {
display: flex;
flex-direction: column;
gap: 3rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Button } from '@chakra-ui/react';
import { TimerLifeCycle } from 'ontime-types';

import * as Panel from '../../panel-utils/PanelUtils';

import AutomationItem from './AutomationItem';

import style from './AutomationManagement.module.scss';

const data = [
{
id: '1',
title: 'Automation 1',
trigger: TimerLifeCycle.onClock,
filterRule: 'all',
filter: [],
output: [],
},
{
id: '2',
title: 'Automation 1',
trigger: TimerLifeCycle.onClock,
filterRule: 'all',
filter: [],
output: [],
},
{
id: '3',
title: 'Automation 1',
trigger: TimerLifeCycle.onClock,
filterRule: 'all',
filter: [],
output: [],
},
];

export default function AutomationManagement() {
return (
<Panel.Card>
<Panel.SubHeader>
Manage automations
<div>
<Button variant='ontime-ghosted' size='sm' onClick={() => undefined} isDisabled={false}>
Revert to saved
</Button>
<Button variant='ontime-filled' size='sm' type='submit' form='osc-form' isDisabled={false} isLoading={false}>
Save
</Button>
</div>
</Panel.SubHeader>

<Panel.Divider />

<Panel.Section
as='form'
id='automations-form'
onSubmit={() => undefined}
onKeyDown={() => console.log('prevent escapee')}
>
<Panel.Loader isLoading={false} />
<ul className={style.list}>
{data.map((automation) => {
return <AutomationItem key={automation.id} title={automation.title} trigger={automation.trigger} />;
})}
</ul>
</Panel.Section>
</Panel.Card>
);
}
Loading

0 comments on commit cd34149

Please sign in to comment.