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
10 changes: 10 additions & 0 deletions src/app/protected/settings/counters/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { revalidatePath } from "next/cache"
import { headers } from "next/headers"
import { CounterTable } from "@/components/settings/counters/CounterTable"
import { getAllCounters } from "@/lib/prisma/counter/getAllCounters"
import { insertCounter } from "@/lib/prisma/counter/insertCounter"
import { updateCounter } from "@/lib/prisma/counter/updateCounter"
import { getAllLocations } from "@/lib/prisma/location/getAllLocations"
import { getAllStaffUsers } from "@/lib/prisma/staff_user/getAllStaffUsers"
import { getStaffUserBySub } from "@/lib/prisma/staff_user/getStaffUserBySub"
import { getAuthContext } from "@/utils/auth/getAuthContext"

Expand All @@ -15,6 +19,8 @@ export default async function Page() {

const currentUser = await getStaffUserBySub(user?.sub ?? "")
const counters = await getAllCounters()
const locations = await getAllLocations()
const staffUsers = await getAllStaffUsers()

const revalidateTable = async () => {
"use server"
Expand All @@ -27,6 +33,10 @@ export default async function Page() {
<CounterTable
currentUser={currentUser}
counters={counters}
locations={locations}
staffUsers={staffUsers}
updateCounter={updateCounter}
insertCounter={insertCounter}
revalidateTable={revalidateTable}
/>
</div>
Expand Down
77 changes: 77 additions & 0 deletions src/components/settings/counters/CounterForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { Dispatch, SetStateAction } from "react"
import { TextField } from "@/components/common"
import { MultiSelect } from "@/components/common/select/MultiSelect"
import { useCounterForm } from "@/hooks/settings/counters/useCounterForm"
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"

export type CounterFormProps = {
counter: Partial<CounterWithRelations>
locations: LocationWithRelations[]
staffUsers: StaffUserWithRelations[]
setFormData: Dispatch<SetStateAction<Partial<CounterWithRelations> | null>>
isReadonly: boolean
}

/**
* CounterForm component renders the form fields for editing a counter.
*
* @param props - The properties object.
* @property props.counter - The counter being edited.
* @property props.locations - List of office locations.
* @property props.staffUsers - List of staff users for assignment.
* @property props.setFormData - Function to update the form data state.
* @property props.isReadonly - Whether the section inputs are read-only.
*/
export const CounterForm = ({
counter,
locations,
staffUsers,
setFormData,
isReadonly,
}: CounterFormProps) => {
const {
selectedLocationCodes,
locationOptions,
selectedStaffUserGuids,
staffUserOptions,
handleNameChange,
handleLocationsChange,
handleStaffUsersChange,
} = useCounterForm({ counter, locations, staffUsers, setFormData })

return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-2 gap-2">
<TextField
id="counter-name"
label="Name"
value={counter.name || ""}
onChange={handleNameChange}
disabled={isReadonly}
required
/>
<div className="col-span-1" />
</div>
<MultiSelect
id="counter-locations"
label="Locations"
options={locationOptions}
selected={selectedLocationCodes}
onChange={handleLocationsChange}
placeholder="Select locations"
disabled={isReadonly}
/>
<MultiSelect
id="counter-staffUsers"
label="Staff Users"
options={staffUserOptions}
selected={selectedStaffUserGuids}
onChange={handleStaffUsersChange}
placeholder={selectedLocationCodes.length === 0 ? "Select locations first" : "Select staff"}
disabled={isReadonly || selectedLocationCodes.length === 0}
/>
</div>
)
}
51 changes: 49 additions & 2 deletions src/components/settings/counters/CounterTable/CounterTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,46 @@
import { DataTable } from "@/components/common/datatable"
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 { CreateCounterModal } from "../CreateCounterModal"
import { EditCounterModal } from "../EditCounterModal"
import { columns } from "./columns"

export type CounterTableProps = {
currentUser: StaffUserWithRelations | null
counters: CounterWithRelations[]
locations: LocationWithRelations[]
staffUsers: StaffUserWithRelations[]
updateCounter: (
counter: Partial<CounterWithRelations>,
prevCounter: Partial<CounterWithRelations>
) => Promise<CounterWithRelations | null>
insertCounter: (counter: Partial<CounterWithRelations>) => Promise<CounterWithRelations | null>
revalidateTable: () => Promise<void>
}

export const CounterTable = ({ currentUser, counters, revalidateTable }: CounterTableProps) => {
const { canCreate, countersToShow, handleRowClick, openCreateCounterModal } = useCounterTable({
export const CounterTable = ({
currentUser,
counters,
locations,
staffUsers,
updateCounter,
insertCounter,
revalidateTable,
}: CounterTableProps) => {
const {
selectedCounter,
canCreate,
canEditSelectedCounter,
countersToShow,
handleRowClick,
editCounterModalOpen,
closeEditCounterModal,
createCounterModalOpen,
openCreateCounterModal,
closeCreateCounterModal,
} = useCounterTable({
currentUser,
counters,
revalidateTable,
Expand Down Expand Up @@ -43,6 +72,24 @@ export const CounterTable = ({ currentUser, counters, revalidateTable }: Counter
emptyMessage="No counters found."
onRowClick={handleRowClick}
/>
<EditCounterModal
open={editCounterModalOpen}
onClose={closeEditCounterModal}
counter={selectedCounter}
locations={locations}
staffUsers={staffUsers}
canEdit={canEditSelectedCounter}
updateCounter={updateCounter}
revalidateTable={revalidateTable}
/>
<CreateCounterModal
open={createCounterModalOpen}
onClose={closeCreateCounterModal}
locations={locations}
staffUsers={staffUsers}
insertCounter={insertCounter}
revalidateTable={revalidateTable}
/>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client"

import {
CloseButton,
DialogActions,
DialogBody,
DialogHeader,
DialogTitle,
Modal,
} from "@/components/common/dialog"
import { useCreateCounterModal } from "@/hooks/settings/counters/useCreateCounterModal"
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 { CounterForm } from "../CounterForm"

type CreateCounterModalProps = {
open: boolean
onClose: () => void
locations: LocationWithRelations[]
staffUsers: StaffUserWithRelations[]
insertCounter: (counter: Partial<CounterWithRelations>) => Promise<CounterWithRelations | null>
revalidateTable: () => Promise<void>
}

export const CreateCounterModal = ({
open,
onClose,
locations,
staffUsers,
insertCounter,
revalidateTable,
}: CreateCounterModalProps) => {
const { isSaving, error, formData, setFormData, isReadonly, isSaveDisabled, handleSave } =
useCreateCounterModal({
open,
onClose,
insertCounter,
revalidateTable,
})

if (!formData) return null

return (
<Modal open={open} onClose={onClose} size="lg">
<DialogHeader trailing={<CloseButton onClick={onClose} />}>
<DialogTitle>Create 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>
)}

<CounterForm
counter={formData}
locations={locations}
staffUsers={staffUsers}
setFormData={setFormData}
isReadonly={isReadonly}
/>
</form>
</DialogBody>

<DialogActions>
<button type="button" className="tertiary" onClick={onClose}>
Cancel
</button>
<button type="button" className="primary" onClick={handleSave} disabled={isSaveDisabled}>
{isSaving ? "Saving..." : "Save Changes"}
</button>
</DialogActions>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./CreateCounterModal"
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"use client"

import {
CloseButton,
DialogActions,
DialogBody,
DialogHeader,
DialogTitle,
Modal,
} from "@/components/common/dialog"
import { useEditCounterModal } from "@/hooks/settings/counters/useEditCounterModal"
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 { CounterForm } from "../CounterForm"

type EditCounterModalProps = {
open: boolean
onClose: () => void
counter: CounterWithRelations | null
locations: LocationWithRelations[]
staffUsers: StaffUserWithRelations[]
canEdit: boolean
updateCounter: (
counter: Partial<CounterWithRelations>,
prevCounter: Partial<CounterWithRelations>
) => Promise<CounterWithRelations | null>
revalidateTable: () => Promise<void>
}

export const EditCounterModal = ({
open,
onClose,
counter,
locations,
staffUsers,
canEdit,
updateCounter,
revalidateTable,
}: EditCounterModalProps) => {
const { isSaving, error, formData, setFormData, isReadonly, isSaveDisabled, handleSave } =
useEditCounterModal({
open,
onClose,
counter,
canEdit,
updateCounter,
revalidateTable,
})

if (!counter || !formData) return null

return (
<Modal open={open} onClose={onClose} size="lg">
<DialogHeader trailing={<CloseButton onClick={onClose} />}>
<DialogTitle>Edit Counter: {counter.name}</DialogTitle>
</DialogHeader>

<DialogBody>
<form className="space-y-5">
{!canEdit && (
<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">
{counter.name === "Counter"
? "The default counter cannot be edited."
: "You do not have permission to edit this counter."}
</p>
</div>
)}

{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>
)}

<CounterForm
counter={formData}
locations={locations}
staffUsers={staffUsers}
setFormData={setFormData}
isReadonly={isReadonly}
/>
</form>
</DialogBody>

<DialogActions>
<button type="button" className="tertiary" onClick={onClose}>
Cancel
</button>
<button type="button" className="primary" onClick={handleSave} disabled={isSaveDisabled}>
{isSaving ? "Saving..." : "Save Changes"}
</button>
</DialogActions>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./EditCounterModal"
1 change: 1 addition & 0 deletions src/hooks/settings/counters/useCounterForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useCounterForm } from "./useCounterForm"
Loading
Loading