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}
);
}