203 lines
9.3 KiB
TypeScript
203 lines
9.3 KiB
TypeScript
![]() |
import { motion } from "framer-motion";
|
||
|
|
||
|
// RegisterForm.tsx
|
||
|
export const RegisterForm = ({ form, onSubmit, isLoading }) => {
|
||
|
const { register, handleSubmit, formState: { errors }, watch } = form;
|
||
|
const password = watch("password");
|
||
|
|
||
|
return (
|
||
|
<motion.form
|
||
|
initial={{ opacity: 0 }}
|
||
|
animate={{ opacity: 1 }}
|
||
|
exit={{ opacity: 0 }}
|
||
|
className="space-y-6"
|
||
|
onSubmit={handleSubmit(onSubmit)}
|
||
|
>
|
||
|
<h2 className="text-2xl font-bold text-white text-center mb-8">
|
||
|
Create Account
|
||
|
</h2>
|
||
|
|
||
|
<div className="space-y-4">
|
||
|
{/* Department Selection */}
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Department
|
||
|
</label>
|
||
|
<select
|
||
|
{...register("deptId", { required: "Department is required" })}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
>
|
||
|
<option value="">Select Department</option>
|
||
|
<option value="AF-HQ">Air Force Headquarters</option>
|
||
|
<option value="AF-OPS">Operations Command</option>
|
||
|
<option value="AF-LOG">Logistics Command</option>
|
||
|
<option value="AF-TRN">Training Command</option>
|
||
|
</select>
|
||
|
{errors.deptId && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.deptId.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
{/* User Information Row */}
|
||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Username
|
||
|
</label>
|
||
|
<input
|
||
|
{...register("username", {
|
||
|
required: "Username is required",
|
||
|
minLength: {
|
||
|
value: 2,
|
||
|
message: "Username must be at least 2 characters"
|
||
|
},
|
||
|
pattern: {
|
||
|
value: /^[a-zA-Z0-9._-]+$/,
|
||
|
message: "Username can only contain letters, numbers, and ._-"
|
||
|
}
|
||
|
})}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
placeholder="Username"
|
||
|
/>
|
||
|
{errors.username && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.username.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Full Name
|
||
|
</label>
|
||
|
<input
|
||
|
{...register("showname", {
|
||
|
required: "Full name is required",
|
||
|
minLength: {
|
||
|
value: 2,
|
||
|
message: "Name must be at least 2 characters"
|
||
|
}
|
||
|
})}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
placeholder="Full Name"
|
||
|
/>
|
||
|
{errors.showname && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.showname.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
{/* Service ID */}
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Service ID
|
||
|
</label>
|
||
|
<input
|
||
|
{...register("officerId", {
|
||
|
required: "Service ID is required",
|
||
|
pattern: {
|
||
|
value: /^\d{5,12}$/,
|
||
|
message: "Please enter a valid Service ID (5-12 digits)"
|
||
|
}
|
||
|
})}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
placeholder="Service ID"
|
||
|
/>
|
||
|
{errors.officerId && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.officerId.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
{/* Password Fields */}
|
||
|
<div className="space-y-4">
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Password
|
||
|
</label>
|
||
|
<input
|
||
|
type="password"
|
||
|
{...register("password", {
|
||
|
required: "Password is required",
|
||
|
minLength: {
|
||
|
value: 8,
|
||
|
message: "Password must be at least 8 characters"
|
||
|
},
|
||
|
pattern: {
|
||
|
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
|
||
|
message: "Password must contain uppercase, lowercase, number and special character"
|
||
|
}
|
||
|
})}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
placeholder="Password"
|
||
|
/>
|
||
|
{errors.password && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.password.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
|
||
|
<div>
|
||
|
<label className="block text-sm font-medium text-gray-300 mb-1">
|
||
|
Confirm Password
|
||
|
</label>
|
||
|
<input
|
||
|
type="password"
|
||
|
{...register("repeatPass", {
|
||
|
required: "Please confirm your password",
|
||
|
validate: value => value === password || "Passwords do not match"
|
||
|
})}
|
||
|
className="w-full px-4 py-2 bg-white/10 border border-gray-600
|
||
|
rounded-lg focus:ring-2 focus:ring-blue-500
|
||
|
text-white placeholder-gray-400"
|
||
|
placeholder="Confirm Password"
|
||
|
/>
|
||
|
{errors.repeatPass && (
|
||
|
<span className="text-red-400 text-sm mt-1">
|
||
|
{errors.repeatPass.message}
|
||
|
</span>
|
||
|
)}
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
{/* Submit Button */}
|
||
|
<button
|
||
|
type="submit"
|
||
|
disabled={isLoading}
|
||
|
className="w-full py-3 px-4 bg-blue-600 text-white rounded-lg
|
||
|
hover:bg-blue-700 transition-colors duration-300
|
||
|
disabled:opacity-50 disabled:cursor-not-allowed
|
||
|
flex items-center justify-center space-x-2"
|
||
|
>
|
||
|
{isLoading ? (
|
||
|
<>
|
||
|
<svg className="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||
|
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||
|
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||
|
</svg>
|
||
|
<span>Creating Account...</span>
|
||
|
</>
|
||
|
) : (
|
||
|
<span>Create Account</span>
|
||
|
)}
|
||
|
</button>
|
||
|
|
||
|
</motion.form>
|
||
|
);
|
||
|
};
|