mediahub-fe/components/auth/identity-form.tsx

226 lines
7.8 KiB
TypeScript
Raw Normal View History

"use client";
import React, { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useTranslations } from "next-intl";
import { FormField } from "@/components/auth/form-field";
import {
IdentityFormProps,
JournalistRegistrationData,
PersonnelRegistrationData,
GeneralRegistrationData,
UserCategory,
journalistRegistrationSchema,
personnelRegistrationSchema,
generalRegistrationSchema
} from "@/types/registration";
import { useUserDataValidation } from "@/hooks/use-registration";
import { ASSOCIATIONS } from "@/lib/registration-utils";
export const IdentityForm: React.FC<IdentityFormProps> = ({
category,
onSuccess,
onError,
className,
}) => {
const t = useTranslations("LandingPage");
const { validateJournalistData, validatePersonnelData, loading: validationLoading } = useUserDataValidation();
const [memberIdentity, setMemberIdentity] = useState("");
const [memberIdentityError, setMemberIdentityError] = useState("");
// Determine which schema to use based on category
const getSchema = () => {
switch (category) {
case "6":
return journalistRegistrationSchema;
case "7":
return personnelRegistrationSchema;
default:
return generalRegistrationSchema;
}
};
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
setValue,
watch,
} = useForm<JournalistRegistrationData | PersonnelRegistrationData | GeneralRegistrationData>({
resolver: zodResolver(getSchema()),
mode: "onChange",
});
const watchedEmail = watch("email");
const handleMemberIdentityChange = async (value: string) => {
setMemberIdentity(value);
setMemberIdentityError("");
if (!value.trim()) {
return;
}
try {
if (category === "6") {
await validateJournalistData(value);
setValue("journalistCertificate" as keyof JournalistRegistrationData, value);
} else if (category === "7") {
await validatePersonnelData(value);
setValue("policeNumber" as keyof PersonnelRegistrationData, value);
}
} catch (error: any) {
setMemberIdentityError(error.message || "Invalid identity number");
}
};
const onSubmit = async (data: JournalistRegistrationData | PersonnelRegistrationData | GeneralRegistrationData) => {
try {
// Additional validation for member identity
if ((category === "6" || category === "7") && !memberIdentity.trim()) {
setMemberIdentityError("Identity number is required");
return;
}
if (memberIdentityError) {
onError?.(memberIdentityError);
return;
}
onSuccess?.(data);
} catch (error: any) {
onError?.(error.message || "Form submission failed");
}
};
const renderJournalistFields = () => (
<>
<div className="flex flex-col px-0 lg:px-8 mb-4">
<Label htmlFor="association" className="mb-2">
{t("member", { defaultValue: "Member" })} <span className="text-red-500">*</span>
</Label>
<select
className={`py-2 px-3 rounded-md border text-sm border-slate-300 bg-white dark:bg-slate-600 ${
errors.association ? "border-red-500" : ""
}`}
{...register("association" as keyof JournalistRegistrationData)}
id="association"
>
<option value="" disabled>
{t("association", { defaultValue: "Select Association" })}
</option>
{ASSOCIATIONS.map((association) => (
<option key={association.id} value={association.value}>
{association.name}
</option>
))}
</select>
{errors.association && (
<div className="text-red-500 text-sm mt-1">{errors.association.message}</div>
)}
</div>
<div className="px-0 lg:px-[34px] mb-4">
<Label htmlFor="journalistCertificate">
{t("journalistNumber", { defaultValue: "Journalist Certificate Number" })} <span className="text-red-500">*</span>
</Label>
<Input
className={`mt-2 ${memberIdentityError ? "border-red-500" : ""}`}
autoComplete="off"
placeholder={t("inputJournalist", { defaultValue: "Enter journalist certificate number" })}
type="text"
value={memberIdentity}
onChange={(e) => handleMemberIdentityChange(e.target.value)}
/>
{memberIdentityError && (
<p className="text-red-500 text-sm mt-1">{memberIdentityError}</p>
)}
</div>
</>
);
const renderPersonnelFields = () => (
<div className="px-0 lg:px-[34px] mb-4">
<Label htmlFor="policeNumber">
<b>{t("policeNumber", { defaultValue: "Police Number" })}</b> <span className="text-red-500">*</span>
</Label>
<Input
className={`mt-2 ${memberIdentityError ? "border-red-500" : ""}`}
autoComplete="off"
placeholder="Enter your Police Registration Number"
type="text"
value={memberIdentity}
onChange={(e) => handleMemberIdentityChange(e.target.value)}
/>
{memberIdentityError && (
<p className="text-red-500 text-sm mt-1">{memberIdentityError}</p>
)}
</div>
);
return (
<div className={className}>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="flex flex-col w-full px-8 lg:px-12 gap-4">
{/* Category-specific fields */}
{category === "6" && renderJournalistFields()}
{category === "7" && renderPersonnelFields()}
{/* Email field - common for all categories */}
<div className="flex flex-col w-full px-0 lg:px-8 gap-2">
<Label htmlFor="email">
<b>Email</b> <span className="text-red-500">*</span>
</Label>
<Input
className={`w-full ${errors.email ? "border-red-500" : ""}`}
autoComplete="off"
placeholder={t("inputEmail", { defaultValue: "Enter your email" })}
type="email"
{...register("email")}
/>
{errors.email && (
<p className="text-sm text-red-500 mt-1">{errors.email.message}</p>
)}
</div>
{/* Terms and conditions */}
<div className="text-center mb-2 px-[34px]">
<p className="text-sm lg:text-base">
{t("byRegis", { defaultValue: "By registering, you agree to our" })} <br />{" "}
<a href="/privacy" target="_blank" className="text-red-500">
<b>{t("terms", { defaultValue: "Terms of Service" })}</b>
</a>{" "}
{t("and", { defaultValue: "and" })}{" "}
<a href="/privacy" target="_blank" className="text-red-500">
<b>{t("privacy", { defaultValue: "Privacy Policy" })}</b>
</a>
</p>
</div>
{/* Submit button */}
<div className="mb-5 mt-7 px-[34px] w-full text-center flex justify-center">
<Button
type="submit"
disabled={isSubmitting || validationLoading}
className="border cursor-pointer border-red-500 px-4 py-3 rounded-lg text-white bg-[#dc3545] w-[550px] hover:bg-red-600 transition-colors"
>
{isSubmitting || validationLoading ? (
<div className="flex items-center gap-2">
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
Processing...
</div>
) : (
`${t("send", { defaultValue: "Send" })} OTP`
)}
</Button>
</div>
</div>
</form>
</div>
);
};