import React, { useState, useEffect } from "react"; import { View, ScrollView, Pressable, TextInput, StyleSheet, ActivityIndicator, } from "react-native"; import { useColorScheme } from "nativewind"; import { Text } from "@/components/ui/text"; import { Button } from "@/components/ui/button"; import { Trash2, Plus, Calendar, ChevronDown, CalendarSearch, } from "@/lib/icons"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { FormFlow } from "@/components/FormFlow"; import { CustomerPicker } from "@/components/CustomerPicker"; import { useSirouRouter } from "@sirou/react-native"; import { AppRoutes } from "@/lib/routes"; import { Stack, useLocalSearchParams } from "expo-router"; import { useRouter } from "expo-router"; import { api } from "@/lib/api"; import { toast } from "@/lib/toast-store"; import { PickerModal, SelectOption } from "@/components/PickerModal"; import { CalendarGrid } from "@/components/CalendarGrid"; import { StandardHeader } from "@/components/StandardHeader"; type Item = { id: number; description: string; qty: string; price: 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, textAlign: "center", textAlignVertical: "center", }, }); function useInputColors() { const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; return { bg: isDark ? "rgba(30,30,30,0.8)" : "rgba(241,245,249,0.2)", border: isDark ? "rgba(255,255,255,0.08)" : "rgba(203,213,225,0.6)", text: isDark ? "#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, }: { label: string; value: string; onPress: () => void; }) { const c = useInputColors(); return ( {label} {value} ); } const STEPS = [ { key: "customer", label: "Customer" }, { key: "details", label: "Details" }, { key: "dates", label: "Dates" }, { key: "items", label: "Items" }, { key: "totals", label: "Totals" }, { key: "config", label: "Config" }, ]; export default function EditProformaScreen() { const nav = useSirouRouter(); const router = useRouter(); const { id } = useLocalSearchParams(); const isEdit = !!id; const [loading, setLoading] = useState(isEdit); const [submitting, setSubmitting] = useState(false); const [step, setStep] = useState(0); const [proformaNumber, setProformaNumber] = useState(""); const [customerName, setCustomerName] = useState(""); const [customerEmail, setCustomerEmail] = useState(""); const [customerPhone, setCustomerPhone] = useState(""); const [currency, setCurrency] = useState("USD"); const [description, setDescription] = useState(""); const [notes, setNotes] = useState(""); const [taxAmount, setTaxAmount] = useState(""); const [discountAmount, setDiscountAmount] = useState(""); const [issueDate, setIssueDate] = useState(new Date()); const [dueDate, setDueDate] = useState(new Date()); const [items, setItems] = useState([ { id: 1, description: "", qty: "", price: "" }, ]); const [currencyModal, setCurrencyModal] = useState(false); const [issueModal, setIssueModal] = useState(false); const [dueModal, setDueModal] = useState(false); useEffect(() => { if (isEdit) { fetchProforma(); } }, [id]); const fetchProforma = async () => { try { setLoading(true); const data = await api.proforma.getById({ params: { id: id as string } }); setProformaNumber(data.proformaNumber || ""); setCustomerName(data.customerName || ""); setCustomerEmail(data.customerEmail || ""); setCustomerPhone(data.customerPhone || ""); setCurrency(data.currency || "USD"); setDescription(data.description || ""); setNotes(data.notes || ""); setTaxAmount( String( typeof data.taxAmount === "object" ? data.taxAmount?.value : data.taxAmount || "", ), ); setDiscountAmount( String( typeof data.discountAmount === "object" ? data.discountAmount?.value : data.discountAmount || "", ), ); setIssueDate(new Date(data.issueDate)); setDueDate(new Date(data.dueDate)); setItems( data.items?.map((item: any, idx: number) => ({ id: idx + 1, description: item.description || "", qty: String(item.quantity || ""), price: String(item.unitPrice?.value || item.unitPrice || ""), })) || [{ id: 1, description: "", qty: "", price: "" }], ); } catch (error) { toast.error("Error", "Failed to load proforma"); } finally { setLoading(false); } }; const addItem = () => { const newId = Math.max(...items.map((i) => i.id)) + 1; setItems([...items, { id: newId, description: "", qty: "", price: "" }]); }; const removeItem = (id: number) => { if (items.length > 1) { setItems(items.filter((i) => i.id !== id)); } }; const updateItem = (id: number, field: keyof Item, value: string) => { setItems(items.map((i) => (i.id === id ? { ...i, [field]: value } : i))); }; const calculateSubtotal = () => { return items.reduce((acc, item) => { const qty = parseFloat(item.qty) || 0; const price = parseFloat(item.price) || 0; return acc + qty * price; }, 0); }; const calculateTotal = () => { const subtotal = calculateSubtotal(); const tax = parseFloat(taxAmount) || 0; const discount = parseFloat(discountAmount) || 0; return subtotal + tax - discount; }; const handleNext = () => { if (step === 0 && !customerName.trim()) { toast.error("Validation", "Customer name is required"); return; } if (step === 1 && !proformaNumber.trim()) { toast.error("Validation", "Proforma number is required"); return; } setStep(step + 1); }; const handleSubmit = async () => { if (!proformaNumber || !customerName) { toast.error("Error", "Please fill required fields"); return; } setSubmitting(true); try { const payload = { proformaNumber, customerName, customerEmail, customerPhone, amount: calculateTotal(), currency, issueDate: issueDate.toISOString(), dueDate: dueDate.toISOString(), description, notes, taxAmount: parseFloat(taxAmount) || 0, discountAmount: parseFloat(discountAmount) || 0, items: items.map((item) => ({ description: item.description, quantity: parseFloat(item.qty) || 0, unitPrice: parseFloat(item.price) || 0, total: (parseFloat(item.qty) || 0) * (parseFloat(item.price) || 0), })), }; if (isEdit) { await api.proforma.update({ params: { id: id as string }, body: payload, }); toast.success("Success", "Proforma updated successfully"); } else { await api.proforma.create({ body: payload }); toast.success("Success", "Proforma created successfully"); } nav.back(); } catch (error: any) { toast.error("Error", error.message || "Failed to save proforma"); } finally { setSubmitting(false); } }; if (loading) { return ( ); } const currencies = ["USD", "EUR", "GBP", "CAD"]; return ( setStep(step - 1)} onComplete={handleSubmit} loading={submitting} completeLabel={isEdit ? "Update Proforma" : "Create Proforma"} > {step === 0 && ( Customer Details Customer Name { setCustomerName(c.name); setCustomerEmail(c.email); setCustomerPhone(c.phone.replace("+251", "")); }} placeholder="Select or search for a customer" /> )} {step === 1 && ( Proforma Details )} {step === 2 && ( Dates setIssueModal(true)} /> setDueModal(true)} /> )} {step === 3 && ( Items Add {items.map((item) => ( Item {item.id} removeItem(item.id)} hitSlop={8}> updateItem(item.id, "description", v)} /> updateItem(item.id, "qty", v)} flex={1} /> updateItem(item.id, "price", v)} flex={3} /> ))} )} {step === 4 && ( Totals Subtotal {currency} {calculateSubtotal().toFixed(2)} Total {currency} {calculateTotal().toFixed(2)} )} {step === 5 && ( Configuration setCurrencyModal(true)} /> )} setCurrencyModal(false)} > {currencies.map((curr) => ( { setCurrency(v); setCurrencyModal(false); }} /> ))} setIssueModal(false)} > { setIssueDate(new Date(dateStr)); setIssueModal(false); }} selectedDate={issueDate.toISOString().substring(0, 10)} /> setDueModal(false)} > { setDueDate(new Date(dateStr)); setDueModal(false); }} selectedDate={dueDate.toISOString().substring(0, 10)} /> ); }