diff --git a/prisma/migrations/20260625230055_drop_person_department_enum/migration.sql b/prisma/migrations/20260625230055_drop_person_department_enum/migration.sql new file mode 100644 index 0000000..01f2c9c --- /dev/null +++ b/prisma/migrations/20260625230055_drop_person_department_enum/migration.sql @@ -0,0 +1,41 @@ +BEGIN; + +-- Seed legacy teams from the old PersonDepartment enum English display names. +INSERT INTO "Team" ("id", "name", "createdAt", "updatedAt") +VALUES + (gen_random_uuid(), 'IT', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Engineering', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Logistics', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Traffic', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Driver', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Administration', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Sales', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP), + (gen_random_uuid(), 'Other', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +ON CONFLICT (lower("name")) DO NOTHING; + +-- Backfill Person.teamId from the legacy Person.department enum values. +UPDATE "Person" +SET "teamId" = ( + SELECT "id" FROM "Team" WHERE lower("name") = lower(CASE "department" + WHEN 'IT' THEN 'IT' + WHEN 'ENGINEERING' THEN 'Engineering' + WHEN 'LOGISTICS' THEN 'Logistics' + WHEN 'TRAFFIC' THEN 'Traffic' + WHEN 'DRIVER' THEN 'Driver' + WHEN 'ADMINISTRATION' THEN 'Administration' + WHEN 'SALES' THEN 'Sales' + WHEN 'OTHER' THEN 'Other' + END) +) +WHERE "department" IS NOT NULL; + +-- Drop the legacy department index. +DROP INDEX "Person_department_deletedAt_idx"; + +-- Drop the legacy department column. +ALTER TABLE "Person" DROP COLUMN "department"; + +-- Drop the legacy enum type. +DROP TYPE "PersonDepartment"; + +COMMIT; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 2453021..99e1765 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -110,22 +110,10 @@ model UserInvitation { // PEOPLE // ====================================================== -enum PersonDepartment { - IT - ENGINEERING - LOGISTICS - TRAFFIC - DRIVER - ADMINISTRATION - SALES - OTHER -} - model Person { - id String @id @default(uuid(7)) @db.Uuid - firstName String - lastName String - department PersonDepartment? + id String @id @default(uuid(7)) @db.Uuid + firstName String + lastName String email String? phone String? @@ -143,7 +131,6 @@ model Person { assignments Assignment[] @@index([lastName, firstName]) - @@index([department, deletedAt]) @@index([teamId, deletedAt]) @@index([teamId]) @@index([deletedAt]) diff --git a/src/actions/assignment.actions.ts b/src/actions/assignment.actions.ts index 4c50ba4..54f3aae 100644 --- a/src/actions/assignment.actions.ts +++ b/src/actions/assignment.actions.ts @@ -35,14 +35,18 @@ export async function createAssignment(formData: CreateAssignmentFormType) { try { const createdBy = await getAuthenticatedUserId() + const { itemId, quantity, notes } = validatedFields.data + if (!itemId || quantity == null) { + throw new Error("Missing required assignment fields") + } const result = await createAssignmentUseCase({ ...validatedFields.data, lines: [ { - itemId: validatedFields.data.itemId!, - quantity: validatedFields.data.quantity!, - notes: validatedFields.data.notes, + itemId, + quantity, + notes, }, ], actorId: createdBy, @@ -86,14 +90,18 @@ export async function updateAssignment(formData: UpdateAssignmentFormType) { try { const createdBy = await getAuthenticatedUserId() + const { itemId, quantity, notes } = validatedFields.data + if (!itemId || quantity == null) { + throw new Error("Missing required assignment fields") + } const result = await updateAssignmentUseCase({ ...validatedFields.data, lines: [ { - itemId: validatedFields.data.itemId!, - quantity: validatedFields.data.quantity!, - notes: validatedFields.data.notes, + itemId, + quantity, + notes, }, ], actorId: createdBy, diff --git a/src/actions/import.actions.ts b/src/actions/import.actions.ts index e0f1ca0..b1b4b21 100644 --- a/src/actions/import.actions.ts +++ b/src/actions/import.actions.ts @@ -303,7 +303,6 @@ export async function importItems(formData: ImportFormType) { lastName, email: undefined, phone: "", - department: "OTHER", }) } else { newPerson = existingPerson.data[0] diff --git a/src/actions/person.messages.ts b/src/actions/person.messages.ts index fc60ab6..d42adc8 100644 --- a/src/actions/person.messages.ts +++ b/src/actions/person.messages.ts @@ -6,6 +6,7 @@ type FieldErrors = Record const personErrorMessageKeys = { "Email already exists": "duplicateEmail", + "Team not found": "teamNotFound", } as const satisfies Record function isPersonErrorMessage( diff --git a/src/actions/user.messages.ts b/src/actions/user.messages.ts index 9d39261..f43f466 100644 --- a/src/actions/user.messages.ts +++ b/src/actions/user.messages.ts @@ -76,7 +76,7 @@ export function localizeUnifiedCreateFieldErrors( return message if (field === "lastName" && message === schemaCopy.lastNameRequired) return message - if (field === "department" && message === schemaCopy.departmentRequired) + if (field === "teamId" && message === schemaCopy.teamIdInvalid) return message if (field === "email" && message === schemaCopy.emailInvalid) return message diff --git a/src/app/(dashboard)/inventory/assets/[assetId]/page.tsx b/src/app/(dashboard)/inventory/assets/[assetId]/page.tsx index 1bbb435..97fb846 100644 --- a/src/app/(dashboard)/inventory/assets/[assetId]/page.tsx +++ b/src/app/(dashboard)/inventory/assets/[assetId]/page.tsx @@ -7,7 +7,10 @@ import { Button } from "@/components/ui/button" import { getI18n } from "@/i18n/server" import { AssetService } from "@/services/asset.service" -import type { AssetDetailCopy, AssetStatusCopy } from "../_components/asset.copy" +import type { + AssetDetailCopy, + AssetStatusCopy, +} from "../_components/asset.copy" function formatAssetStatus( status: string, @@ -77,7 +80,9 @@ export default async function AssetDetailPage({
{asset.serialNumber}
-
{copy.labels.assetTag}
+
+ {copy.labels.assetTag} +
{asset.assetTag ?? missingValue}
@@ -119,11 +124,19 @@ export default async function AssetDetailPage({
{asset.notes ?? missingValue}
-
{copy.labels.status}
-
{formatAssetStatus(asset.status, statusCopy, { unknownStatus: missingValue })}
+
+ {copy.labels.status} +
+
+ {formatAssetStatus(asset.status, statusCopy, { + unknownStatus: missingValue, + })} +
-
{copy.labels.person}
+
+ {copy.labels.person} +
{formatPersonName(asset.assignment?.person, missingValue)}
diff --git a/src/app/(dashboard)/people/[personId]/edit/page.tsx b/src/app/(dashboard)/people/[personId]/edit/page.tsx index 99c94f6..a49690a 100644 --- a/src/app/(dashboard)/people/[personId]/edit/page.tsx +++ b/src/app/(dashboard)/people/[personId]/edit/page.tsx @@ -1,5 +1,6 @@ import { getI18n } from "@/i18n/server" import { PersonService } from "@/services/person.service" +import { listTeamsUseCase } from "@/use-cases/team.use-cases" import EditPersonForm from "../../_components/edit.person.form" @@ -13,6 +14,7 @@ export default async function PersonEditPage({ const personCopy = dictionary.inventory.people const userCopy = dictionary.admin.users const person = await PersonService.findByIdWithUser(personId) + const teams = await listTeamsUseCase() if (!person) { return
{personCopy.edit.notFound}
@@ -28,10 +30,8 @@ export default async function PersonEditPage({ formCopy={userCopy.form} schemaCopy={{ ...userCopy.schema, ...personCopy.schema }} roleLabels={userCopy.roles} - userFallbackCopy={userCopy.fallback} - departmentCopy={personCopy.departments} - fallbackCopy={personCopy.fallback} submitButtonCopy={dictionary.common.submitButton} + teams={teams} /> ) diff --git a/src/app/(dashboard)/people/[personId]/page.tsx b/src/app/(dashboard)/people/[personId]/page.tsx index fd462d3..9434eba 100644 --- a/src/app/(dashboard)/people/[personId]/page.tsx +++ b/src/app/(dashboard)/people/[personId]/page.tsx @@ -4,7 +4,6 @@ import { getI18n } from "@/i18n/server" import { AssignmentService } from "@/services/assignment.service" import { PersonService } from "@/services/person.service" -import { formatPersonDepartment } from "../_components/person.copy" import { formatUserRole, type UserFallbackCopy, @@ -45,16 +44,8 @@ export default async function PersonInfoPage({ {person.phone}
- - {copy.detail.labels.department} - - - {formatPersonDepartment( - person.department, - copy.departments, - copy.fallback, - )} - + {copy.detail.labels.team} + {person.team?.name ?? copy.fallback.noTeam}
{person.user ? ( <> diff --git a/src/app/(dashboard)/people/_components/edit.person.form.tsx b/src/app/(dashboard)/people/_components/edit.person.form.tsx index d7594da..e6953fe 100644 --- a/src/app/(dashboard)/people/_components/edit.person.form.tsx +++ b/src/app/(dashboard)/people/_components/edit.person.form.tsx @@ -12,20 +12,16 @@ import { type SubmitButtonCopy, } from "@/components/forms/submitButton" import { UserStatus } from "@/generated/prisma/client" -import { PERSON_DEPARTMENTS } from "@/lib/constants" import { buildUnifiedUpdateSchema, type UnifiedSchemaCopy, type UnifiedUpdateFormType, } from "@/schemas/user.schema" import type { PersonWithUser } from "@/services/person.service" +import type { TeamSummary } from "@/types" import { - formatPersonDepartment, formatUserRole, - type PersonDepartmentCopy, - type PersonFallbackCopy, - type UserFallbackCopy, type UserFormCopy, type UserRoleCopy, } from "./user.copy" @@ -35,19 +31,15 @@ export default function EditPersonForm({ formCopy, schemaCopy, roleLabels, - userFallbackCopy, - departmentCopy, - fallbackCopy, submitButtonCopy, + teams, }: { person: PersonWithUser formCopy: UserFormCopy schemaCopy: UnifiedSchemaCopy roleLabels: UserRoleCopy - userFallbackCopy: UserFallbackCopy - departmentCopy: PersonDepartmentCopy - fallbackCopy: PersonFallbackCopy submitButtonCopy: SubmitButtonCopy + teams: TeamSummary[] }) { const router = useRouter() const schema = useMemo( @@ -68,7 +60,7 @@ export default function EditPersonForm({ id: person.id, firstName: person.firstName, lastName: person.lastName, - department: person.department ?? "OTHER", + teamId: person.teamId ?? null, email: person.email ?? "", phone: person.phone ?? "", ...(hasUser && user @@ -116,12 +108,11 @@ export default function EditPersonForm({ placeholder={formCopy.lastNamePlaceholder} register={register("lastName")} /> - -