import React, { useEffect, useMemo, useState } from "react"; import { ActivityIndicator, Pressable, ScrollView, StyleSheet, TextInput, View, } from "react-native"; import { Stack } from "expo-router"; import { useSirouRouter } from "@sirou/react-native"; import { colorScheme, useColorScheme } from "nativewind"; import { api } from "@/lib/api"; import { AppRoutes } from "@/lib/routes"; import { toast } from "@/lib/toast-store"; import { getPlaceholderColor } from "@/lib/colors"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { ShadowWrapper } from "@/components/ShadowWrapper"; import { PickerModal, SelectOption } from "@/components/PickerModal"; import { CalendarGrid } from "@/components/CalendarGrid"; import { Button } from "@/components/ui/button"; import { Text } from "@/components/ui/text"; import { Calendar, CalendarSearch, ChevronDown, Plus, Send, Trash2, } from "@/lib/icons"; type Item = { id: number; description: string; qty: string; price: string }; type Account = { id: number; bankName: string; accountName: string; accountNumber: string; currency: string; }; const S = StyleSheet.create({ input: { height: 44, paddingHorizontal: 12, fontSize: 12, fontWeight: "500", borderRadius: 6, borderWidth: 1, }, inputCenter: { height: 44, paddingHorizontal: 12, fontSize: 14, fontWeight: "500", borderRadius: 6, borderWidth: 1, textAlign: "center", }, }); function useInputColors() { const { colorScheme: scheme } = useColorScheme(); const dark = scheme === "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, }: { label: string; value: string; onChangeText: (v: string) => void; placeholder: string; numeric?: boolean; center?: boolean; flex?: number; }) { const c = useInputColors(); const isDark = colorScheme.get() === "dark"; return ( {label} ); } function Label({ children, noMargin, }: { children: string; noMargin?: boolean; }) { return ( {children} ); } const CURRENCIES = ["USD", "ETB", "EUR", "GBP", "KES", "ZAR"]; const STATUSES = ["DRAFT", "PENDING", "PAID", "CANCELLED"]; export default function CreatePaymentRequestScreen() { const nav = useSirouRouter(); const [submitting, setSubmitting] = useState(false); const [paymentRequestNumber, setPaymentRequestNumber] = useState(""); const [customerName, setCustomerName] = useState(""); const [customerEmail, setCustomerEmail] = useState(""); const [customerPhone, setCustomerPhone] = useState(""); const [customerId, setCustomerId] = useState(""); const [amount, setAmount] = useState(""); const [currency, setCurrency] = useState("USD"); const [description, setDescription] = useState(""); const [notes, setNotes] = useState(""); const [taxAmount, setTaxAmount] = useState("0"); const [discountAmount, setDiscountAmount] = useState("0"); const [issueDate, setIssueDate] = useState( new Date().toISOString().split("T")[0], ); const [dueDate, setDueDate] = useState(""); const [paymentId, setPaymentId] = useState(""); const [status, setStatus] = useState("DRAFT"); const [items, setItems] = useState([ { id: 1, description: "", qty: "1", price: "" }, ]); const [accounts, setAccounts] = useState([ { id: 1, bankName: "", accountName: "", accountNumber: "", currency: "ETB", }, ]); const c = useInputColors(); const [showCurrency, setShowCurrency] = useState(false); const [showIssueDate, setShowIssueDate] = useState(false); const [showDueDate, setShowDueDate] = useState(false); const [showStatus, setShowStatus] = useState(false); useEffect(() => { const year = new Date().getFullYear(); const random = Math.floor(1000 + Math.random() * 9000); setPaymentRequestNumber(`PAYREQ-${year}-${random}`); const d = new Date(); d.setDate(d.getDate() + 30); setDueDate(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(), description: "", qty: "1", price: "" }, ]); const removeItem = (id: number) => { if (items.length > 1) setItems((prev) => prev.filter((it) => it.id !== id)); }; const updateAccount = (id: number, field: keyof Account, value: string) => setAccounts((prev) => prev.map((acc) => (acc.id === id ? { ...acc, [field]: value } : acc)), ); const addAccount = () => setAccounts((prev) => [ ...prev, { id: Date.now(), bankName: "", accountName: "", accountNumber: "", currency: "ETB", }, ]); const removeAccount = (id: number) => { if (accounts.length > 1) setAccounts((prev) => prev.filter((acc) => acc.id !== id)); }; const subtotal = useMemo( () => items.reduce( (sum, item) => sum + (parseFloat(item.qty) || 0) * (parseFloat(item.price) || 0), 0, ), [items], ); const computedTotal = useMemo( () => subtotal + (parseFloat(taxAmount) || 0) - (parseFloat(discountAmount) || 0), [subtotal, taxAmount, discountAmount], ); const isDark = colorScheme.get() === "dark"; const handleSubmit = async () => { if (!customerName) { toast.error("Validation Error", "Please enter a customer name"); return; } const formattedPhone = customerPhone.startsWith("+") ? customerPhone : customerPhone.length > 0 ? `+251${customerPhone}` : ""; const body = { paymentRequestNumber, customerName, customerEmail, customerPhone: formattedPhone, amount: amount ? Number(amount) : Number(computedTotal.toFixed(2)), currency, issueDate: new Date(issueDate).toISOString(), dueDate: new Date(dueDate).toISOString(), description: description || `Payment request for ${customerName}`, notes, taxAmount: parseFloat(taxAmount) || 0, discountAmount: parseFloat(discountAmount) || 0, status, paymentId, accounts: accounts.map((a) => ({ bankName: a.bankName, accountName: a.accountName, accountNumber: a.accountNumber, currency: a.currency, })), items: items.map((i) => ({ description: i.description || "Item", quantity: parseFloat(i.qty) || 0, unitPrice: parseFloat(i.price) || 0, total: Number( ((parseFloat(i.qty) || 0) * (parseFloat(i.price) || 0)).toFixed(2), ), })), customerId: customerId || undefined, }; try { setSubmitting(true); await api.paymentRequests.create({ body }); toast.success("Success", "Payment request created successfully!"); nav.back(); } catch (err: any) { console.error("[PaymentRequestCreate] Error:", err); toast.error("Error", err?.message || "Failed to create payment request"); } finally { setSubmitting(false); } }; return ( Issue Date setShowIssueDate(true)} className="h-11 px-3 border border-border rounded-[6px] flex-row items-center justify-between" style={{ backgroundColor: c.bg, borderColor: c.border }} > {issueDate} Due Date setShowDueDate(true)} className="h-11 px-3 border border-border rounded-[6px] flex-row items-center justify-between" style={{ backgroundColor: c.bg, borderColor: c.border }} > {dueDate || "Select Date"} Currency setShowCurrency(true)} className="h-11 px-3 border border-border rounded-[6px] flex-row items-center justify-between" style={{ backgroundColor: c.bg, borderColor: c.border }} > {currency} Status setShowStatus(true)} className="h-11 px-3 border border-border rounded-[6px] flex-row items-center justify-between" style={{ backgroundColor: c.bg, borderColor: c.border }} > {status} Add Item {items.map((item, index) => ( Item {index + 1} {items.length > 1 && ( removeItem(item.id)} hitSlop={8}> )} updateItem(item.id, "description", v)} /> updateItem(item.id, "qty", v)} flex={1} /> updateItem(item.id, "price", v)} flex={2} /> Total {currency} {( (parseFloat(item.qty) || 0) * (parseFloat(item.price) || 0) ).toFixed(2)} ))} Add Account {accounts.map((acc, index) => ( Account {index + 1} {accounts.length > 1 && ( removeAccount(acc.id)} hitSlop={8}> )} updateAccount(acc.id, "bankName", v)} placeholder="e.g. Yaltopia Bank" /> updateAccount(acc.id, "accountName", v)} placeholder="e.g. Yaltopia Tech PLC" /> updateAccount(acc.id, "accountNumber", v) } placeholder="123456789" flex={1} /> updateAccount(acc.id, "currency", v)} placeholder="ETB" flex={1} /> ))} Subtotal {currency} {subtotal.toLocaleString()} Total Amount {currency}{" "} {(amount ? Number(amount) : computedTotal).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2, })} setShowCurrency(false)} title="Select Currency" > {CURRENCIES.map((curr) => ( { setCurrency(v); setShowCurrency(false); }} /> ))} setShowStatus(false)} title="Select Status" > {STATUSES.map((s) => ( { setStatus(v); setShowStatus(false); }} /> ))} setShowIssueDate(false)} title="Select Issue Date" > { setIssueDate(v); setShowIssueDate(false); }} /> setShowDueDate(false)} title="Select Due Date" > { setDueDate(v); setShowDueDate(false); }} /> ); }