import React, { useEffect, useState, useMemo } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { v4 } from "uuid"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "ikoncomponents"; import ExpenseDataTable from "./expenseDataTable"; import { Button, Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, Form, FormControl, FormField, FormItem, FormLabel, FormMessage, Input, Textarea, } from "ikoncomponents"; import { CountryDetails } from "@/app/utils/mapping/country"; import { updateProductApi } from "@/app/utils/api/productOfProjectApi"; import { useAppCache } from "@/app/utils/context/AppCacheContext"; // ─── Zod Schema ─────────────────────────────────────────────────────────────── const expenseSchema = z.object({ expenseName: z.string().min(1, "Expense name is required"), location: z.string().min(1, "Location is required"), currency: z.string().min(1, "Currency is required"), cost: z.number().positive("Cost must be greater than 0"), quantity: z.number().positive("Quantity must be greater than 0"), description: z.string().optional(), totalCost: z.number(), }); type ExpenseData = z.infer; interface ExpenseModalProps { isOpen: boolean; onClose: (updatedData?: any) => void; productIdentifier: string; productData: any; setProductData: (data: any) => void; } const ExpenseModal: React.FC = ({ isOpen, onClose, productIdentifier, productData, setProductData, }) => { const [expenseDetails, setExpenseDetails] = useState>({}); const [editingExpenseId, setEditingExpenseId] = useState(null); const [isLoading, setIsLoading] = useState(false); // FX rates come from the shared app-wide cache (year -> currency -> rate). // Expenses carry no year, so use the current year's rates, falling back to // the most recent year available. const { fxRateMap } = useAppCache(); const fxRates = useMemo>(() => { if (!fxRateMap || fxRateMap.size === 0) return new Map(); const years = Array.from(fxRateMap.keys()).sort(); const currentYear = String(new Date().getFullYear()); const yearKey = fxRateMap.has(currentYear) ? currentYear : years[years.length - 1]; return fxRateMap.get(yearKey) ?? new Map(); }, [fxRateMap]); const form = useForm({ resolver: zodResolver(expenseSchema), defaultValues: { expenseName: "", location: "", currency: "USD", cost: 0, quantity: 0, description: "", totalCost: 0, }, }); // Load existing expense details useEffect(() => { if (!productData || !productData.expenseDetails) return; const transformed: Record = {}; Object.keys(productData.expenseDetails).forEach((uuid) => { const e = productData.expenseDetails[uuid]; transformed[uuid] = { expenseName: e.expenseName || "", location: e.location || "", currency: e.currency || "USD", cost: Number(e.cost) || 0, quantity: Number(e.quantity) || 0, totalCost: Number(e.totalCost) || 0, description: e.remarks || "", }; }); setExpenseDetails(transformed); }, [productData, isOpen]); // Auto-calculate totalCost on watch useEffect(() => { const subscription = form.watch((value) => { const { cost, quantity, currency } = value; if (cost && quantity && currency) { const fx = fxRates.get(currency) || 1; const total = Number(cost) * Number(quantity) * fx; if (form.getValues("totalCost") !== total) { form.setValue("totalCost", total, { shouldValidate: false }); } } }); return () => subscription.unsubscribe(); }, [form, fxRates]); // ─── Add / Update (Zod validates here via handleSubmit) ─────────────────── const handleAddOrUpdate = (data: ExpenseData) => { const fx = fxRates.get(data.currency) || 1; const totalCost = data.cost * data.quantity * fx; const expenseId = editingExpenseId || v4(); setExpenseDetails((prev) => ({ ...prev, [expenseId]: { ...data, totalCost }, })); setEditingExpenseId(null); form.reset(); }; const handleEdit = (id: string) => { const item = expenseDetails[id]; if (item) { form.reset(item); setEditingExpenseId(id); } }; // ─── Done / Save (No Zod validation, bypasses form submit) ─────────────── const handleSave = async () => { if (!productData) { console.error("No productData passed to modal"); return; } setIsLoading(true); try { const transformedExpenseDetails: Record = {}; Object.keys(expenseDetails).forEach((uuid) => { transformedExpenseDetails[uuid] = { expenseName: expenseDetails[uuid].expenseName, location: expenseDetails[uuid].location, currency: expenseDetails[uuid].currency, cost: Number(expenseDetails[uuid].cost), quantity: Number(expenseDetails[uuid].quantity), totalCost: Number(expenseDetails[uuid].totalCost), remarks: expenseDetails[uuid].description || "", }; }); const newProductData = { ...productData, productIdentifier, expenseDetails: transformedExpenseDetails, }; // console.log("Final Payload Before PUT:", newProductData); const response = await updateProductApi(newProductData); setProductData(newProductData); console.log("Backend response pf expense ",response) onClose(); } catch (error) { console.error("Error saving expenses:", error); } finally { setIsLoading(false); } }; const expenseArray = Object.keys(expenseDetails).map((uuid) => ({ uuid, ...expenseDetails[uuid], })); return ( open || onClose()}> Expense
( Expense Name* )} /> ( Location* )} /> ( Currency* )} />
( Cost* field.onChange(e.target.valueAsNumber)} /> )} /> ( Quantity* field.onChange(e.target.valueAsNumber)} /> )} /> ( Total Cost* field.onChange(e.target.valueAsNumber)} /> )} />
( Description