diff options
Diffstat (limited to 'src/views/ChoreEdit/ThingTriggerSection.jsx')
-rw-r--r-- | src/views/ChoreEdit/ThingTriggerSection.jsx | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/views/ChoreEdit/ThingTriggerSection.jsx b/src/views/ChoreEdit/ThingTriggerSection.jsx new file mode 100644 index 0000000..7a040ad --- /dev/null +++ b/src/views/ChoreEdit/ThingTriggerSection.jsx @@ -0,0 +1,230 @@ +import { Widgets } from '@mui/icons-material' +import { + Autocomplete, + Box, + Button, + Card, + Chip, + FormControl, + FormLabel, + Input, + ListItem, + ListItemContent, + ListItemDecorator, + Option, + Select, + TextField, + Typography, +} from '@mui/joy' +import { useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' +const isValidTrigger = (thing, condition, triggerState) => { + const newErrors = {} + if (!thing || !triggerState) { + newErrors.thing = 'Please select a thing and trigger state' + return false + } + if (thing.type === 'boolean') { + if (['true', 'false'].includes(triggerState)) { + return true + } else { + newErrors.type = 'Boolean type does not require a condition' + return false + } + } + if (thing.type === 'number') { + if (isNaN(triggerState)) { + newErrors.triggerState = 'Trigger state must be a number' + return false + } + if (['eq', 'neq', 'gt', 'gte', 'lt', 'lte'].includes(condition)) { + return true + } + } + if (thing.type === 'text') { + if (typeof triggerState === 'string') { + return true + } + } + newErrors.triggerState = 'Trigger state must be a number' + + return false +} + +const ThingTriggerSection = ({ + things, + onTriggerUpdate, + onValidate, + selected, + isAttepmtingToSave, +}) => { + const [selectedThing, setSelectedThing] = useState(null) + const [condition, setCondition] = useState(null) + const [triggerState, setTriggerState] = useState(null) + const navigate = useNavigate() + + useEffect(() => { + if (selected) { + setSelectedThing(things?.find(t => t.id === selected.thingId)) + setCondition(selected.condition) + setTriggerState(selected.triggerState) + } + }, [things]) + + useEffect(() => { + if (selectedThing && triggerState) { + onTriggerUpdate({ + thing: selectedThing, + condition: condition, + triggerState: triggerState, + }) + } + if (isValidTrigger(selectedThing, condition, triggerState)) { + onValidate(true) + } else { + onValidate(false) + } + }, [selectedThing, condition, triggerState]) + + return ( + <Card sx={{ mt: 1 }}> + <Typography level='h5'> + Trigger a task when a thing state changes to a desired state + </Typography> + {things.length !== 0 && ( + <Typography level='body-sm'> + it's look like you don't have any things yet, create a thing to + trigger a task when the state changes. + <Button + startDecorator={<Widgets />} + size='sm' + onClick={() => { + navigate('/things') + }} + > + Go to Things + </Button>{' '} + to create a thing + </Typography> + )} + <FormControl error={isAttepmtingToSave && !selectedThing}> + <Autocomplete + options={things} + value={selectedThing} + onChange={(e, newValue) => setSelectedThing(newValue)} + getOptionLabel={option => option.name} + renderOption={(props, option) => ( + <ListItem {...props}> + <Box + sx={{ + display: 'flex', + flexDirection: 'column', + justifyContent: 'space-between', + alignItems: 'center', + p: 1, + }} + > + <ListItemDecorator sx={{ alignSelf: 'flex-start' }}> + <Typography level='body-lg' textColor='primary'> + {option.name} + </Typography> + </ListItemDecorator> + <ListItemContent> + <Typography level='body2' textColor='text.secondary'> + <Chip>type: {option.type}</Chip>{' '} + <Chip>state: {option.state}</Chip> + </Typography> + </ListItemContent> + </Box> + </ListItem> + )} + renderInput={params => ( + <TextField {...params} label='Select a thing' /> + )} + /> + </FormControl> + <Typography level='body-sm'> + Create a condition to trigger a task when the thing state changes to + desired state + </Typography> + {selectedThing?.type == 'boolean' && ( + <Box> + <Typography level='body-sm'> + When the state of {selectedThing.name} changes as specified below, + the task will become due. + </Typography> + <Select + value={triggerState} + onChange={e => { + if (e?.target.value === 'true' || e?.target.value === 'false') + setTriggerState(e.target.value) + else setTriggerState('false') + }} + > + {['true', 'false'].map(state => ( + <Option + key={state} + value={state} + onClick={() => setTriggerState(state)} + > + {state.charAt(0).toUpperCase() + state.slice(1)} + </Option> + ))} + </Select> + </Box> + )} + {selectedThing?.type == 'number' && ( + <Box> + <Typography level='body-sm'> + When the state of {selectedThing.name} changes as specified below, + the task will become due. + </Typography> + + <Box sx={{ display: 'flex', gap: 1, direction: 'row' }}> + <Typography level='body-sm'>State is</Typography> + <Select value={condition} sx={{ width: '50%' }}> + {[ + { name: 'Equal', value: 'eq' }, + { name: 'Not equal', value: 'neq' }, + { name: 'Greater than', value: 'gt' }, + { name: 'Greater than or equal', value: 'gte' }, + { name: 'Less than', value: 'lt' }, + { name: 'Less than or equal', value: 'lte' }, + ].map(condition => ( + <Option + key={condition.value} + value={condition.value} + onClick={() => setCondition(condition.value)} + > + {condition.name} + </Option> + ))} + </Select> + <Input + type='number' + value={triggerState} + onChange={e => setTriggerState(e.target.value)} + sx={{ width: '50%' }} + /> + </Box> + </Box> + )} + {selectedThing?.type == 'text' && ( + <Box> + <Typography level='body-sm'> + When the state of {selectedThing.name} changes as specified below, + the task will become due. + </Typography> + + <Input + value={triggerState} + onChange={e => setTriggerState(e.target.value)} + label='Enter the text to trigger the task' + /> + </Box> + )} + </Card> + ) +} + +export default ThingTriggerSection |