osaka/src/pages/AccountCreation.jsx

305 lines
9.1 KiB
JavaScript

import { AnimatePresence } from "motion/react";
import clsx from "clsx";
import { PackageSearch, Copy, UserSearch } from "lucide-react";
import { useState } from "react";
import { motion } from "motion/react";
import FormBox from "../components/FormBox";
import { useTranslation } from "react-i18next";
import FormField from "../components/FormField";
import FormInput from "../components/FormInput";
import FormSelect from "../components/FormSelect";
import Button from "../components/Button";
import { useToast } from "../hooks/useToast";
import productInfo from "../util/productList";
import ProductListTable from "../components/ProductListTable";
function AccountCreation() {
const { t } = useTranslation();
const showToast = useToast();
const [notification] = useState({
visible: false,
message: "",
type: "",
});
const [showProductModal, setShowProductModal] = useState(false);
const [submitting] = useState(false);
const [accountDetails, setAccountDetails] = useState({
productCode: "",
interestCategory: "",
segmentCode: "",
accountHolderType: "",
primaryCifNumber: "",
nomineeCifNumber: "",
activityCode: "",
customerType: "",
collateralFDAccount: "",
rentPayAccount: "",
productCodeValid: true,
interestCategoryValid: true,
segmentCodeValid: true,
accountHolderTypeValid: true,
primaryCifNumberValid: true,
nomineeCifNumberValid: true,
activityCodeValid: true,
customerTypeValid: true,
collateralFDAccountValid: true,
rentPayAccountValid: true,
});
const handleProductSelect = (product) => {
const newAccountDetails = { ...accountDetails };
newAccountDetails.productCode = product.productCode;
newAccountDetails.interestCategory = product.interestCategory;
setAccountDetails(newAccountDetails);
setShowProductModal(false);
};
const accountDetailsFields = [
{
label: t("productCode"),
name: "productCode",
type: "input",
subType: "number",
readOnly: true,
validate: (value) => value !== "",
icon: {
icon: <PackageSearch size={18} />,
onClick: () => setShowProductModal(true),
},
},
{
label: t("interestCategory"),
name: "interestCategory",
type: "input",
subType: "number",
readOnly: true,
validate: (value) => value !== "",
},
{
label: t("segmentCode"),
name: "segmentCode",
type: "select",
options: [
{ value: "0706", label: "0706: Individual" },
{ value: "0306", label: "0306: Staff" },
{ value: "5003", label: "5003: Senior Citizen" },
{ value: "5010", label: "5010: SHG" },
{ value: "5000", label: "5000: Bank" },
{ value: "5009", label: "5009: Institutions" },
{ value: "5050", label: "5050: Others" },
{ value: "5007", label: "5007: Society" },
],
validate: (value) => value !== "",
},
{
label: t("accountHolderType"),
name: "accountHolderType",
type: "select",
options: [
{ value: "1", label: "Single" },
{ value: "2", label: "Joint" },
],
validate: (value) => value === "1" || value === "2",
},
{
label: t("primaryCifNumber"),
name: "primaryCifNumber",
type: "input",
subType: "number",
maxLength: 17,
validate: (value) => /^[0-9]{17}$/.test(value),
icon: { icon: <UserSearch size={18} /> },
},
{
label: t("nomineeCifNumber"),
name: "nomineeCifNumber",
type: "input",
subType: "number",
maxLength: 17,
validate: (value) => /^[0-9]{17}$/.test(value),
},
];
const additionalDetailsFields = [
{
label: t("activityCode"),
name: "activityCode",
type: "select",
options: [
{ value: "0701", label: "Direct Agriculture" },
{ value: "0702", label: "Indirect Agriculture" },
{ value: "0703", label: "Agricultural Services Unit" },
{ value: "0704", label: "Farm Irrigation" },
{ value: "0705", label: "Fruits & Vegetables" },
{ value: "0706", label: "Non-Agriculture" },
],
validate: (value) => value !== "",
},
{
label: t("customerType"),
name: "customerType",
type: "select",
options: [
{ value: "0709", label: "Individual" },
{ value: "0701", label: "Corporate" },
],
validate: (value) => value === "0709" || value === "0701",
},
{
label: t("collateralFDAccount"),
name: "collateralFDAccount",
type: "input",
subType: "number",
maxLength: 17,
validate: (value) => /^[0-9]{17}$/.test(value),
},
{
label: t("rentPayAccount"),
name: "rentPayAccount",
type: "input",
subType: "number",
maxLength: 17,
validate: (value) => /^[0-9]{17}$/.test(value),
},
];
const handleSubmit = (e) => {
e.preventDefault();
let isValid = true;
const newValidationState = { ...accountDetails };
// Validate account details fields
[...accountDetailsFields, ...additionalDetailsFields].forEach((field) => {
if (field.validate) {
const fieldIsValid = field.validate(accountDetails[field.name]);
newValidationState[`${field.name}Valid`] = fieldIsValid;
if (!fieldIsValid) isValid = false;
}
});
setAccountDetails(newValidationState);
if (!isValid) {
showToast("Highlighted fields are invalid", "error");
return;
}
console.log("Form is valid", accountDetails);
};
const renderProductModal = () => {
return (
<AnimatePresence mode="popLayout">
{showProductModal && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
key="productList"
className="fixed z-50 inset-0 flex items-center justify-center bg-black/50"
>
<motion.div
initial={{ scale: 0.8 }}
animate={{ scale: 1 }}
exit={{ scale: 0.8 }}
className="flex flex-col items-center bg-white p-4 py-8 rounded-3xl w-[60%] max-h-[80%] overflow-auto font-body"
>
<h2 className="text-xl mb-4">Select Product</h2>
<ProductListTable
productInfo={productInfo}
onSelectProduct={handleProductSelect}
/>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};
const renerField = (field) => {
const commonProps = {
value: accountDetails[field.name],
onChange: (e) => {
const newAccountDetails = { ...accountDetails };
newAccountDetails[field.name] = e.target.value;
newAccountDetails[`${field.name}Valid`] = true;
setAccountDetails(newAccountDetails);
},
maxLength: field.maxLength,
className: clsx(!accountDetails[`${field.name}Valid`] && "border-error"),
};
return (
<FormField key={field.name} label={field.label} icon={field.icon}>
{field.type === "input" ? (
<FormInput
{...commonProps}
type={field.subType}
readOnly={field.readOnly}
/>
) : (
<FormSelect {...commonProps} options={field.options} />
)}
</FormField>
);
};
return (
<div>
<AnimatePresence>
{notification.visible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className={clsx(
"p-4 mb-8 font-body text-center text-xl rounded-2xl flex items-center justify-center gap-2",
notification.type === "error"
? "bg-error-surface text-error"
: "bg-success-surface text-success"
)}
>
{notification.message.split(":").map((msg, index) => {
return index === 1 ? (
<span key={index} className="border-b border-dashed">
{msg}
</span>
) : (
<span key={index}>{msg}</span>
);
})}
<Copy
cursor={"pointer"}
size={15}
onClick={navigator.clipboard.writeText(
notification.message.split(":")[1].trim()
)}
/>
</motion.div>
)}
</AnimatePresence>
{renderProductModal()}
<FormBox title="Account Creation" disabled={submitting}>
<div className="p-2 pt-7 ">
<div className="flex flex-col gap-4">
<h1 className="text-2xl font-medium text-primary py-2">
Account Details
</h1>
{accountDetailsFields.map(renerField)}
</div>
<div className="flex flex-col gap-4">
<h1 className="text-2xl font-medium text-primary py-2 pt-6">
Additional Details
</h1>
{additionalDetailsFields.map(renerField)}
</div>
</div>
<Button
text={t("submit")}
onClick={handleSubmit}
disabled={submitting}
/>
</FormBox>
</div>
);
}
export default AccountCreation;