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