31d80cd6ae
continuous-integration/drone/push Build is passing
Add uuid-based section keys and memoized DynamicSection to reduce re-renders, fix dashboard GSAP scoping, update logo sizing, and temporarily hide notice-type and closing-soon widgets. Co-authored-by: Cursor <cursoragent@cursor.com>
193 lines
6.2 KiB
TypeScript
193 lines
6.2 KiB
TypeScript
import { PasswordIcon } from "@/assets/icons";
|
|
import {
|
|
createPhoneFieldRules,
|
|
PhoneNumberInput,
|
|
} from "@/components/FormElements/PhoneNumberInput";
|
|
import type { DynamicFormSection } from "@/components/forms/DynamicFormBuilder";
|
|
import FileImageUploadCard from "@/components/ui/FileImageUploadCard";
|
|
import { FormErrorMessages } from "@/constants/Texts";
|
|
import type { Dispatch, SetStateAction } from "react";
|
|
import { Controller, type UseFormRegisterReturn } from "react-hook-form";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import type { CreateAdminFormValues } from "./createAdminForm.types";
|
|
|
|
const adminSectionId = uuidv4();
|
|
|
|
interface CreateAdminFormSectionsOptions {
|
|
editMode?: boolean;
|
|
passwordFieldInputType: "password" | "text";
|
|
setPasswordFieldInputType: Dispatch<SetStateAction<"password" | "text">>;
|
|
profileImageRegister: UseFormRegisterReturn;
|
|
isUploadingImage: boolean;
|
|
uploadProgress: number;
|
|
previewUrl: string | null;
|
|
selectedFileName: string;
|
|
selectedFileSize: number | null;
|
|
clearSelectedProfileImage: () => void;
|
|
}
|
|
|
|
export const createAdminFormSections = ({
|
|
editMode,
|
|
passwordFieldInputType,
|
|
setPasswordFieldInputType,
|
|
profileImageRegister,
|
|
isUploadingImage,
|
|
uploadProgress,
|
|
previewUrl,
|
|
selectedFileName,
|
|
selectedFileSize,
|
|
clearSelectedProfileImage,
|
|
}: CreateAdminFormSectionsOptions): DynamicFormSection<CreateAdminFormValues>[] => [
|
|
{
|
|
id: adminSectionId,
|
|
title: "Input Fields",
|
|
fields: [
|
|
{
|
|
kind: "input",
|
|
name: "full_name",
|
|
label: "Full name",
|
|
type: "text",
|
|
placeholder: "Enter full name (5-100 characters)",
|
|
required: true,
|
|
dataCy: "admin-form-full-name-input",
|
|
rules: {
|
|
required: { message: FormErrorMessages.required, value: true },
|
|
minLength: { value: 5, message: FormErrorMessages.minLength(5) },
|
|
maxLength: { value: 100, message: FormErrorMessages.maxLength(100) },
|
|
},
|
|
},
|
|
{
|
|
kind: "input",
|
|
name: "username",
|
|
label: "Username",
|
|
type: "text",
|
|
placeholder: "Letters and numbers only (3-30)",
|
|
autoComplete: "off",
|
|
required: true,
|
|
dataCy: "admin-form-username-input",
|
|
rules: {
|
|
required: { value: true, message: FormErrorMessages.required },
|
|
pattern: {
|
|
value: /^[a-zA-Z0-9]+$/,
|
|
message: FormErrorMessages.alphaNumeric,
|
|
},
|
|
minLength: { value: 3, message: FormErrorMessages.minLength(3) },
|
|
maxLength: { value: 30, message: FormErrorMessages.maxLength(30) },
|
|
},
|
|
},
|
|
{
|
|
kind: "input",
|
|
name: "email",
|
|
label: "Email",
|
|
type: "email",
|
|
placeholder: "you@example.com",
|
|
autoComplete: "off",
|
|
required: true,
|
|
dataCy: "admin-form-email-input",
|
|
rules: {
|
|
required: { value: true, message: FormErrorMessages.required },
|
|
pattern: {
|
|
value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
|
message: FormErrorMessages.invalidEmail,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
kind: "input",
|
|
name: "password",
|
|
label: "Password",
|
|
type: passwordFieldInputType,
|
|
placeholder: "At least 8 characters",
|
|
autoComplete: "new-password",
|
|
required: true,
|
|
hidden: editMode,
|
|
dataCy: "admin-form-password-input",
|
|
icon: (
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
setPasswordFieldInputType(
|
|
passwordFieldInputType === "password" ? "text" : "password",
|
|
)
|
|
}
|
|
className="absolute right-4.5 top-1/2 z-10 flex h-6 w-6 -translate-y-1/2 items-center justify-center rounded text-dark-5 transition hover:text-primary dark:text-white/75 [&>svg]:!static [&>svg]:!size-5 [&>svg]:!translate-y-0"
|
|
aria-label={
|
|
passwordFieldInputType === "password"
|
|
? "Show password"
|
|
: "Hide password"
|
|
}
|
|
>
|
|
<PasswordIcon className="pointer-events-none" />
|
|
</button>
|
|
),
|
|
rules: {
|
|
required: { value: true, message: FormErrorMessages.required },
|
|
minLength: { value: 8, message: FormErrorMessages.minLength(8) },
|
|
maxLength: { value: 128, message: FormErrorMessages.maxLength(128) },
|
|
},
|
|
},
|
|
{
|
|
kind: "custom",
|
|
name: "phone",
|
|
render: ({ control, errors }) => (
|
|
<Controller
|
|
name="phone"
|
|
control={control}
|
|
rules={createPhoneFieldRules({ required: false })}
|
|
render={({ field }) => (
|
|
<PhoneNumberInput
|
|
label="Phone"
|
|
value={field.value ?? ""}
|
|
onChange={field.onChange}
|
|
onBlur={field.onBlur}
|
|
error={errors.phone?.message as string | undefined}
|
|
dataCy="admin-form-phone-input"
|
|
/>
|
|
)}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
kind: "input",
|
|
name: "department",
|
|
label: "Department",
|
|
type: "text",
|
|
dataCy: "admin-form-department-input",
|
|
rules: {
|
|
minLength: { value: 2, message: FormErrorMessages.minLength(2) },
|
|
maxLength: { value: 100, message: FormErrorMessages.maxLength(100) },
|
|
},
|
|
},
|
|
{
|
|
kind: "input",
|
|
name: "position",
|
|
label: "Position",
|
|
type: "text",
|
|
dataCy: "admin-form-position-input",
|
|
rules: {
|
|
minLength: { value: 2, message: FormErrorMessages.minLength(2) },
|
|
maxLength: { value: 100, message: FormErrorMessages.maxLength(100) },
|
|
},
|
|
},
|
|
{
|
|
kind: "custom",
|
|
name: "profile_image",
|
|
render: ({ error }) => (
|
|
<FileImageUploadCard
|
|
label="Profile image"
|
|
inputId="profile-image-upload"
|
|
fileRegister={profileImageRegister}
|
|
isLoading={isUploadingImage}
|
|
uploadProgress={uploadProgress}
|
|
previewUrl={previewUrl}
|
|
selectedFileName={selectedFileName}
|
|
selectedFileSize={selectedFileSize}
|
|
onClear={clearSelectedProfileImage}
|
|
errorMessage={error}
|
|
/>
|
|
),
|
|
},
|
|
],
|
|
},
|
|
];
|