refactor: consolidate admin/users management under /people
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import PersonForm from "../../_components/person.form"
|
||||
import EditPersonForm from "../../_components/edit.person.form"
|
||||
|
||||
export default async function PersonEditPage({
|
||||
params,
|
||||
@@ -10,25 +10,27 @@ export default async function PersonEditPage({
|
||||
}) {
|
||||
const { personId } = await params
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.people
|
||||
const person = await PersonService.findById(personId)
|
||||
const personCopy = dictionary.inventory.people
|
||||
const userCopy = dictionary.admin.users
|
||||
const person = await PersonService.findByIdWithUser(personId)
|
||||
|
||||
if (!person) {
|
||||
return <div>{copy.edit.notFound}</div>
|
||||
return <div>{personCopy.edit.notFound}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="text-2xl font-bold">{copy.edit.title}</h1>
|
||||
<h1 className="text-2xl font-bold">{personCopy.edit.title}</h1>
|
||||
</div>
|
||||
<PersonForm
|
||||
initialData={person}
|
||||
mode="edit"
|
||||
formCopy={copy.form}
|
||||
schemaCopy={copy.schema}
|
||||
departmentCopy={copy.departments}
|
||||
fallbackCopy={copy.fallback}
|
||||
<EditPersonForm
|
||||
person={person}
|
||||
formCopy={userCopy.form}
|
||||
schemaCopy={{ ...userCopy.schema, ...personCopy.schema }}
|
||||
roleLabels={userCopy.roles}
|
||||
userFallbackCopy={userCopy.fallback}
|
||||
departmentCopy={personCopy.departments}
|
||||
fallbackCopy={personCopy.fallback}
|
||||
submitButtonCopy={dictionary.common.submitButton}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,11 @@ import { AssignmentService } from "@/services/assignment.service"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import { formatPersonDepartment } from "../_components/person.copy"
|
||||
import {
|
||||
formatUserRole,
|
||||
type UserFallbackCopy,
|
||||
type UserRoleCopy,
|
||||
} from "../_components/user.copy"
|
||||
|
||||
export default async function PersonInfoPage({
|
||||
params,
|
||||
@@ -14,7 +19,8 @@ export default async function PersonInfoPage({
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.people
|
||||
const assignmentCopy = dictionary.inventory.assignments
|
||||
const person = await PersonService.findById(personId)
|
||||
const userCopy = dictionary.admin.users
|
||||
const person = await PersonService.findByIdWithUser(personId)
|
||||
const assignments = await AssignmentService.findAllByPerson(personId)
|
||||
|
||||
if (!person) {
|
||||
@@ -49,6 +55,37 @@ export default async function PersonInfoPage({
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{person.user ? (
|
||||
<>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{copy.detail.labels.role}
|
||||
</span>
|
||||
<span>
|
||||
{formatUserRole(
|
||||
person.user.role,
|
||||
userCopy.roles as UserRoleCopy,
|
||||
userCopy.fallback as UserFallbackCopy,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600">
|
||||
{copy.detail.labels.status}
|
||||
</span>
|
||||
<span>
|
||||
{person.user.isActive
|
||||
? userCopy.status.active
|
||||
: userCopy.status.inactive}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="col-span-2 flex justify-between">
|
||||
<span className="text-gray-600">{copy.detail.labels.role}</span>
|
||||
<span>{copy.detail.labels.noUser}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useMemo } from "react"
|
||||
import type { UseFormRegisterReturn } from "react-hook-form"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import { updatePersonUserAction } from "@/actions/person.actions"
|
||||
import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import { PERSON_DEPARTMENTS } from "@/lib/constants"
|
||||
import {
|
||||
buildUnifiedUpdateSchema,
|
||||
type UnifiedSchemaCopy,
|
||||
type UnifiedUpdateFormType,
|
||||
} from "@/schemas/user.schema"
|
||||
import type { PersonWithUser } from "@/services/person.service"
|
||||
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
formatUserRole,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
type UserFallbackCopy,
|
||||
type UserFormCopy,
|
||||
type UserRoleCopy,
|
||||
} from "./user.copy"
|
||||
|
||||
export default function EditPersonForm({
|
||||
person,
|
||||
formCopy,
|
||||
schemaCopy,
|
||||
roleLabels,
|
||||
userFallbackCopy,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
submitButtonCopy,
|
||||
}: {
|
||||
person: PersonWithUser
|
||||
formCopy: UserFormCopy
|
||||
schemaCopy: UnifiedSchemaCopy
|
||||
roleLabels: UserRoleCopy
|
||||
userFallbackCopy: UserFallbackCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}) {
|
||||
const router = useRouter()
|
||||
const schema = useMemo(
|
||||
() => buildUnifiedUpdateSchema(schemaCopy),
|
||||
[schemaCopy],
|
||||
)
|
||||
const hasUser = Boolean(person.userId && person.user)
|
||||
const user = person.user
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setError,
|
||||
formState: { errors, isSubmitting, isSubmitSuccessful },
|
||||
} = useForm<UnifiedUpdateFormType>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
id: person.id,
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
department: person.department ?? "OTHER",
|
||||
email: person.email ?? "",
|
||||
phone: person.phone ?? "",
|
||||
...(hasUser && user ? { role: user.role, isActive: user.isActive } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = async (formData: UnifiedUpdateFormType) => {
|
||||
const response = await updatePersonUserAction(formData)
|
||||
|
||||
if (response?.errors) {
|
||||
Object.entries(response.errors).forEach(([fieldName, messages]) => {
|
||||
messages.forEach((message: string) => {
|
||||
setError(fieldName as keyof UnifiedUpdateFormType, {
|
||||
type: "server",
|
||||
message,
|
||||
})
|
||||
toast.error(message)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (response?.success) {
|
||||
toast.success(response.message)
|
||||
router.push("/people")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<input type="hidden" {...register("id")} />
|
||||
<TextInput
|
||||
error={errors.firstName?.message}
|
||||
id="firstName"
|
||||
label={formCopy.firstNameLabel}
|
||||
placeholder={formCopy.firstNamePlaceholder}
|
||||
register={register("firstName")}
|
||||
/>
|
||||
<TextInput
|
||||
error={errors.lastName?.message}
|
||||
id="lastName"
|
||||
label={formCopy.lastNameLabel}
|
||||
placeholder={formCopy.lastNamePlaceholder}
|
||||
register={register("lastName")}
|
||||
/>
|
||||
<DepartmentSelect
|
||||
error={errors.department?.message}
|
||||
formCopy={formCopy}
|
||||
departmentCopy={departmentCopy}
|
||||
fallbackCopy={fallbackCopy}
|
||||
register={register("department")}
|
||||
/>
|
||||
<TextInput
|
||||
error={errors.email?.message}
|
||||
id="email"
|
||||
label={formCopy.emailLabel}
|
||||
placeholder={formCopy.emailPlaceholder}
|
||||
register={register("email")}
|
||||
type="email"
|
||||
/>
|
||||
<TextInput
|
||||
error={errors.phone?.message}
|
||||
id="phone"
|
||||
label={formCopy.phoneLabel}
|
||||
placeholder={formCopy.phonePlaceholder}
|
||||
register={register("phone")}
|
||||
/>
|
||||
|
||||
{hasUser && (
|
||||
<section
|
||||
className="flex flex-col gap-4 border-t pt-4"
|
||||
aria-labelledby="user-account-heading"
|
||||
>
|
||||
<h2 id="user-account-heading" className="text-xl font-semibold">
|
||||
{formCopy.userAccountHeading}
|
||||
</h2>
|
||||
<RoleSelect
|
||||
register={register("role")}
|
||||
roleLabel={formCopy.roleLabel}
|
||||
roleLabels={roleLabels}
|
||||
/>
|
||||
<label className="flex items-center gap-2">
|
||||
<input type="checkbox" {...register("isActive")} />
|
||||
{formCopy.activeLabel}
|
||||
</label>
|
||||
<TextInput
|
||||
error={errors.password?.message}
|
||||
id="password"
|
||||
label={formCopy.newPasswordLabel}
|
||||
placeholder={formCopy.newPasswordPlaceholder}
|
||||
register={register("password")}
|
||||
type="password"
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<SubmitButton
|
||||
copy={submitButtonCopy}
|
||||
isSubmitting={isSubmitting}
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
>
|
||||
{formCopy.updateSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
function TextInput({
|
||||
error,
|
||||
id,
|
||||
label,
|
||||
placeholder,
|
||||
register,
|
||||
type = "text",
|
||||
}: {
|
||||
error?: string
|
||||
id: string
|
||||
label: string
|
||||
placeholder: string
|
||||
register: UseFormRegisterReturn
|
||||
type?: string
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor={id} className="mb-2 block text-lg">
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
type={type}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
{...register}
|
||||
className={`w-full rounded-lg border px-4 py-2 ${error ? "border-error" : ""}`}
|
||||
/>
|
||||
{error && <p className="text-error">{error}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function RoleSelect({
|
||||
register,
|
||||
roleLabel,
|
||||
roleLabels,
|
||||
}: {
|
||||
register: UseFormRegisterReturn
|
||||
roleLabel: string
|
||||
roleLabels: UserRoleCopy
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="role" className="mb-2 block text-lg">
|
||||
{roleLabel}
|
||||
</label>
|
||||
<select
|
||||
id="role"
|
||||
{...register}
|
||||
className="w-full rounded-lg border px-4 py-2"
|
||||
>
|
||||
<option value="ADMIN">{roleLabels.ADMIN}</option>
|
||||
<option value="MANAGER">{roleLabels.MANAGER}</option>
|
||||
<option value="STAFF">{roleLabels.STAFF}</option>
|
||||
<option value="VIEWER">{roleLabels.VIEWER}</option>
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DepartmentSelect({
|
||||
error,
|
||||
formCopy,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
register,
|
||||
}: {
|
||||
error?: string
|
||||
formCopy: UserFormCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
register: UseFormRegisterReturn
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<label htmlFor="department" className="mb-2 block text-lg">
|
||||
{formCopy.departmentLabel}
|
||||
</label>
|
||||
<select
|
||||
id="department"
|
||||
{...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>
|
||||
))}
|
||||
</select>
|
||||
{error && <p className="text-error">{error}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Re-export for tests that need to verify the data shape passed to this form.
|
||||
export { formatUserRole }
|
||||
@@ -1,188 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useRouter } from "next/navigation"
|
||||
import { useMemo } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { toast } from "sonner"
|
||||
import { createNewPerson, updatePerson } from "@/actions/person.actions"
|
||||
import {
|
||||
SubmitButton,
|
||||
type SubmitButtonCopy,
|
||||
} from "@/components/forms/submitButton"
|
||||
import { PERSON_DEPARTMENTS } from "@/lib/constants"
|
||||
import {
|
||||
buildCreatePersonSchema,
|
||||
buildUpdatePersonSchema,
|
||||
type CreatePersonFormType,
|
||||
type PersonSchemaCopy,
|
||||
type UpdatePersonFormType,
|
||||
} from "@/schemas/person.schema"
|
||||
import type { Person } from "@/types"
|
||||
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
type PersonFormCopy,
|
||||
} from "./person.copy"
|
||||
|
||||
interface PersonFormProps {
|
||||
initialData?: Person
|
||||
mode?: "create" | "edit"
|
||||
formCopy: PersonFormCopy
|
||||
schemaCopy: PersonSchemaCopy
|
||||
departmentCopy: PersonDepartmentCopy
|
||||
fallbackCopy: PersonFallbackCopy
|
||||
submitButtonCopy: SubmitButtonCopy
|
||||
}
|
||||
|
||||
export default function PersonForm({
|
||||
initialData,
|
||||
mode = "create",
|
||||
formCopy,
|
||||
schemaCopy,
|
||||
departmentCopy,
|
||||
fallbackCopy,
|
||||
submitButtonCopy,
|
||||
}: PersonFormProps) {
|
||||
const router = useRouter()
|
||||
const schema = useMemo(
|
||||
() =>
|
||||
mode === "create"
|
||||
? buildCreatePersonSchema(schemaCopy)
|
||||
: buildUpdatePersonSchema(schemaCopy),
|
||||
[mode, schemaCopy],
|
||||
)
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setError,
|
||||
formState: { errors, isSubmitting, isSubmitSuccessful },
|
||||
} = useForm<CreatePersonFormType>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
id: initialData?.id || "",
|
||||
firstName: initialData?.firstName || "",
|
||||
lastName: initialData?.lastName || "",
|
||||
department: initialData?.department || "OTHER",
|
||||
email: initialData?.email || "",
|
||||
phone: initialData?.phone || "",
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = async (formData: CreatePersonFormType) => {
|
||||
const response =
|
||||
mode === "create"
|
||||
? await createNewPerson(formData)
|
||||
: await updatePerson(formData as UpdatePersonFormType)
|
||||
|
||||
if (response?.errors) {
|
||||
Object.entries(response.errors).forEach(([fieldName, messages]) => {
|
||||
messages.forEach((msg: string) => {
|
||||
setError(fieldName as keyof CreatePersonFormType, {
|
||||
type: "server",
|
||||
message: msg,
|
||||
})
|
||||
toast.error(msg)
|
||||
})
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (response?.success) {
|
||||
toast.success(response.message)
|
||||
router.push("/people")
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<input type="hidden" {...register("id")} />
|
||||
<div>
|
||||
<label htmlFor="firstName" className="mb-2 block text-lg">
|
||||
{formCopy.firstNameLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="firstName"
|
||||
placeholder={formCopy.firstNamePlaceholder}
|
||||
{...register("firstName")}
|
||||
className={`w-full rounded-lg border px-4 py-2`}
|
||||
/>
|
||||
{errors?.firstName && (
|
||||
<p className="text-error">{errors.firstName.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="lastName" className="mb-2 block text-lg">
|
||||
{formCopy.lastNameLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="lastName"
|
||||
placeholder={formCopy.lastNamePlaceholder}
|
||||
{...register("lastName")}
|
||||
className={`w-full rounded-lg border px-4 py-2`}
|
||||
/>
|
||||
{errors?.lastName && (
|
||||
<p className="text-error">{errors.lastName.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="department" className="mb-2 block text-lg">
|
||||
{formCopy.departmentLabel}
|
||||
</label>
|
||||
<select
|
||||
id="department"
|
||||
{...register("department")}
|
||||
className="w-full rounded-lg border px-4 py-2"
|
||||
>
|
||||
<option value="">{formCopy.departmentPlaceholder}</option>
|
||||
{Object.keys(PERSON_DEPARTMENTS).map((department) => (
|
||||
<option key={department} value={department}>
|
||||
{formatPersonDepartment(department, departmentCopy, fallbackCopy)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors?.department && (
|
||||
<p className="text-error">{errors.department.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="email" className="mb-2 block text-lg">
|
||||
{formCopy.emailLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="email"
|
||||
placeholder={formCopy.emailPlaceholder}
|
||||
{...register("email")}
|
||||
className={`w-full rounded-lg border px-4 py-2`}
|
||||
/>
|
||||
{errors?.email && <p className="text-error">{errors.email.message}</p>}
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="phone" className="mb-2 block text-lg">
|
||||
{formCopy.phoneLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="phone"
|
||||
placeholder={formCopy.phonePlaceholder}
|
||||
{...register("phone")}
|
||||
className={`w-full rounded-lg border px-4 py-2`}
|
||||
/>
|
||||
{errors?.phone && <p className="text-error">{errors.phone.message}</p>}
|
||||
</div>
|
||||
<SubmitButton
|
||||
copy={submitButtonCopy}
|
||||
isSubmitting={isSubmitting}
|
||||
isSubmitSuccessful={isSubmitSuccessful}
|
||||
>
|
||||
{mode === "create" ? formCopy.createSubmit : formCopy.updateSubmit}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -4,11 +4,19 @@ import Link from "next/link"
|
||||
import PageHeader from "@/components/common/pageheader"
|
||||
import PaginationButtons from "@/components/common/pagination"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import type { Person } from "@/generated/prisma/client"
|
||||
import { getI18n } from "@/i18n/server"
|
||||
import { PersonService } from "@/services/person.service"
|
||||
|
||||
import { formatPersonDepartment } from "./_components/person.copy"
|
||||
import {
|
||||
formatPersonDepartment,
|
||||
type PersonDepartmentCopy,
|
||||
type PersonFallbackCopy,
|
||||
} from "./_components/person.copy"
|
||||
import {
|
||||
formatUserRole,
|
||||
type UserFallbackCopy,
|
||||
type UserRoleCopy,
|
||||
} from "./_components/user.copy"
|
||||
|
||||
export default async function PeoplePage(props: {
|
||||
searchParams?: Promise<{
|
||||
@@ -26,6 +34,12 @@ export default async function PeoplePage(props: {
|
||||
})
|
||||
const { dictionary } = await getI18n()
|
||||
const copy = dictionary.inventory.people
|
||||
const userCopy = dictionary.admin.users
|
||||
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
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
@@ -54,13 +68,19 @@ export default async function PeoplePage(props: {
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.department}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.role}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.status}
|
||||
</th>
|
||||
<th scope="col" className="p-4">
|
||||
{copy.list.columns.actions}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{people.map((person: Person) => (
|
||||
{people.map((person) => (
|
||||
<tr key={person.id} className="border-b">
|
||||
<td className="p-4">
|
||||
{`${person.firstName} ${person.lastName}`}
|
||||
@@ -70,10 +90,26 @@ export default async function PeoplePage(props: {
|
||||
<td className="p-4">
|
||||
{formatPersonDepartment(
|
||||
person.department,
|
||||
copy.departments,
|
||||
copy.fallback,
|
||||
departmentCopy,
|
||||
personFallbackCopy,
|
||||
)}
|
||||
</td>
|
||||
<td className="p-4">
|
||||
{person.user
|
||||
? formatUserRole(
|
||||
person.user.role,
|
||||
userRoleLabels,
|
||||
userFallbackCopy,
|
||||
)
|
||||
: "—"}
|
||||
</td>
|
||||
<td className="p-4">
|
||||
{person.user
|
||||
? person.user.isActive
|
||||
? userStatusCopy.active
|
||||
: userStatusCopy.inactive
|
||||
: "—"}
|
||||
</td>
|
||||
<td className="flex items-center gap-2 p-4">
|
||||
<Link href={`/people/${person.id}`} passHref>
|
||||
<Button
|
||||
@@ -100,7 +136,7 @@ export default async function PeoplePage(props: {
|
||||
</tbody>
|
||||
<tfoot className="border-t">
|
||||
<tr>
|
||||
<td colSpan={5} className="p-4 text-center text-sm">
|
||||
<td colSpan={7} className="p-4 text-center text-sm">
|
||||
<PaginationButtons totalPages={totalPages} />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user