fixing
This commit is contained in:
parent
2e75d679b3
commit
00ddca22b9
|
|
@ -9,16 +9,17 @@ import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Icon } from "@/components/ui/icon";
|
import { Icon } from "@/components/ui/icon";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { FormField } from "@/components/auth/form-field";
|
import {
|
||||||
import {
|
|
||||||
ProfileFormProps,
|
ProfileFormProps,
|
||||||
RegistrationFormData,
|
RegistrationFormData,
|
||||||
InstituteData,
|
InstituteData,
|
||||||
UserCategory,
|
registrationSchema,
|
||||||
registrationSchema
|
|
||||||
} from "@/types/registration";
|
} from "@/types/registration";
|
||||||
import { useLocationData, useInstituteData, useRegistration } from "@/hooks/use-registration";
|
import {
|
||||||
import { validatePassword } from "@/lib/registration-utils";
|
useLocationData,
|
||||||
|
useInstituteData,
|
||||||
|
useRegistration,
|
||||||
|
} from "@/hooks/use-registration";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
const PasswordChecklist = dynamic(() => import("react-password-checklist"), {
|
||||||
|
|
@ -34,9 +35,20 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const t = useTranslations("LandingPage");
|
const t = useTranslations("LandingPage");
|
||||||
const { submitRegistration, loading: submitLoading } = useRegistration();
|
const { submitRegistration, loading: submitLoading } = useRegistration();
|
||||||
const { provinces, cities, districts, fetchCities, fetchDistricts, loading: locationLoading } = useLocationData();
|
const {
|
||||||
const { institutes, saveInstitute, loading: instituteLoading } = useInstituteData(Number(category));
|
provinces,
|
||||||
|
cities,
|
||||||
|
districts,
|
||||||
|
fetchCities,
|
||||||
|
fetchDistricts,
|
||||||
|
loading: locationLoading,
|
||||||
|
} = useLocationData();
|
||||||
|
const {
|
||||||
|
institutes,
|
||||||
|
saveInstitute,
|
||||||
|
loading: instituteLoading,
|
||||||
|
} = useInstituteData(Number(category));
|
||||||
|
|
||||||
const [selectedProvince, setSelectedProvince] = useState("");
|
const [selectedProvince, setSelectedProvince] = useState("");
|
||||||
const [selectedCity, setSelectedCity] = useState("");
|
const [selectedCity, setSelectedCity] = useState("");
|
||||||
const [selectedDistrict, setSelectedDistrict] = useState("");
|
const [selectedDistrict, setSelectedDistrict] = useState("");
|
||||||
|
|
@ -58,6 +70,7 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
} = useForm<RegistrationFormData>({
|
} = useForm<RegistrationFormData>({
|
||||||
resolver: zodResolver(registrationSchema),
|
resolver: zodResolver(registrationSchema),
|
||||||
mode: "onChange",
|
mode: "onChange",
|
||||||
|
defaultValues: { email: userData, },
|
||||||
});
|
});
|
||||||
|
|
||||||
const watchedPassword = watch("password");
|
const watchedPassword = watch("password");
|
||||||
|
|
@ -132,8 +145,13 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
instituteId = Number(selectedInstitute);
|
instituteId = Number(selectedInstitute);
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = await submitRegistration(data, category, userData, instituteId);
|
const success = await submitRegistration(
|
||||||
|
data,
|
||||||
|
category,
|
||||||
|
userData,
|
||||||
|
instituteId
|
||||||
|
);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
onSuccess?.(data);
|
onSuccess?.(data);
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +167,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
<div className="flex flex-col gap-3 px-0 lg:px-[34px] mb-4">
|
<div className="flex flex-col gap-3 px-0 lg:px-[34px] mb-4">
|
||||||
<div className="flex flex-col mb-2">
|
<div className="flex flex-col mb-2">
|
||||||
<Label htmlFor="institute" className="mb-2">
|
<Label htmlFor="institute" className="mb-2">
|
||||||
{t("institutions", { defaultValue: "Institution" })} <span className="text-red-500">*</span>
|
{t("institutions", { defaultValue: "Institution" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<select
|
||||||
className="mb-3 p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer"
|
className="mb-3 p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer"
|
||||||
|
|
@ -175,7 +194,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="customInstituteName" className="mb-2">
|
<Label htmlFor="customInstituteName" className="mb-2">
|
||||||
{t("instName", { defaultValue: "Institution Name" })} <span className="text-red-500">*</span>
|
{t("instName", { defaultValue: "Institution Name" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
className="mb-3"
|
className="mb-3"
|
||||||
|
|
@ -188,11 +208,14 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="instituteAddress" className="mb-2">
|
<Label htmlFor="instituteAddress" className="mb-2">
|
||||||
{t("instAddress", { defaultValue: "Institution Address" })} <span className="text-red-500">*</span>
|
{t("instAddress", { defaultValue: "Institution Address" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="mb-3"
|
className="mb-3"
|
||||||
placeholder={t("addressInst", { defaultValue: "Enter institution address" })}
|
placeholder={t("addressInst", {
|
||||||
|
defaultValue: "Enter institution address",
|
||||||
|
})}
|
||||||
rows={3}
|
rows={3}
|
||||||
value={instituteAddress}
|
value={instituteAddress}
|
||||||
onChange={(e) => setInstituteAddress(e.target.value)}
|
onChange={(e) => setInstituteAddress(e.target.value)}
|
||||||
|
|
@ -212,15 +235,23 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
{(category === "6" || category === "7") && (
|
{(category === "6" || category === "7") && (
|
||||||
<div className="px-0 lg:px-[34px] mb-4">
|
<div className="px-0 lg:px-[34px] mb-4">
|
||||||
<Label className="mb-2">
|
<Label className="mb-2">
|
||||||
{category === "6" ? t("journalistNumber", { defaultValue: "Journalist Number" }) : "NRP"}
|
{category === "6"
|
||||||
|
? t("journalistNumber", { defaultValue: "Journalist Number" })
|
||||||
|
: "NRP"}
|
||||||
<span className="text-red-500">*</span>
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className="mb-3"
|
className="mb-3"
|
||||||
placeholder={t("inputNumberIdentity", { defaultValue: "Enter identity number" })}
|
placeholder={t("inputNumberIdentity", {
|
||||||
value={userData?.journalistCertificate || userData?.policeNumber || ""}
|
defaultValue: "Enter identity number",
|
||||||
|
})}
|
||||||
|
value={
|
||||||
|
userData?.journalistCertificate ||
|
||||||
|
userData?.policeNumber ||
|
||||||
|
""
|
||||||
|
}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,17 +260,22 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
{/* Personal Information */}
|
{/* Personal Information */}
|
||||||
<div className="mb-4 px-0 lg:px-[34px]">
|
<div className="mb-4 px-0 lg:px-[34px]">
|
||||||
<Label className="mb-2">
|
<Label className="mb-2">
|
||||||
{t("fullName", { defaultValue: "Full Name" })} <span className="text-red-500">*</span>
|
{t("fullName", { defaultValue: "Full Name" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={errors.firstName ? "border-red-500" : ""}
|
className={errors.firstName ? "border-red-500" : ""}
|
||||||
{...register("firstName")}
|
{...register("firstName")}
|
||||||
placeholder={t("enterFullName", { defaultValue: "Enter your full name" })}
|
placeholder={t("enterFullName", {
|
||||||
|
defaultValue: "Enter your full name",
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.firstName && (
|
{errors.firstName && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.firstName.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.firstName.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -252,14 +288,20 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={errors.username ? "border-red-500" : ""}
|
className={errors.username ? "border-red-500" : ""}
|
||||||
{...register("username")}
|
{...register("username")}
|
||||||
placeholder={t("enterUsername", { defaultValue: "Enter username" })}
|
placeholder={t("enterUsername", {
|
||||||
|
defaultValue: "Enter username",
|
||||||
|
})}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value.replace(/[^\w.-]/g, "").toLowerCase();
|
const value = e.target.value
|
||||||
|
.replace(/[^\w.-]/g, "")
|
||||||
|
.toLowerCase();
|
||||||
setValue("username", value);
|
setValue("username", value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{errors.username && (
|
{errors.username && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.username.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.username.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -273,42 +315,54 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
className={errors.email ? "border-red-500" : ""}
|
className={errors.email ? "border-red-500" : ""}
|
||||||
{...register("email")}
|
{...register("email")}
|
||||||
placeholder="Enter your email"
|
placeholder="Enter your email"
|
||||||
value={userData?.email || ""}
|
value={userData || ""}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
{errors.email && (
|
{errors.email && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.email.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.email.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
||||||
<Label className="mb-2">
|
<Label className="mb-2">
|
||||||
{t("number", { defaultValue: "Phone Number" })} <span className="text-red-500">*</span>
|
{t("number", { defaultValue: "Phone Number" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Input
|
<Input
|
||||||
type="tel"
|
type="tel"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={errors.phoneNumber ? "border-red-500" : ""}
|
className={errors.phoneNumber ? "border-red-500" : ""}
|
||||||
{...register("phoneNumber")}
|
{...register("phoneNumber")}
|
||||||
placeholder={t("enterNumber", { defaultValue: "Enter phone number" })}
|
placeholder={t("enterNumber", {
|
||||||
|
defaultValue: "Enter phone number",
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
{errors.phoneNumber && (
|
{errors.phoneNumber && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.phoneNumber.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.phoneNumber.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4 px-0 lg:px-[34px]">
|
<div className="mb-4 px-0 lg:px-[34px]">
|
||||||
<Label htmlFor="address" className="mb-2">
|
<Label htmlFor="address" className="mb-2">
|
||||||
{t("address", { defaultValue: "Address" })} <span className="text-red-500">*</span>
|
{t("address", { defaultValue: "Address" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
className={errors.address ? "border-red-500" : ""}
|
className={errors.address ? "border-red-500" : ""}
|
||||||
{...register("address")}
|
{...register("address")}
|
||||||
placeholder={t("insertAddress", { defaultValue: "Enter your address" })}
|
placeholder={t("insertAddress", {
|
||||||
|
defaultValue: "Enter your address",
|
||||||
|
})}
|
||||||
rows={3}
|
rows={3}
|
||||||
/>
|
/>
|
||||||
{errors.address && (
|
{errors.address && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.address.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.address.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -318,7 +372,8 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
{/* Location Fields */}
|
{/* Location Fields */}
|
||||||
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
||||||
<Label htmlFor="provinsi" className="mb-2">
|
<Label htmlFor="provinsi" className="mb-2">
|
||||||
{t("province", { defaultValue: "Province" })} <span className="text-red-500">*</span>
|
{t("province", { defaultValue: "Province" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<select
|
||||||
className={`mb-3 p-2 border rounded-md text-sm text-slate-400 border-slate-300 bg-white cursor-pointer ${
|
className={`mb-3 p-2 border rounded-md text-sm text-slate-400 border-slate-300 bg-white cursor-pointer ${
|
||||||
|
|
@ -339,13 +394,16 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.provinsi && (
|
{errors.provinsi && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.provinsi.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.provinsi.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
||||||
<Label htmlFor="kota" className="mb-2">
|
<Label htmlFor="kota" className="mb-2">
|
||||||
{t("city", { defaultValue: "City" })} <span className="text-red-500">*</span>
|
{t("city", { defaultValue: "City" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<select
|
||||||
className={`mb-3 p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer ${
|
className={`mb-3 p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer ${
|
||||||
|
|
@ -367,13 +425,16 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.kota && (
|
{errors.kota && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.kota.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.kota.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
<div className="flex flex-col px-0 lg:px-[34px] mb-4">
|
||||||
<Label htmlFor="kecamatan" className="mb-2">
|
<Label htmlFor="kecamatan" className="mb-2">
|
||||||
{t("subdistrict", { defaultValue: "Subdistrict" })} <span className="text-red-500">*</span>
|
{t("subdistrict", { defaultValue: "Subdistrict" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<select
|
<select
|
||||||
className={`p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer ${
|
className={`p-2 border text-sm text-slate-400 rounded-md border-slate-300 bg-white cursor-pointer ${
|
||||||
|
|
@ -395,14 +456,20 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{errors.kecamatan && (
|
{errors.kecamatan && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.kecamatan.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.kecamatan.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Password Fields */}
|
{/* Password Fields */}
|
||||||
<div className="mt-3.5 space-y-2 px-0 lg:px-[34px] mb-4">
|
<div className="mt-3.5 space-y-2 px-0 lg:px-[34px] mb-4">
|
||||||
<Label htmlFor="password" className="mb-2 font-medium text-default-600">
|
<Label
|
||||||
{t("password", { defaultValue: "Password" })} <span className="text-red-500">*</span>
|
htmlFor="password"
|
||||||
|
className="mb-2 font-medium text-default-600"
|
||||||
|
>
|
||||||
|
{t("password", { defaultValue: "Password" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -414,22 +481,37 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
placeholder={t("inputPass", { defaultValue: "Enter password" })}
|
placeholder={t("inputPass", { defaultValue: "Enter password" })}
|
||||||
onChange={(e) => handlePasswordChange(e.target.value)}
|
onChange={(e) => handlePasswordChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer" onClick={togglePasswordVisibility}>
|
<div
|
||||||
|
className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer"
|
||||||
|
onClick={togglePasswordVisibility}
|
||||||
|
>
|
||||||
{showPassword ? (
|
{showPassword ? (
|
||||||
<Icon icon="heroicons:eye-slash" className="w-5 h-5 text-default-400" />
|
<Icon
|
||||||
|
icon="heroicons:eye-slash"
|
||||||
|
className="w-5 h-5 text-default-400"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Icon icon="heroicons:eye" className="w-5 h-5 text-default-400" />
|
<Icon
|
||||||
|
icon="heroicons:eye"
|
||||||
|
className="w-5 h-5 text-default-400"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{errors.password && (
|
{errors.password && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.password.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.password.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3.5 space-y-2 px-0 lg:px-[34px] mb-4">
|
<div className="mt-3.5 space-y-2 px-0 lg:px-[34px] mb-4">
|
||||||
<Label htmlFor="passwordConf" className="mb-2 font-medium text-default-600">
|
<Label
|
||||||
{t("confirmPass", { defaultValue: "Confirm Password" })} <span className="text-red-500">*</span>
|
htmlFor="passwordConf"
|
||||||
|
className="mb-2 font-medium text-default-600"
|
||||||
|
>
|
||||||
|
{t("confirmPass", { defaultValue: "Confirm Password" })}{" "}
|
||||||
|
<span className="text-red-500">*</span>
|
||||||
</Label>
|
</Label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input
|
<Input
|
||||||
|
|
@ -438,19 +520,32 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
className={errors.passwordConf ? "border-red-500" : ""}
|
className={errors.passwordConf ? "border-red-500" : ""}
|
||||||
{...register("passwordConf")}
|
{...register("passwordConf")}
|
||||||
placeholder={t("samePass", { defaultValue: "Confirm your password" })}
|
placeholder={t("samePass", {
|
||||||
|
defaultValue: "Confirm your password",
|
||||||
|
})}
|
||||||
onChange={(e) => handlePasswordConfChange(e.target.value)}
|
onChange={(e) => handlePasswordConfChange(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer" onClick={togglePasswordConfVisibility}>
|
<div
|
||||||
|
className="absolute top-1/2 -translate-y-1/2 ltr:right-4 rtl:left-4 cursor-pointer"
|
||||||
|
onClick={togglePasswordConfVisibility}
|
||||||
|
>
|
||||||
{showPasswordConf ? (
|
{showPasswordConf ? (
|
||||||
<Icon icon="heroicons:eye-slash" className="w-5 h-5 text-default-400" />
|
<Icon
|
||||||
|
icon="heroicons:eye-slash"
|
||||||
|
className="w-5 h-5 text-default-400"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Icon icon="heroicons:eye" className="w-5 h-5 text-default-400" />
|
<Icon
|
||||||
|
icon="heroicons:eye"
|
||||||
|
className="w-5 h-5 text-default-400"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{errors.passwordConf && (
|
{errors.passwordConf && (
|
||||||
<div className="text-red-500 text-sm mt-1">{errors.passwordConf.message}</div>
|
<div className="text-red-500 text-sm mt-1">
|
||||||
|
{errors.passwordConf.message}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -465,10 +560,20 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
// Password validation is handled by the form schema
|
// Password validation is handled by the form schema
|
||||||
}}
|
}}
|
||||||
messages={{
|
messages={{
|
||||||
minLength: t("passCharacter", { defaultValue: "Password must be at least 8 characters" }),
|
minLength: t("passCharacter", {
|
||||||
specialChar: t("passSpecial", { defaultValue: "Password must contain at least one special character" }),
|
defaultValue: "Password must be at least 8 characters",
|
||||||
number: t("passNumber", { defaultValue: "Password must contain at least one number" }),
|
}),
|
||||||
capital: t("passCapital", { defaultValue: "Password must contain at least one uppercase letter" }),
|
specialChar: t("passSpecial", {
|
||||||
|
defaultValue:
|
||||||
|
"Password must contain at least one special character",
|
||||||
|
}),
|
||||||
|
number: t("passNumber", {
|
||||||
|
defaultValue: "Password must contain at least one number",
|
||||||
|
}),
|
||||||
|
capital: t("passCapital", {
|
||||||
|
defaultValue:
|
||||||
|
"Password must contain at least one uppercase letter",
|
||||||
|
}),
|
||||||
match: t("passSame", { defaultValue: "Passwords must match" }),
|
match: t("passSame", { defaultValue: "Passwords must match" }),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
@ -478,10 +583,18 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
<div className="flex justify-center items-center mt-2 mb-4 px-[34px]">
|
<div className="flex justify-center items-center mt-2 mb-4 px-[34px]">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isSubmitting || submitLoading || locationLoading || instituteLoading}
|
disabled={
|
||||||
|
isSubmitting ||
|
||||||
|
submitLoading ||
|
||||||
|
locationLoading ||
|
||||||
|
instituteLoading
|
||||||
|
}
|
||||||
className="border w-[550px] text-center bg-red-700 text-white hover:bg-white hover:text-red-700"
|
className="border w-[550px] text-center bg-red-700 text-white hover:bg-white hover:text-red-700"
|
||||||
>
|
>
|
||||||
{isSubmitting || submitLoading || locationLoading || instituteLoading ? (
|
{isSubmitting ||
|
||||||
|
submitLoading ||
|
||||||
|
locationLoading ||
|
||||||
|
instituteLoading ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
||||||
Processing...
|
Processing...
|
||||||
|
|
@ -495,4 +608,4 @@ export const ProfileForm: React.FC<ProfileFormProps> = ({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@ export const RegistrationOTPForm: React.FC<RegistrationOTPFormProps> = ({
|
||||||
className,
|
className,
|
||||||
}) => {
|
}) => {
|
||||||
const t = useTranslations("LandingPage");
|
const t = useTranslations("LandingPage");
|
||||||
const { verifyOTP, resendOTP, loading, error, formattedTime, canResend } = useOTP();
|
const { verifyOTP, resendOTP, loading, error, formattedTime, canResend } =
|
||||||
|
useOTP();
|
||||||
const [otpValue, setOtpValue] = useState("");
|
const [otpValue, setOtpValue] = useState("");
|
||||||
|
|
||||||
const handleOTPChange = (value: string) => {
|
const handleOTPChange = (value: string) => {
|
||||||
|
|
@ -36,8 +37,14 @@ export const RegistrationOTPForm: React.FC<RegistrationOTPFormProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userData = await verifyOTP(email, otpValue, category, memberIdentity);
|
const userData = await verifyOTP(
|
||||||
onSuccess?.(userData);
|
email,
|
||||||
|
otpValue,
|
||||||
|
category,
|
||||||
|
memberIdentity
|
||||||
|
);
|
||||||
|
if (userData?.error) return false;
|
||||||
|
onSuccess(email);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
onError?.(error.message || "OTP verification failed");
|
onError?.(error.message || "OTP verification failed");
|
||||||
}
|
}
|
||||||
|
|
@ -95,39 +102,39 @@ export const RegistrationOTPForm: React.FC<RegistrationOTPFormProps> = ({
|
||||||
className="gap-2"
|
className="gap-2"
|
||||||
>
|
>
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={0}
|
index={0}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={1}
|
index={1}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
<InputOTPSeparator />
|
<InputOTPSeparator />
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={2}
|
index={2}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={3}
|
index={3}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
</InputOTPGroup>
|
</InputOTPGroup>
|
||||||
<InputOTPSeparator />
|
<InputOTPSeparator />
|
||||||
<InputOTPGroup>
|
<InputOTPGroup>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={4}
|
index={4}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
<InputOTPSlot
|
<InputOTPSlot
|
||||||
index={5}
|
index={5}
|
||||||
onKeyDown={handleTypeOTP}
|
onKeyDown={handleTypeOTP}
|
||||||
className="w-12 h-12 text-lg"
|
className="w-12 h-12 text-lg"
|
||||||
/>
|
/>
|
||||||
|
|
@ -151,10 +158,11 @@ export const RegistrationOTPForm: React.FC<RegistrationOTPFormProps> = ({
|
||||||
disabled={!canResend || loading}
|
disabled={!canResend || loading}
|
||||||
className="bg-slate-300 dark:bg-black text-center rounded-lg mr-1 w-[200px] py-2 text-base"
|
className="bg-slate-300 dark:bg-black text-center rounded-lg mr-1 w-[200px] py-2 text-base"
|
||||||
>
|
>
|
||||||
{canResend
|
{canResend
|
||||||
? t("resend", { defaultValue: "Resend OTP" })
|
? t("resend", { defaultValue: "Resend OTP" })
|
||||||
: `${t("resending", { defaultValue: "Resending" })} (${formattedTime})`
|
: `${t("resending", {
|
||||||
}
|
defaultValue: "Resending",
|
||||||
|
})} (${formattedTime})`}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -176,18 +184,21 @@ export const RegistrationOTPForm: React.FC<RegistrationOTPFormProps> = ({
|
||||||
{/* Help Text */}
|
{/* Help Text */}
|
||||||
<div className="text-center px-8">
|
<div className="text-center px-8">
|
||||||
<p className="text-sm text-gray-600">
|
<p className="text-sm text-gray-600">
|
||||||
{t("otpHelp", { defaultValue: "Didn't receive the code? Check your spam folder or" })}{" "}
|
{t("otpHelp", {
|
||||||
|
defaultValue:
|
||||||
|
"Didn't receive the code? Check your spam folder or",
|
||||||
|
})}{" "}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleResendOTP}
|
onClick={handleResendOTP}
|
||||||
disabled={!canResend || loading}
|
disabled={!canResend || loading}
|
||||||
className="text-blue-600 hover:text-blue-800 underline disabled:opacity-50 disabled:cursor-not-allowed"
|
className="text-blue-600 hover:text-blue-800 underline disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{t("resendOTP", { defaultValue: "resend OTP" })}
|
{t("resend", { defaultValue: "resend OTP" })}
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -458,7 +458,7 @@ export const useRegistration = () => {
|
||||||
const transformedData = transformRegistrationData(sanitizedData, category, userData, instituteId);
|
const transformedData = transformRegistrationData(sanitizedData, category, userData, instituteId);
|
||||||
|
|
||||||
const response = await postRegistration(transformedData);
|
const response = await postRegistration(transformedData);
|
||||||
|
console.log("PPPP", transformedData)
|
||||||
if (response?.error) {
|
if (response?.error) {
|
||||||
throw new Error(response.message || "Registration failed");
|
throw new Error(response.message || "Registration failed");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -566,6 +566,8 @@
|
||||||
"polda": "Official coverage sourced from Polri activities at Polda",
|
"polda": "Official coverage sourced from Polri activities at Polda",
|
||||||
"satker": "Official coverage sourced from Polri activities at Satker",
|
"satker": "Official coverage sourced from Polri activities at Satker",
|
||||||
"resending": "Resending",
|
"resending": "Resending",
|
||||||
|
"resend": "Resend",
|
||||||
|
"otpHelp": "Didn't receive the code? Check your spam folder or",
|
||||||
"regionNews": "Region Police News",
|
"regionNews": "Region Police News",
|
||||||
"divisionNews": "Division Police News",
|
"divisionNews": "Division Police News",
|
||||||
"areaCoverage": "Area Coverage & Divison",
|
"areaCoverage": "Area Coverage & Divison",
|
||||||
|
|
|
||||||
|
|
@ -353,6 +353,8 @@
|
||||||
"versionHistory": "VERSION HISTORY"
|
"versionHistory": "VERSION HISTORY"
|
||||||
},
|
},
|
||||||
"LandingPage": {
|
"LandingPage": {
|
||||||
|
"resend": "Kirim Ulang",
|
||||||
|
"otpHelp": "Tidak menerima kode? Periksa folder spam Anda atau",
|
||||||
"content": "Konten",
|
"content": "Konten",
|
||||||
"new": "Terbaru",
|
"new": "Terbaru",
|
||||||
"schedule": "Jadwal",
|
"schedule": "Jadwal",
|
||||||
|
|
|
||||||
|
|
@ -117,10 +117,7 @@ export async function saveInstitutes(data: any) {
|
||||||
|
|
||||||
export async function postRegistration(data: any) {
|
export async function postRegistration(data: any) {
|
||||||
const url = "public/users/save";
|
const url = "public/users/save";
|
||||||
const headers = {
|
return httpPost(url, data);
|
||||||
"content-type": "application/json",
|
|
||||||
};
|
|
||||||
return httpPost(url, headers, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestOTP(data: any) {
|
export async function requestOTP(data: any) {
|
||||||
|
|
@ -176,4 +173,4 @@ export async function getDataJournalist(cert: any) {
|
||||||
export async function getDataPersonil(nrp: any) {
|
export async function getDataPersonil(nrp: any) {
|
||||||
const url = `public/users/search-personil?nrp=${nrp}`;
|
const url = `public/users/search-personil?nrp=${nrp}`;
|
||||||
return httpGetInterceptor(url);
|
return httpGetInterceptor(url);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue