aboutsummaryrefslogtreecommitdiffstats
path: root/src/views/ChoreEdit/ThingTriggerSection.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/views/ChoreEdit/ThingTriggerSection.jsx')
-rw-r--r--src/views/ChoreEdit/ThingTriggerSection.jsx230
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