-
context: front-end, admin panel Which option do you think is better? A. To keep them separate in its own state for every single entity In my opinion it's a good idea to add a name for the record which is about to be removed. Please answer below so we know how to proceed. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
This is how I would've made it: import { action, makeObservable, observable } from 'mobx'
import { enableStaticRendering } from 'mobx-react'
enableStaticRendering(typeof window === 'undefined')
type Record = {
id: string
name: string
}
class ModalStoreImpl {
isDetailsOpen = false
isDeleteOpen = false
isDeleteAllOpen = false
isSelected = false
recordToDelete: Record ={
id: '',
name: '',
}
idsToDelete: string[] = []
constructor() {
makeObservable(this, {
isDetailsOpen: observable,
isDeleteOpen: observable,
isDeleteAllOpen: observable,
isSelected: observable,
recordToDelete: observable,
idsToDelete: observable,
setRecordToDelete: action,
setIdsToDelete: action,
selectedPositive: action,
selectedNegative: action,
showDetails: action,
hideDetails: action,
showDelete: action,
hideDelete: action,
showDeleteAll: action,
hideDeleteAll: action,
})
}
selectedPositive = () => {
this.isSelected = true
}
selectedNegative = () => {
this.isSelected = false
}
showDetails = () => {
this.isDetailsOpen = true
}
hideDetails = () => {
this.isDetailsOpen = false
}
showDelete = () => {
this.isDeleteOpen = true
}
hideDelete = () => {
this.isDeleteOpen = false
}
showDeleteAll = () => {
this.isDeleteAllOpen = true
}
hideDeleteAll = () => {
this.isDeleteAllOpen = false
}
setRecordToDelete = (record: Record) => {
this.recordToDelete = record
}
setIdsToDelete = (ids: string[]) => {
this.idsToDelete = ids
}
}
export const ModalStore = new ModalStoreImpl() |
Beta Was this translation helpful? Give feedback.
-
Seems good. |
Beta Was this translation helpful? Give feedback.
-
I agree to have them using a single state included in the ModalStore. |
Beta Was this translation helpful? Give feedback.
-
I'm gonna share some ideas on how I've resolved similar scenarios before The general idea is to keep the state encapsulated in a hook and use that hook whenever we need confirmation for deletion. Using a modal store would lead us in direction of breaking the open/closed SOLID principle as everyone will be able to open or close our modal even if we do not expect that. My suggestion for this functionality:
export type ConfirmProps = {
onConfirm?: () => void | Promise<void>
onClose?: () => void | Promise<void>
}
export type ConfirmHookProps = {
open: boolean
openHandler: () => void
confirmHandler: () => void
closeHandler: () => void
}
const useConfirm = ({ onConfirm, onClose }: ConfirmProps): ConfirmHookProps => {
const [open, setOpen] = useState<boolean>(false)
return {
open,
loading,
openHandler: () => {
setOpen(true)
},
confirmHandler: async () => {
setOpen(false)
if (typeof onConfirm === 'function') {
await onConfirm()
}
},
closeHandler: async () => {
setOpen(false)
if (typeof onClose === 'function') {
await onClose()
}
},
}
} After we have the hook with encapsulated // DeleteButton.tsx
const mutationFn = useDeleteBankAccount(params.row.id) // This is received as param to <DeleteButton mutationFn={...} />
const deleteMutation = useMutation({
mutationFn,
onError: () => AlertStore.show(t('alerts.error'), 'error'),
onSuccess: () => {
AlertStore.show(t('alerts.delete'), 'success')
router.push(routes.admin.bankaccounts.index) // This is received as param to <DeleteButton onDelete={...} />
},
})
const { open, openHandler, closeHandler, confirmHandler } = useConfirm({
onConfirm: async () => {
try {
// execute delete mutation here
deleteMutation()
} catch (error) {
console.error(error)
}
},
})
// ...
return (
<>
<Button onClick={openHandler} />
<ConfirmModal
confirmLabel={'Delete this record?"}
{...{ open, closeHandler, confirmHandler }}
/>
</>
) And we have the actual // ConfirmModal.tsx
type Props = Pick<ConfirmHookProps, 'open' | 'confirmHandler' | 'closeHandler'> & {
confirmLabel: string
confirmContent?: string
confirmButtonLabel?: string
}
export default function ConfirmModal({
open,
confirmLabel,
confirmContent,
confirmButtonLabel = 'cta.confirm',
closeHandler,
confirmHandler,
}: Props) {
const { t } = useTranslation('admin')
return (
<Dialog open={open} onClose={closeHandler}>
<Card>
<CardContent>
<Typography variant="h6" sx={{ marginBottom: '16px', textAlign: 'center' }}>
{confirmLabel}
</Typography>
<Typography variant="body1" sx={{ marginBottom: '16px', textAlign: 'center' }}>
{t('modals.delete.content')} <b>{confirmContent}</b>
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Button color="error" onClick={confirmHandler}>
{confirmButtonLabel}
</Button>
<Button onClick={closeHandler}>{t('cta.cancel')}</Button>
</Box>
</CardContent>
</Card>
</Dialog>
)
} With all these components we achieve great reusability and we're keeping them close for external modification. We can use all of them inside the export default function GridActions({ id }:{ id:string }) {
const mutationFn = useDeleteBankAccount(params.row.id) // This can be even above GridActions in the hierarchy
return (
<Box>
<DeleteButton
mutationFn={mutationFn}
onDelete={() => {
router.push(routes.admin.bankaccounts.index)
// invalidate
}}
/>
</Box>
)
} cc @dpmarkov |
Beta Was this translation helpful? Give feedback.
I'm gonna share some ideas on how I've resolved similar scenarios before
The general idea is to keep the state encapsulated in a hook and use that hook whenever we need confirmation for deletion.
Using a modal store would lead us in direction of breaking the open/closed SOLID principle as everyone will be able to open or close our modal even if we do not expect that.
Using a single instance modal store would lead to having a collection of records/ids that belong to different entities at the same time.
My suggestion for this functionality:
useConfirm
…