import { Type as ListType, SwipeableList, SwipeableListItem, SwipeAction, TrailingActions, } from '@meauxt/react-swipeable-list' import '@meauxt/react-swipeable-list/dist/styles.css' import { Add, Delete, Edit, Flip, MoreVert, PlusOne, ToggleOff, ToggleOn, Widgets, } from '@mui/icons-material' import { Avatar, Box, Chip, Container, IconButton, Stack, Typography, } from '@mui/joy' import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { useNotification } from '../../service/NotificationProvider' import { CreateThing, DeleteThing, GetThings, SaveThing, UpdateThingState, } from '../../utils/Fetcher' import { getSafeBottomStyles } from '../../utils/SafeAreaUtils' import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import CreateThingModal from '../Modals/Inputs/CreateThingModal' import EditThingStateModal from '../Modals/Inputs/EditThingState' const ThingCardContent = ({ thing, onCardClick, onToggleActions }) => { const getThingIcon = type => { if (type === 'text') { return } else if (type === 'number') { return } else if (type === 'boolean') { if (thing.state === 'true') { return } else { return } } else { return } } const getThingAvatar = () => { const typeConfig = { text: { color: 'primary', icon: }, number: { color: 'success', icon: }, boolean: { color: thing.state === 'true' ? 'success' : 'neutral', icon: thing.state === 'true' ? : , }, } const config = typeConfig[thing?.type] || typeConfig.boolean return ( {config.icon} ) } return ( {/* Avatar and Primary Action */} {getThingAvatar()} {/* Content - Center */} {/* Line 1: Name + State */} {thing?.name} {thing?.state} {/* Line 2: Type */} {thing?.type} {onToggleActions && ( { e.stopPropagation() onToggleActions() }} > )} ) } const ThingsView = () => { const navigate = useNavigate() const [things, setThings] = useState([]) const [isShowCreateThingModal, setIsShowCreateThingModal] = useState(false) const [isShowEditThingStateModal, setIsShowEditStateModal] = useState(false) const [createModalThing, setCreateModalThing] = useState(null) const [confirmModelConfig, setConfirmModelConfig] = useState({}) const [showMoreInfoId, setShowMoreInfoId] = useState(null) const { showError, showNotification } = useNotification() useEffect(() => { // fetch things GetThings().then(result => { result.json().then(data => { setThings(data.res) }) }) }, []) const handleSaveThing = thing => { let saveFunc = CreateThing if (thing?.id) { saveFunc = SaveThing } saveFunc(thing) .then(result => { result.json().then(data => { if (thing?.id) { const currentThings = [...things] const thingIndex = currentThings.findIndex( currentThing => currentThing.id === thing.id, ) currentThings[thingIndex] = data.res setThings(currentThings) } else { const currentThings = [...things] currentThings.push(data.res) setThings(currentThings) } showNotification({ type: 'success', title: 'Saved', message: 'Thing saved successfully', }) }) }) .catch(error => { if (error?.queued) { showError({ title: 'Unable to save thing', message: 'You are offline and the request has been queued', }) } else { showError({ title: 'Unable to save thing', message: 'An error occurred while saving the thing', }) } }) } const handleEditClick = thing => { setIsShowEditStateModal(true) setCreateModalThing(thing) } const handleDeleteClick = thing => { setConfirmModelConfig({ isOpen: true, title: 'Delete Things', confirmText: 'Delete', cancelText: 'Cancel', message: 'Are you sure you want to delete this Thing?', onClose: isConfirmed => { if (isConfirmed === true) { DeleteThing(thing.id) .then(response => { if (response.ok) { const currentThings = [...things] const thingIndex = currentThings.findIndex( currentThing => currentThing.id === thing.id, ) currentThings.splice(thingIndex, 1) setThings(currentThings) } else if (response.status === 405) { showError({ title: 'Unable to Delete Thing', message: 'Unable to delete thing with associated tasks', }) } // if method not allwo show snackbar: }) .catch(error => { if (error?.queued) { showError({ title: 'Unable to delete thing', message: 'You are offline and the request has been queued', }) } else { showError({ title: 'Unable to delete thing', message: 'An error occurred while deleting the thing', }) } }) } setConfirmModelConfig({}) }, }) } const handleStateChangeRequest = thing => { const updatedThing = { ...thing } if (updatedThing?.type === 'number') { updatedThing.state = Number(updatedThing.state) + 1 } else if (updatedThing?.type === 'boolean') { if (updatedThing.state === 'true') { updatedThing.state = 'false' } else { updatedThing.state = 'true' } } UpdateThingState(updatedThing) .then(result => { result.json().then(data => { const currentThings = [...things] const thingIndex = currentThings.findIndex( currentThing => currentThing.id === updatedThing.id, ) currentThings[thingIndex] = data.res setThings(currentThings) showNotification({ type: 'success', title: 'Updated', message: 'Thing state updated successfully', }) }) }) .catch(error => { if (error?.queued) { showError({ title: 'Unable to update thing state', message: 'You are offline and the request has been queued', }) } else { showError({ title: 'Unable to update thing state', message: 'An error occurred while updating the thing state', }) } }) } const handleSetThingState = thing => { UpdateThingState(thing) .then(result => { result.json().then(data => { const currentThings = [...things] const thingIndex = currentThings.findIndex( currentThing => currentThing.id === thing.id, ) currentThings[thingIndex] = data.res setThings(currentThings) showNotification({ type: 'success', title: 'Updated', message: 'Thing state updated successfully', }) }) }) .catch(error => { showError({ title: 'Unable to update thing state', message: 'An error occurred while updating the thing state', }) }) } return ( {/* */} Things Things are custom fields that can be attached to tasks to capture additional information. They can be of type text, number, or boolean. You can associate things with tasks and have the task due once condition is met {things.length === 0 && ( No things has been created/found )} {things.map(thing => ( navigate(`/things/${thing?.id}`)} key={thing.id} swipeActionOpen={showMoreInfoId === thing.id ? 'trailing' : null} trailingActions={ { if (thing?.type === 'text') { handleEditClick(thing) } else { handleStateChangeRequest(thing) } }} > {thing?.type === 'text' ? ( ) : thing?.type === 'number' ? ( ) : thing.state === 'true' ? ( ) : ( )} {thing?.type === 'text' ? 'Edit' : 'Toggle'} handleEditClick(thing)}> Edit handleDeleteClick(thing)}> Delete } > { if (showMoreInfoId === thing.id) { setShowMoreInfoId(null) } else { setShowMoreInfoId(thing.id) } }} /> ))} } onClick={() => { setIsShowCreateThingModal(true) }} > {isShowCreateThingModal && ( { setIsShowCreateThingModal(false) setCreateModalThing(null) }} onSave={handleSaveThing} currentThing={createModalThing} /> )} {isShowEditThingStateModal && ( { setIsShowEditStateModal(false) setCreateModalThing(null) }} onSave={handleSetThingState} currentThing={createModalThing} /> )} ) } export default ThingsView