Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/protected/settings/counters/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { revalidatePath } from "next/cache"
import { headers } from "next/headers"
import { CounterTable } from "@/components/settings/counters/CounterTable"
import { deleteCounter } from "@/lib/prisma/counter/deleteCounter"
import { getAllCounters } from "@/lib/prisma/counter/getAllCounters"
import { insertCounter } from "@/lib/prisma/counter/insertCounter"
import { updateCounter } from "@/lib/prisma/counter/updateCounter"
Expand Down Expand Up @@ -37,6 +38,7 @@ export default async function Page() {
staffUsers={staffUsers}
updateCounter={updateCounter}
insertCounter={insertCounter}
deleteCounter={deleteCounter}
revalidateTable={revalidateTable}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use client"

import {
CloseButton,
DialogActions,
DialogBody,
DialogHeader,
DialogTitle,
Modal,
} from "@/components/common/dialog"
import { useConfirmDeleteCounterModal } from "@/hooks/settings/counters/useConfirmDeleteCounterModal"
import type { CounterWithRelations } from "@/lib/prisma/counter/types"

type ConfirmDeleteCounterModalProps = {
open: boolean
onClose: () => void
counter: CounterWithRelations | null
deleteCounter: (id: string) => Promise<boolean>
revalidateTable: () => Promise<void>
}

export const ConfirmDeleteCounterModal = ({
open,
onClose,
counter,
deleteCounter,
revalidateTable,
}: ConfirmDeleteCounterModalProps) => {
const { error, deleteConfirmation, setDeleteConfirmation, isDeleteDisabled, handleDelete } =
useConfirmDeleteCounterModal({
open,
onClose,
counter,
deleteCounter,
revalidateTable,
})

if (!counter) return null

return (
<Modal open={open} onClose={onClose} size="sm">
<DialogHeader trailing={<CloseButton onClick={onClose} />} className="bg-background-danger">
<DialogTitle className="text-white">Delete Counter</DialogTitle>
</DialogHeader>

<DialogBody>
<form className="space-y-5">
{error && (
<div className="flex flex-col gap-1 rounded-md border-l-4 border-l-red-600 bg-red-50 p-4">
<p className="text-sm font-medium text-red-800">{error}</p>
</div>
)}

<p className="text-sm text-typography-primary">
All users assigned to this counter will be set to the default counter.
</p>

<div>
<label
htmlFor="delete-counter"
className="block text-sm font-medium text-typography-primary"
>
Type "<span className="text-typography-danger">{counter.name}</span>" to confirm
deleting this counter.
</label>
<input
id="delete-counter"
value={deleteConfirmation}
onChange={(e) => setDeleteConfirmation(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && !isDeleteDisabled) handleDelete()
}}
autoComplete="off"
className="mt-2 block w-full rounded-md border border-border-dark px-2 py-1 text-xs text-typography-primary"
/>
</div>
</form>
</DialogBody>

<DialogActions>
<button type="button" className="tertiary" onClick={onClose}>
Cancel
</button>
<button
type="button"
className="primary danger"
onClick={handleDelete}
disabled={isDeleteDisabled}
>
Delete
</button>
</DialogActions>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ConfirmDeleteCounterModal"
16 changes: 16 additions & 0 deletions src/components/settings/counters/CounterTable/CounterTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useCounterTable } from "@/hooks/settings/counters/useCounterTable"
import type { CounterWithRelations } from "@/lib/prisma/counter/types"
import type { LocationWithRelations } from "@/lib/prisma/location/types"
import type { StaffUserWithRelations } from "@/lib/prisma/staff_user/types"
import { ConfirmDeleteCounterModal } from "../ConfirmDeleteCounterModal"
import { CreateCounterModal } from "../CreateCounterModal"
import { EditCounterModal } from "../EditCounterModal"
import { columns } from "./columns"
Expand All @@ -19,6 +20,7 @@ export type CounterTableProps = {
prevCounter: Partial<CounterWithRelations>
) => Promise<CounterWithRelations | null>
insertCounter: (counter: Partial<CounterWithRelations>) => Promise<CounterWithRelations | null>
deleteCounter: (id: string) => Promise<boolean>
revalidateTable: () => Promise<void>
}

Expand All @@ -29,19 +31,24 @@ export const CounterTable = ({
staffUsers,
updateCounter,
insertCounter,
deleteCounter,
revalidateTable,
}: CounterTableProps) => {
const {
selectedCounter,
canCreate,
canEditSelectedCounter,
canDeleteSelectedCounter,
countersToShow,
handleRowClick,
editCounterModalOpen,
closeEditCounterModal,
createCounterModalOpen,
openCreateCounterModal,
closeCreateCounterModal,
deleteCounterModalOpen,
openDeleteCounterModal,
closeDeleteCounterModal,
} = useCounterTable({
currentUser,
counters,
Expand Down Expand Up @@ -79,8 +86,10 @@ export const CounterTable = ({
locations={locations}
staffUsers={staffUsers}
canEdit={canEditSelectedCounter}
canDelete={canDeleteSelectedCounter}
updateCounter={updateCounter}
revalidateTable={revalidateTable}
openConfirmDeleteCounterModal={openDeleteCounterModal}
/>
<CreateCounterModal
open={createCounterModalOpen}
Expand All @@ -90,6 +99,13 @@ export const CounterTable = ({
insertCounter={insertCounter}
revalidateTable={revalidateTable}
/>
<ConfirmDeleteCounterModal
open={deleteCounterModalOpen}
onClose={closeDeleteCounterModal}
counter={selectedCounter}
deleteCounter={deleteCounter}
revalidateTable={revalidateTable}
/>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ type EditCounterModalProps = {
locations: LocationWithRelations[]
staffUsers: StaffUserWithRelations[]
canEdit: boolean
canDelete: boolean
updateCounter: (
counter: Partial<CounterWithRelations>,
prevCounter: Partial<CounterWithRelations>
) => Promise<CounterWithRelations | null>
revalidateTable: () => Promise<void>
openConfirmDeleteCounterModal: () => void
}

export const EditCounterModal = ({
Expand All @@ -35,18 +37,30 @@ export const EditCounterModal = ({
locations,
staffUsers,
canEdit,
canDelete,
updateCounter,
revalidateTable,
openConfirmDeleteCounterModal,
}: EditCounterModalProps) => {
const { isSaving, error, formData, setFormData, isReadonly, isSaveDisabled, handleSave } =
useEditCounterModal({
open,
onClose,
counter,
canEdit,
updateCounter,
revalidateTable,
})
const {
isSaving,
error,
formData,
setFormData,
isReadonly,
isSaveDisabled,
handleSave,
handleOpenDelete,
} = useEditCounterModal({
open,
onClose,
counter,
canEdit,
canDelete,
updateCounter,
revalidateTable,
openConfirmDeleteCounterModal,
})

if (!counter || !formData) return null

Expand Down Expand Up @@ -88,6 +102,11 @@ export const EditCounterModal = ({
<button type="button" className="tertiary" onClick={onClose}>
Cancel
</button>
{canDelete && (
<button type="button" className="secondary danger" onClick={handleOpenDelete}>
Delete
</button>
)}
<button type="button" className="primary" onClick={handleSave} disabled={isSaveDisabled}>
{isSaving ? "Saving..." : "Save Changes"}
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useConfirmDeleteCounterModal } from "./useConfirmDeleteCounterModal"
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useRouter } from "next/navigation"
import { useEffect, useState } from "react"
import type { CounterWithRelations } from "@/lib/prisma/counter/types"

type UseConfirmDeleteCounterModalProps = {
open: boolean
onClose: () => void
counter: CounterWithRelations | null
deleteCounter: (id: string) => Promise<boolean>
revalidateTable: () => Promise<void>
}

/**
* Custom hook encapsulating all logic for the ConfirmDeleteCounterModal component.
*
* @param props - Hook configuration.
* @property props.open - Whether the modal is open.
* @property props.onClose - Callback to close the modal.
* @property props.counter - The counter to delete.
* @property props.deleteCounter - Async function to delete the counter.
* @property props.revalidateTable - Async function to refresh the table.
* @returns State, derived flags, and handlers for the modal.
*/
export const useConfirmDeleteCounterModal = ({
open,
onClose,
counter,
deleteCounter,
revalidateTable,
}: UseConfirmDeleteCounterModalProps) => {
const router = useRouter()
const [error, setError] = useState<string | null>(null)
const [deleteConfirmation, setDeleteConfirmation] = useState("")

useEffect(() => {
if (open) {
setDeleteConfirmation("")
setError(null)
}
}, [open])

const handleDelete = async () => {
if (!counter) return
try {
await deleteCounter(counter.id)
await revalidateTable()
setDeleteConfirmation("")
onClose()
router.refresh()
} catch (e: unknown) {
if (e instanceof Error) {
setError(e.message)
} else {
setError("An unknown error occurred")
}
}
}

const isDeleteDisabled = deleteConfirmation !== counter?.name

return {
error,
deleteConfirmation,
setDeleteConfirmation,
isDeleteDisabled,
handleDelete,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ type UseEditCounterModalProps = {
onClose: () => void
counter: CounterWithRelations | null
canEdit: boolean
canDelete: boolean
updateCounter: (
counter: Partial<CounterWithRelations>,
prevCounter: Partial<CounterWithRelations>
) => Promise<CounterWithRelations | null>
revalidateTable: () => Promise<void>
openConfirmDeleteCounterModal: () => void
}

const EditCounterSchema = z.object({
Expand All @@ -38,8 +40,10 @@ export const useEditCounterModal = ({
onClose,
counter,
canEdit,
canDelete,
updateCounter,
revalidateTable,
openConfirmDeleteCounterModal,
}: UseEditCounterModalProps) => {
const router = useRouter()
const [isSaving, setIsSaving] = useState(false)
Expand Down Expand Up @@ -92,13 +96,20 @@ export const useEditCounterModal = ({

const isSaveDisabled = isReadonly || isSaving || !isFormValid || !hasMadeChanges

const handleOpenDelete = () => {
openConfirmDeleteCounterModal()
onClose()
}

return {
isSaving,
error,
formData,
setFormData,
isReadonly,
isSaveDisabled,
canDelete,
handleSave,
handleOpenDelete,
}
}
Loading
Loading