import React, { useEffect, useMemo, useState } from "react";
import {
ActivityIndicator,
Pressable,
TextInput,
StyleSheet,
View,
} from "react-native";
import { useSirouRouter } from "@sirou/react-native";
import { useColorScheme } from "nativewind";
import { api } from "@/lib/api";
import { AppRoutes } from "@/lib/routes";
import { toast } from "@/lib/toast-store";
import { ScreenWrapper } from "@/components/ScreenWrapper";
import { FormFlow } from "@/components/FormFlow";
import { PickerModal, SelectOption } from "@/components/PickerModal";
import { CalendarGrid } from "@/components/CalendarGrid";
import { CustomerPicker } from "@/components/CustomerPicker";
import { Text } from "@/components/ui/text";
import { ChevronDown, Plus, Trash2 } from "@/lib/icons";
type Item = { id: number; description: string; quantity: string; unitPrice: 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 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,
keyboardType,
}: {
label: string;
value: string;
onChangeText: (v: string) => void;
placeholder: string;
numeric?: boolean;
center?: boolean;
flex?: number;
multiline?: boolean;
keyboardType?: "default" | "numeric" | "email-address" | "phone-pad";
}) {
const c = useInputColors();
return (
{label}
);
}
function PickerField({
label,
value,
onPress,
}: {
label: string;
value: string;
onPress: () => void;
}) {
const c = useInputColors();
return (
{label}
{value}
);
}
const CURRENCIES = ["ETB", "USD", "EUR", "GBP", "KES", "ZAR"];
const CHANNELS = ["EMAIL", "PHONE"];
const STEPS = [
{ key: "details", label: "Details" },
{ key: "customer", label: "Customer" },
{ key: "schedule", label: "Schedule" },
{ key: "items", label: "Items" },
{ key: "paymentMethod", label: "Payment" },
{ key: "summary", label: "Summary" },
];
export default function CreatePaymentRequestScreen() {
const nav = useSirouRouter();
const [step, setStep] = useState(0);
const [submitting, setSubmitting] = useState(false);
const [paymentRequestNumber, setPaymentRequestNumber] = useState("");
const [description, setDescription] = useState("");
const [customerName, setCustomerName] = useState("");
const [customerEmail, setCustomerEmail] = useState("");
const [customerId, setCustomerId] = useState("");
const [channel, setChannel] = useState("EMAIL");
const [recipient, setRecipient] = useState("");
const [amount, setAmount] = useState("");
const [currency, setCurrency] = useState("ETB");
const [issueDate, setIssueDate] = useState(
new Date().toISOString().split("T")[0],
);
const [dueDate, setDueDate] = useState("");
const [companyPaymentMethodId, setCompanyPaymentMethodId] = useState("");
const [paymentMethods, setPaymentMethods] = useState([]);
const [loadingPaymentMethods, setLoadingPaymentMethods] = useState(false);
const [items, setItems] = useState- ([
{ id: 1, description: "", quantity: "1", unitPrice: "" },
]);
const c = useInputColors();
const [showCurrency, setShowCurrency] = useState(false);
const [showIssueDate, setShowIssueDate] = useState(false);
const [showDueDate, setShowDueDate] = useState(false);
const [showChannel, setShowChannel] = useState(false);
const [showPaymentMethod, setShowPaymentMethod] = 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]);
}, []);
useEffect(() => {
(async () => {
setLoadingPaymentMethods(true);
try {
const res = await api.company.paymentMethods();
const list = Array.isArray(res) ? res : res?.data || [];
setPaymentMethods(list);
} catch {
setPaymentMethods([]);
} finally {
setLoadingPaymentMethods(false);
}
})();
}, []);
useEffect(() => {
if (channel === "EMAIL" && customerEmail && !recipient) {
setRecipient(customerEmail);
}
}, [channel, customerEmail]);
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: "", quantity: "1", unitPrice: "" },
]);
const removeItem = (id: number) => {
if (items.length > 1) setItems((prev) => prev.filter((it) => it.id !== id));
};
const subtotal = useMemo(
() =>
items.reduce(
(sum, item) =>
sum +
(parseFloat(item.quantity) || 0) * (parseFloat(item.unitPrice) || 0),
0,
),
[items],
);
const handleNext = () => {
if (step === 0 && !paymentRequestNumber.trim()) {
toast.error("Validation", "Payment request number is required");
return;
}
if (step === 1 && !customerName.trim()) {
toast.error("Validation", "Customer name is required");
return;
}
setStep(step + 1);
};
const handleSubmit = async () => {
if (!customerName) {
toast.error("Validation", "Please select a customer");
return;
}
const body = {
paymentRequestNumber,
customerName,
customerEmail: customerEmail || undefined,
channel,
recipient,
amount: amount ? Number(amount) : Number(subtotal.toFixed(2)),
currency,
issueDate: new Date(issueDate).toISOString(),
dueDate: new Date(dueDate).toISOString(),
companyPaymentMethodId: companyPaymentMethodId || undefined,
customerId: customerId || undefined,
...(description ? { description } : {}),
items: items.map((i) => ({
description: i.description || "Item",
quantity: parseFloat(i.quantity) || 0,
unitPrice: parseFloat(i.unitPrice) || 0,
total: Number(
((parseFloat(i.quantity) || 0) * (parseFloat(i.unitPrice) || 0)).toFixed(2),
),
})),
};
try {
setSubmitting(true);
await api.paymentRequests.create({
body,
headers: {
"Content-Type": "application/json",
},
});
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);
}
};
const paymentMethodLabel = companyPaymentMethodId
? paymentMethods.find((pm: any) => pm.id === companyPaymentMethodId)
?.label || paymentMethods.find((pm: any) => pm.id === companyPaymentMethodId)
?.providerName || "Selected"
: "Select";
return (
setStep(step - 1)}
onComplete={handleSubmit}
loading={submitting}
completeLabel="Create Request"
>
{step === 0 && (
Request Details
)}
{step === 1 && (
Customer Information
Customer
{
setCustomerId(ids[0] || "");
setCustomerName(customers[0]?.name || "");
setCustomerEmail(customers[0]?.email || "");
}}
placeholder="Select a customer"
/>
setShowChannel(true)}
/>
)}
{step === 2 && (
Schedule & Currency
setShowIssueDate(true)}
/>
setShowDueDate(true)}
/>
setShowCurrency(true)}
/>
0 ? `${currency} ${subtotal.toFixed(2)}` : "Enter amount"}
numeric
/>
)}
{step === 3 && (
Items
Add
{items.map((item, index) => (
Item {index + 1}
removeItem(item.id)} hitSlop={8}>
updateItem(item.id, "description", v)}
/>
updateItem(item.id, "quantity", v)}
flex={1}
/>
updateItem(item.id, "unitPrice", v)}
flex={3}
/>
{parseFloat(item.quantity) > 0 && parseFloat(item.unitPrice) > 0 && (
= {currency}{" "}
{(
(parseFloat(item.quantity) || 0) *
(parseFloat(item.unitPrice) || 0)
).toFixed(2)}
)}
))}
{subtotal > 0 && (
Subtotal
{currency} {subtotal.toFixed(2)}
)}
)}
{step === 4 && (
Payment Method
Company Payment Method
{
if (paymentMethods.length > 0) {
setShowPaymentMethod(true);
} else {
toast.error(
"No Methods",
"No payment methods available. Please configure one in company settings.",
);
}
}}
className="h-11 px-3 border border-border rounded-[6px] flex-row items-center justify-between"
style={{ backgroundColor: c.bg, borderColor: c.border }}
>
{loadingPaymentMethods ? (
) : (
<>
{paymentMethodLabel}
>
)}
)}
{step === 5 && (
Summary
{description ? (
) : null}
{items.length > 0 && (
Items ({items.length})
{items.map((item, i) => (
{item.description || `Item ${i + 1}`}
{item.quantity} × {currency}{" "}
{parseFloat(item.unitPrice || "0").toFixed(2)}
))}
)}
{paymentMethodLabel !== "Select" && (
)}
Total Amount
{currency}{" "}
{(amount ? Number(amount) : subtotal).toLocaleString(
"en-US",
{ minimumFractionDigits: 2, maximumFractionDigits: 2 },
)}
)}
setShowCurrency(false)}
title="Select Currency"
>
{CURRENCIES.map((curr) => (
{
setCurrency(v);
setShowCurrency(false);
}}
/>
))}
setShowChannel(false)}
title="Invite Channel"
>
{CHANNELS.map((ch) => (
{
setChannel(v);
setShowChannel(false);
}}
/>
))}
setShowPaymentMethod(false)}
title="Payment Method"
>
{paymentMethods.map((pm: any) => (
{
setCompanyPaymentMethodId(v);
setShowPaymentMethod(false);
}}
/>
))}
setShowIssueDate(false)}
title="Select Issue Date"
>
{
setIssueDate(v);
setShowIssueDate(false);
}}
/>
setShowDueDate(false)}
title="Select Due Date"
>
{
setDueDate(v);
setShowDueDate(false);
}}
/>
);
}
function SummaryRow({
label,
value,
multiline,
}: {
label: string;
value: string;
multiline?: boolean;
}) {
return (
{label}
{value}
);
}