import React, { useState, useEffect } from "react"; import { View, Pressable, TextInput, StyleSheet, Switch } from "react-native"; import { useColorScheme } from "nativewind"; import { Text } from "@/components/ui/text"; import { ChevronDown, Plus, Trash2, X } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { api } from "@/lib/api"; import { toast } from "@/lib/toast-store"; import { PickerModal, SelectOption } from "@/components/PickerModal"; import { CalendarGrid } from "@/components/CalendarGrid"; import { FormFlow } from "@/components/FormFlow"; import { CustomerPicker } from "@/components/CustomerPicker"; type Item = { id: number; itemName: string; itemDescription: string; quantity: string; unitOfMeasure: string; }; const S = StyleSheet.create({ input: { paddingHorizontal: 12, paddingVertical: 12, fontSize: 12, fontWeight: "500", borderRadius: 6, borderWidth: 1, textAlignVertical: "center", }, inputCenter: { paddingHorizontal: 12, paddingVertical: 10, fontSize: 14, fontWeight: "500", borderRadius: 6, borderWidth: 1, textAlignVertical: "center", textAlign: "center", }, }); function useInputColors() { const { colorScheme } = useColorScheme(); const dark = colorScheme === "dark"; return { bg: dark ? "rgba(30,30,30,0.8)" : "rgba(241,245,249,0.2)", border: dark ? "rgba(255,255,255,0.08)" : "rgba(203,213,225,0.6)", text: dark ? "#f1f5f9" : "#0f172a", placeholder: "rgba(100,116,139,0.45)", }; } function Field({ label, value, onChangeText, placeholder, numeric = false, center = false, flex, multiline = false, }: { label: string; value: string; onChangeText: (v: string) => void; placeholder: string; numeric?: boolean; center?: boolean; flex?: number; multiline?: boolean; }) { const c = useInputColors(); return ( {label} ); } function PickerField({ label, value, onPress, flex, }: { label: string; value: string; onPress: () => void; flex?: number; }) { const c = useInputColors(); return ( {label} {value || "Select"} ); } const CATEGORIES = ["EQUIPMENT", "SERVICE", "MIXED"]; const INCOTERMS = ["EXW", "FOB", "CIF", "DAP", "DDP"]; const INVITE_CHANNELS = ["EMAIL", "PHONE"]; const UNITS = ["unit", "kg", "m", "m²", "m³", "litre", "hour", "service"]; const STEPS = [ { key: "details", label: "Details" }, { key: "items", label: "Items" }, { key: "terms", label: "Terms" }, { key: "schedule", label: "Schedule" }, { key: "summary", label: "Summary" }, ]; export default function CreateProformaRequestScreen() { const nav = useSirouRouter(); const [step, setStep] = useState(0); const [submitting, setSubmitting] = useState(false); // Step 1 — Details const [title, setTitle] = useState(""); const [description, setDescription] = useState(""); const [category, setCategory] = useState("EQUIPMENT"); // Step 2 — Items const [items, setItems] = useState([ { id: 1, itemName: "", itemDescription: "", quantity: "1", unitOfMeasure: "unit", }, ]); // Step 3 — Terms const [paymentTerms, setPaymentTerms] = useState("Net 30 days"); const [incoterms, setIncoterms] = useState("EXW"); const [taxIncluded, setTaxIncluded] = useState(false); const [discountStructure, setDiscountStructure] = useState(""); const [validityPeriod, setValidityPeriod] = useState("30"); const [allowRevisions, setAllowRevisions] = useState(true); // Step 4 — Schedule & Invite const [submissionDeadline, setSubmissionDeadline] = useState(""); const [inviteChannel, setInviteChannel] = useState("EMAIL"); const [customerIds, setCustomerIds] = useState([]); const [selectedCustomers, setSelectedCustomers] = useState< { id: string; name: string; email: string; phone: string }[] >([]); // Modal visibility const [showCategory, setShowCategory] = useState(false); const [showIncoterms, setShowIncoterms] = useState(false); const [showInviteChannel, setShowInviteChannel] = useState(false); const [showDeadline, setShowDeadline] = useState(false); const c = useInputColors(); useEffect(() => { const d = new Date(); d.setDate(d.getDate() + 14); setSubmissionDeadline(d.toISOString().split("T")[0]); }, []); const updateItem = (id: number, field: keyof Item, value: string) => setItems((prev) => prev.map((it) => (it.id === id ? { ...it, [field]: value } : it)), ); const addItem = () => setItems((prev) => [ ...prev, { id: Date.now(), itemName: "", itemDescription: "", quantity: "1", unitOfMeasure: "unit", }, ]); const removeItem = (id: number) => { if (items.length > 1) setItems((prev) => prev.filter((it) => it.id !== id)); }; const setItemUnit = (id: number, unit: string) => updateItem(id, "unitOfMeasure", unit); const handleNext = () => { if (step === 0 && !title.trim()) { toast.error("Validation", "Title is required"); return; } if (step === 1) { if (items.length === 0) { toast.error("Validation", "Add at least one item"); return; } if (items.every((it) => !it.itemName.trim())) { toast.error("Validation", "At least one item must have a name"); return; } } setStep((s) => s + 1); }; const handleSubmit = async () => { if (!title.trim()) { toast.error("Validation", "Title is required"); return; } if (!submissionDeadline) { toast.error("Validation", "Submission deadline is required"); return; } const payload = { title: title.trim(), description: description.trim() || undefined, category, submissionDeadline: new Date(submissionDeadline).toISOString(), allowRevisions, paymentTerms: paymentTerms.trim() || undefined, incoterms: incoterms || undefined, taxIncluded, discountStructure: discountStructure.trim() || undefined, validityPeriod: parseInt(validityPeriod, 10) || 30, items: items .filter((it) => it.itemName.trim()) .map((it) => ({ itemName: it.itemName.trim(), itemDescription: it.itemDescription.trim() || undefined, quantity: parseInt(it.quantity, 10) || 1, unitOfMeasure: it.unitOfMeasure || "unit", })), customerIds: customerIds.length > 0 ? customerIds : undefined, inviteChannel, }; try { setSubmitting(true); await api.proformaRequests.create({ body: payload, headers: { "Content-Type": "application/json" }, }); toast.success("Success", "Proforma request created successfully!"); nav.back(); } catch (err: any) { console.error("[ProformaRequestCreate] Error:", err); toast.error( "Error", err?.response?.data?.message || err?.message || "Failed to create request", ); } finally { setSubmitting(false); } }; const totalQuantity = items.reduce( (s, it) => s + (parseInt(it.quantity, 10) || 0), 0, ); const namedItemCount = items.filter((it) => it.itemName.trim()).length; return ( setStep(step - 1)} onComplete={handleSubmit} loading={submitting} completeLabel="Create Request" > {step === 0 && ( Request Details setShowCategory(true)} /> )} {step === 1 && ( Requested Items Add {items.map((item, index) => ( 1} onUpdate={updateItem} onRemove={removeItem} onPickUnit={(u) => setItemUnit(item.id, u)} /> ))} )} {step === 2 && ( Commercial Terms setShowIncoterms(true)} flex={2} /> Options )} {step === 3 && ( Deadline & Invitation setShowDeadline(true)} /> setShowInviteChannel(true)} /> Customers { setCustomerIds(ids); setSelectedCustomers(customers); }} placeholder="Select customers to invite" /> {selectedCustomers.length > 0 && ( {selectedCustomers.map((cust) => ( {cust.name} { setCustomerIds((ids) => ids.filter((id) => id !== cust.id), ); setSelectedCustomers((prev) => prev.filter((c) => c.id !== cust.id), ); }} hitSlop={6} > ))} )} )} {step === 4 && ( Summary {description ? ( ) : null} {namedItemCount > 0 ? ( Items ({namedItemCount}) {items .filter((it) => it.itemName.trim()) .map((it, i) => ( {it.itemName} {it.quantity} {it.unitOfMeasure} ))} ) : null} {discountStructure ? ( ) : null} Tax Included {taxIncluded ? "Yes" : "No"} Allow Revisions {allowRevisions ? "Yes" : "No"} {selectedCustomers.length > 0 ? ( Customers ({selectedCustomers.length}) {selectedCustomers.map((cust) => ( {cust.name} ))} ) : null} )} setShowCategory(false)} title="Select Category" > {CATEGORIES.map((cat) => ( { setCategory(v); setShowCategory(false); }} /> ))} setShowIncoterms(false)} title="Select Incoterms" > {INCOTERMS.map((t) => ( { setIncoterms(v); setShowIncoterms(false); }} /> ))} setShowInviteChannel(false)} title="Invite Channel" > {INVITE_CHANNELS.map((ch) => ( { setInviteChannel(v); setShowInviteChannel(false); }} /> ))} setShowDeadline(false)} title="Submission Deadline" > { setSubmissionDeadline(v); setShowDeadline(false); }} /> ); } function ToggleRow({ title, subtitle, value, onValueChange, }: { title: string; subtitle: string; value: boolean; onValueChange: (v: boolean) => void; }) { return ( {title} {subtitle} ); } function ItemRow({ item, index, canRemove, onUpdate, onRemove, onPickUnit, }: { item: Item; index: number; canRemove: boolean; onUpdate: (id: number, field: keyof Item, value: string) => void; onRemove: (id: number) => void; onPickUnit: (u: string) => void; }) { const [showUnits, setShowUnits] = useState(false); const c = useInputColors(); return ( Item {index + 1} {canRemove && ( onRemove(item.id)} hitSlop={8}> )} onUpdate(item.id, "itemName", v)} /> onUpdate(item.id, "itemDescription", v)} multiline /> onUpdate(item.id, "quantity", v)} flex={1} /> Unit setShowUnits(true)} className="h-10 px-3 border border-border rounded-[6px] flex-row items-center justify-between" style={{ backgroundColor: c.bg, borderColor: c.border }} > {item.unitOfMeasure || "unit"} setShowUnits(false)} title="Unit of Measure" > {UNITS.map((u) => ( { onPickUnit(v); setShowUnits(false); }} /> ))} ); } function Row({ label, value, multiline, }: { label: string; value: string; multiline?: boolean; }) { return ( {label} {value} ); }