feat(ui): add team picker to person forms, list and detail pages
This commit is contained in:
@@ -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 <div>{personCopy.edit.notFound}</div>
|
||||
@@ -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}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -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({
|
||||
<span>{person.phone}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{copy.detail.labels.department}
|
||||
</span>
|
||||
<span>
|
||||
{formatPersonDepartment(
|
||||
person.department,
|
||||
copy.departments,
|
||||
copy.fallback,
|
||||
)}
|
||||
</span>
|
||||
<span className="text-gray-600">{copy.detail.labels.team}</span>
|
||||
<span>{person.team?.name ?? copy.fallback.noTeam}</span>
|
||||
</div>
|
||||
{person.user ? (
|
||||
<>
|
||||
|
||||
@@ -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")}
|
||||
/>
|
||||
<DepartmentSelect
|
||||
error={errors.department?.message}
|
||||
<TeamSelect
|
||||
error={errors.teamId?.message}
|
||||
formCopy={formCopy}
|
||||
departmentCopy={departmentCopy}
|
||||
fallbackCopy={fallbackCopy}
|
||||
register={register("department")}
|
||||
register={register("teamId")}
|
||||
teams={teams}
|
||||
/>
|
||||
<TextInput
|
||||
error={errors.email?.message}
|
||||
@@ -238,33 +229,31 @@ function RoleSelect({
|
||||
)
|
||||
}
|
||||
|
||||
function DepartmentSelect({
|
||||
function TeamSelect({
|
||||
error,
|
||||
formCopy,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
register,
|
||||
teams,
|
||||
}: {
|
||||
error?: string
|
||||
formCopy: UserFormCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
register: UseFormRegisterReturn
|
||||
teams: TeamSummary[]
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="department" className="mb-2 block text-lg">
|
||||
{formCopy.departmentLabel}
|
||||
<label htmlFor="teamId" className="mb-2 block text-lg">
|
||||
{formCopy.teamLabel}
|
||||
</label>
|
||||
<select
|
||||
id="department"
|
||||
id="teamId"
|
||||
{...register}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${error ? "border-error" : ""}`}
|
||||
>
|
||||
<option value="">{formCopy.departmentPlaceholder}</option>
|
||||
{Object.keys(PERSON_DEPARTMENTS).map((department) => (
|
||||
<option key={department} value={department}>
|
||||
{formatPersonDepartment(department, departmentCopy, fallbackCopy)}
|
||||
<option value="">{formCopy.teamPlaceholder}</option>
|
||||
{teams.map((team) => (
|
||||
<option key={team.id} value={team.id}>
|
||||
{team.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@@ -11,35 +11,27 @@ import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import { PERSON_DEPARTMENTS } from "@/lib/constants"
|
||||
import {
|
||||
buildUnifiedCreateSchema,
|
||||
type UnifiedCreateFormType,
|
||||
type UnifiedSchemaCopy,
|
||||
} from "@/schemas/user.schema"
|
||||
import type { TeamSummary } from "@/types"
|
||||
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
type UserFormCopy,
|
||||
type UserRoleCopy,
|
||||
} from "./user.copy"
|
||||
import type { UserFormCopy, UserRoleCopy } from "./user.copy"
|
||||
|
||||
export default function NewUserForm({
|
||||
formCopy,
|
||||
schemaCopy,
|
||||
roleLabels,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
submitButtonCopy,
|
||||
teams,
|
||||
}: {
|
||||
formCopy: UserFormCopy
|
||||
schemaCopy: UnifiedSchemaCopy
|
||||
roleLabels: UserRoleCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
teams: TeamSummary[]
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const schema = useMemo(
|
||||
@@ -101,12 +93,11 @@ export default function NewUserForm({
|
||||
placeholder={formCopy.lastNamePlaceholder}
|
||||
register={register("lastName")}
|
||||
/>
|
||||
<DepartmentSelect
|
||||
error={errors.department?.message}
|
||||
<TeamSelect
|
||||
error={errors.teamId?.message}
|
||||
formCopy={formCopy}
|
||||
departmentCopy={departmentCopy}
|
||||
fallbackCopy={fallbackCopy}
|
||||
register={register("department")}
|
||||
register={register("teamId")}
|
||||
teams={teams}
|
||||
/>
|
||||
<UserTextInput
|
||||
error={errors.email?.message}
|
||||
@@ -210,33 +201,31 @@ function RoleSelect({
|
||||
)
|
||||
}
|
||||
|
||||
function DepartmentSelect({
|
||||
function TeamSelect({
|
||||
error,
|
||||
formCopy,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
register,
|
||||
teams,
|
||||
}: {
|
||||
error?: string
|
||||
formCopy: UserFormCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
register: UseFormRegisterReturn
|
||||
teams: TeamSummary[]
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="department" className="mb-2 block text-lg">
|
||||
{formCopy.departmentLabel}
|
||||
<label htmlFor="teamId" className="mb-2 block text-lg">
|
||||
{formCopy.teamLabel}
|
||||
</label>
|
||||
<select
|
||||
id="department"
|
||||
id="teamId"
|
||||
{...register}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${error ? "border-error" : ""}`}
|
||||
>
|
||||
<option value="">{formCopy.departmentPlaceholder}</option>
|
||||
{Object.keys(PERSON_DEPARTMENTS).map((department) => (
|
||||
<option key={department} value={department}>
|
||||
{formatPersonDepartment(department, departmentCopy, fallbackCopy)}
|
||||
<option value="">{formCopy.teamPlaceholder}</option>
|
||||
{teams.map((team) => (
|
||||
<option key={team.id} value={team.id}>
|
||||
{team.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@@ -3,20 +3,4 @@ import type { Dictionary } from "@/i18n/dictionaries"
|
||||
export type PersonListCopy = Dictionary["inventory"]["people"]["list"]
|
||||
export type PersonDetailCopy = Dictionary["inventory"]["people"]["detail"]
|
||||
export type PersonFormCopy = Dictionary["inventory"]["people"]["form"]
|
||||
export type PersonDepartmentCopy =
|
||||
Dictionary["inventory"]["people"]["departments"]
|
||||
export type PersonFallbackCopy = Dictionary["inventory"]["people"]["fallback"]
|
||||
|
||||
export function formatPersonDepartment(
|
||||
department: string | null | undefined,
|
||||
departmentCopy: PersonDepartmentCopy,
|
||||
fallbackCopy: PersonFallbackCopy,
|
||||
) {
|
||||
if (!department) {
|
||||
return fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
return department in departmentCopy
|
||||
? departmentCopy[department as keyof PersonDepartmentCopy]
|
||||
: fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ export type UserStatusCopy = Dictionary["admin"]["users"]["status"]
|
||||
export type UserFallbackCopy = Dictionary["admin"]["users"]["fallback"]
|
||||
export type UserResetPasswordCopy =
|
||||
Dictionary["admin"]["users"]["resetPassword"]
|
||||
export type PersonDepartmentCopy =
|
||||
Dictionary["inventory"]["people"]["departments"]
|
||||
export type PersonFallbackCopy = Dictionary["inventory"]["people"]["fallback"]
|
||||
|
||||
export function formatUserRole(
|
||||
@@ -19,17 +17,3 @@ export function formatUserRole(
|
||||
? roleCopy[role as keyof UserRoleCopy]
|
||||
: fallbackCopy.unknownRole
|
||||
}
|
||||
|
||||
export function formatPersonDepartment(
|
||||
department: string | null | undefined,
|
||||
departmentCopy: PersonDepartmentCopy,
|
||||
fallbackCopy: PersonFallbackCopy,
|
||||
): string {
|
||||
if (!department) {
|
||||
return fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
return department in departmentCopy
|
||||
? departmentCopy[department as keyof PersonDepartmentCopy]
|
||||
: fallbackCopy.unknownDepartment
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { listTeamsUseCase } from "@/use-cases/team.use-cases"
|
||||
|
||||
import NewPersonForm from "../_components/new.person.form"
|
||||
|
||||
export default async function NewUserPage() {
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.admin.users
|
||||
const teams = await listTeamsUseCase()
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -15,9 +17,8 @@ export default async function NewUserPage() {
|
||||
formCopy={copy.form}
|
||||
schemaCopy={{ ...copy.schema, ...dictionary.inventory.people.schema }}
|
||||
roleLabels={copy.roles}
|
||||
departmentCopy={dictionary.inventory.people.departments}
|
||||
fallbackCopy={dictionary.inventory.people.fallback}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
teams={teams}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -8,11 +8,6 @@ import { UserStatus } from "@/generated/prisma/client"
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
} from "./_components/person.copy"
|
||||
import TeamsTab from "./_components/teams.tab"
|
||||
import {
|
||||
formatUserRole,
|
||||
@@ -55,8 +50,7 @@ export default async function PeoplePage(props: {
|
||||
const userStatusCopy = userCopy.status
|
||||
const userRoleLabels = userCopy.roles as UserRoleCopy
|
||||
const userFallbackCopy = userCopy.fallback as UserFallbackCopy
|
||||
const departmentCopy = copy.departments as PersonDepartmentCopy
|
||||
const personFallbackCopy = copy.fallback as PersonFallbackCopy
|
||||
const personFallbackCopy = copy.fallback
|
||||
|
||||
const peopleList = (
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -83,7 +77,7 @@ export default async function PeoplePage(props: {
|
||||
{copy.list.columns.phone}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.department}
|
||||
{copy.list.columns.team}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.role}
|
||||
@@ -105,11 +99,7 @@ export default async function PeoplePage(props: {
|
||||
<td className="p-4">{person.email}</td>
|
||||
<td className="p-4">{person.phone}</td>
|
||||
<td className="p-4">
|
||||
{formatPersonDepartment(
|
||||
person.department,
|
||||
departmentCopy,
|
||||
personFallbackCopy,
|
||||
)}
|
||||
{person.team?.name ?? personFallbackCopy.noTeam}
|
||||
</td>
|
||||
<td className="p-4">
|
||||
{person.user
|
||||
|
||||
Reference in New Issue
Block a user