762 lines
27 KiB
TypeScript
762 lines
27 KiB
TypeScript
import React, { useState, useCallback } from "react";
|
||
import {
|
||
View,
|
||
ScrollView,
|
||
ActivityIndicator,
|
||
Linking,
|
||
Pressable,
|
||
Modal,
|
||
Dimensions,
|
||
StyleSheet,
|
||
Image,
|
||
} from "react-native";
|
||
import { useSirouRouter } from "@sirou/react-native";
|
||
import { AppRoutes } from "@/lib/routes";
|
||
import { Stack, useLocalSearchParams, useFocusEffect } from "expo-router";
|
||
import { Text } from "@/components/ui/text";
|
||
import { EmptyState } from "@/components/EmptyState";
|
||
import {
|
||
FileText,
|
||
Calendar,
|
||
Clock,
|
||
User,
|
||
Hash,
|
||
AlertCircle,
|
||
Edit,
|
||
Mail,
|
||
MessageSquare,
|
||
MoreVertical,
|
||
X,
|
||
Package,
|
||
Share2,
|
||
Download,
|
||
TrendingUp,
|
||
TrendingDown,
|
||
Check,
|
||
Trash2,
|
||
} from "@/lib/icons";
|
||
import { ScreenWrapper } from "@/components/ScreenWrapper";
|
||
import { StandardHeader } from "@/components/StandardHeader";
|
||
import { api, BASE_URL } from "@/lib/api";
|
||
import { toast } from "@/lib/toast-store";
|
||
import { useAuthStore } from "@/lib/auth-store";
|
||
import { useColorScheme } from "nativewind";
|
||
import { ActionModal } from "@/components/ActionModal";
|
||
import { SendHorizonal } from "lucide-react-native";
|
||
import ticketImage from "@/assets/ticket.png";
|
||
|
||
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
|
||
|
||
function safeVal(v: any): number {
|
||
if (v == null) return 0;
|
||
if (typeof v === "object") return Number(v.value) || 0;
|
||
return Number(v) || 0;
|
||
}
|
||
|
||
function fmt(v: number, currency = "ETB") {
|
||
return `${currency} ${v.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
||
}
|
||
|
||
const STATUS_THEME: Record<
|
||
string,
|
||
{ label: string; bg: string; text: string; dot: string }
|
||
> = {
|
||
PAID: {
|
||
label: "Paid",
|
||
bg: "bg-emerald-500/10",
|
||
text: "text-emerald-600",
|
||
dot: "bg-emerald-500",
|
||
},
|
||
PENDING: {
|
||
label: "Pending",
|
||
bg: "bg-amber-500/10",
|
||
text: "text-amber-600",
|
||
dot: "bg-amber-500",
|
||
},
|
||
DRAFT: {
|
||
label: "Draft",
|
||
bg: "bg-blue-500/10",
|
||
text: "text-blue-600",
|
||
dot: "bg-blue-500",
|
||
},
|
||
CANCELLED: {
|
||
label: "Cancelled",
|
||
bg: "bg-slate-500/10",
|
||
text: "text-slate-600",
|
||
dot: "bg-slate-500",
|
||
},
|
||
DEFAULT: {
|
||
label: "Unknown",
|
||
bg: "bg-slate-500/10",
|
||
text: "text-slate-500",
|
||
dot: "bg-slate-500",
|
||
},
|
||
};
|
||
|
||
export default function ProformaDetailScreen() {
|
||
const nav = useSirouRouter<AppRoutes>();
|
||
const { id } = useLocalSearchParams();
|
||
const { colorScheme } = useColorScheme();
|
||
const isDark = colorScheme === "dark";
|
||
|
||
const [loading, setLoading] = useState(true);
|
||
const [proforma, setProforma] = useState<any>(null);
|
||
const [activeTab, setActiveTab] = useState<"details" | "items">("details");
|
||
const [showMoreSheet, setShowMoreSheet] = useState(false);
|
||
const [showSendSheet, setShowSendSheet] = useState(false);
|
||
const [sharing, setSharing] = useState(false);
|
||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||
const [deleting, setDeleting] = useState(false);
|
||
|
||
useFocusEffect(
|
||
useCallback(() => {
|
||
fetchProforma();
|
||
}, [id]),
|
||
);
|
||
|
||
const fetchProforma = async () => {
|
||
try {
|
||
setLoading(true);
|
||
const pid = Array.isArray(id) ? id[0] : id;
|
||
if (!pid) return;
|
||
const data = await api.proforma.getById({ params: { id: pid } });
|
||
setProforma(data);
|
||
} catch {
|
||
toast.error("Error", "Failed to load proforma details");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleGetPdf = async () => {
|
||
try {
|
||
const { token } = useAuthStore.getState();
|
||
const pid = Array.isArray(id) ? id[0] : id;
|
||
await Linking.openURL(`${BASE_URL}proforma/${pid}/pdf?token=${token}`);
|
||
} catch {
|
||
toast.error("Error", "Failed to open PDF");
|
||
}
|
||
};
|
||
|
||
const handleShare = async (channel: "email" | "sms") => {
|
||
try {
|
||
setSharing(true);
|
||
const pid = Array.isArray(id) ? id[0] : id;
|
||
await api.proforma.shareLink({ body: { proformaId: pid, channel } });
|
||
toast.success(
|
||
"Sent",
|
||
`Proforma shared via ${channel === "email" ? "email" : "SMS"}`,
|
||
);
|
||
setShowSendSheet(false);
|
||
} catch (err: any) {
|
||
toast.error("Error", err?.message || "Failed to share proforma");
|
||
} finally {
|
||
setSharing(false);
|
||
}
|
||
};
|
||
|
||
const handleDelete = () => setShowDeleteModal(true);
|
||
|
||
const confirmDelete = async () => {
|
||
try {
|
||
setDeleting(true);
|
||
const pid = Array.isArray(id) ? id[0] : id;
|
||
await api.proforma.delete({ params: { id: pid } });
|
||
toast.success("Deleted", "Proforma has been removed.");
|
||
setShowDeleteModal(false);
|
||
nav.back();
|
||
} catch (err: any) {
|
||
toast.error("Error", err?.message || "Failed to delete proforma");
|
||
} finally {
|
||
setDeleting(false);
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
<StandardHeader title="Proforma" showBack />
|
||
<View className="flex-1 items-center justify-center">
|
||
<ActivityIndicator color="#E46212" size="large" />
|
||
</View>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|
||
|
||
if (!proforma) {
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
<StandardHeader title="Proforma" showBack />
|
||
<View className="flex-1 items-center justify-center px-8">
|
||
<AlertCircle size={48} color="#ef4444" className="mb-4" />
|
||
<Text className="text-muted-foreground text-center font-sans-medium">
|
||
Proforma not found.
|
||
</Text>
|
||
</View>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|
||
|
||
const currency = proforma.currency || "ETB";
|
||
const amount = safeVal(proforma.amount);
|
||
const tax = safeVal(proforma.taxAmount);
|
||
const discount = safeVal(proforma.discountAmount);
|
||
const subtotal = amount + discount - tax;
|
||
const items: any[] = proforma.items || [];
|
||
|
||
const statusKey = (proforma.status || "DRAFT").toUpperCase();
|
||
const theme = STATUS_THEME[statusKey] || STATUS_THEME.DEFAULT;
|
||
|
||
const issueDate = proforma.issueDate ? new Date(proforma.issueDate) : null;
|
||
const dueDate = proforma.dueDate ? new Date(proforma.dueDate) : null;
|
||
|
||
const formatLongDate = (d: Date) =>
|
||
d.toLocaleDateString("en-US", {
|
||
day: "numeric",
|
||
month: "short",
|
||
year: "numeric",
|
||
});
|
||
|
||
const customerName = (
|
||
proforma.customerName?.replace("Customer Name: ", "") || "Walking Client"
|
||
).trim();
|
||
|
||
const ActionOption = ({
|
||
icon,
|
||
label,
|
||
description,
|
||
onPress,
|
||
destructive,
|
||
}: {
|
||
icon: React.ReactNode;
|
||
label: string;
|
||
description: string;
|
||
onPress?: () => void;
|
||
destructive?: boolean;
|
||
}) => (
|
||
<Pressable
|
||
onPress={onPress}
|
||
className="flex-row items-center gap-3.5 p-4 mb-2 rounded-[6px] border border-border bg-card"
|
||
>
|
||
<View className="h-9 w-9 rounded-full bg-primary/10 items-center justify-center">
|
||
{icon}
|
||
</View>
|
||
<View className="flex-1">
|
||
<Text
|
||
className={`text-[14px] font-sans-bold ${
|
||
destructive ? "text-red-500" : "text-foreground"
|
||
}`}
|
||
>
|
||
{label}
|
||
</Text>
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium mt-0.5">
|
||
{description}
|
||
</Text>
|
||
</View>
|
||
</Pressable>
|
||
);
|
||
|
||
return (
|
||
<ScreenWrapper className="bg-background">
|
||
<Stack.Screen options={{ headerShown: false }} />
|
||
<StandardHeader
|
||
title="Proforma Details"
|
||
showBack
|
||
right={
|
||
<Pressable
|
||
onPress={() => setShowMoreSheet(true)}
|
||
className="h-10 w-10 rounded-[10px] bg-card items-center justify-center border border-border"
|
||
>
|
||
<MoreVertical color={isDark ? "#f1f5f9" : "#0f172a"} size={18} />
|
||
</Pressable>
|
||
}
|
||
/>
|
||
|
||
<ScrollView
|
||
className="flex-1"
|
||
contentContainerStyle={{ paddingBottom: 24 }}
|
||
showsVerticalScrollIndicator={false}
|
||
>
|
||
{/* Hero Card — illustration overflows the top */}
|
||
<View className="px-5 pt-3">
|
||
<View
|
||
className="items-center"
|
||
style={{ marginBottom: -60, zIndex: 2 }}
|
||
>
|
||
<Image
|
||
source={ticketImage}
|
||
style={{ width: 150, height: 150 }}
|
||
resizeMode="contain"
|
||
/>
|
||
</View>
|
||
|
||
<View
|
||
className="rounded-[14px] pt-14 pb-6 px-6 items-center bg-primary/5"
|
||
style={{
|
||
borderWidth: 1,
|
||
borderColor: isDark
|
||
? "rgba(255,255,255,0.06)"
|
||
: "rgba(0,0,0,0.05)",
|
||
}}
|
||
>
|
||
<Text className="text-foreground text-[34px] font-sans-black tracking-tight leading-tight">
|
||
{amount.toLocaleString("en-US", {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2,
|
||
})}{" "}
|
||
<Text className="text-foreground text-[20px] font-sans-bold">
|
||
{currency}
|
||
</Text>
|
||
</Text>
|
||
<Text className="text-muted-foreground text-[11px] font-sans-medium mt-1">
|
||
{proforma.proformaNumber
|
||
? `Proforma ${proforma.proformaNumber}`
|
||
: `Proforma #${(proforma.id || "").slice(0, 8).toUpperCase()}`}
|
||
</Text>
|
||
|
||
{/* Status badge */}
|
||
<View className={`px-2.5 py-1 rounded-[4px] mt-4 ${theme.bg}`}>
|
||
<View className="flex-row items-center gap-1.5">
|
||
<View className={`w-1.5 h-1.5 rounded-full ${theme.dot}`} />
|
||
<Text
|
||
className={`text-[9px] font-sans-bold uppercase tracking-widest ${theme.text}`}
|
||
>
|
||
{theme.label}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View className="w-full mt-5 gap-3">
|
||
<View className="flex-row justify-between items-center">
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium">
|
||
Issued
|
||
</Text>
|
||
<Text className="text-foreground text-[12px] font-sans-bold">
|
||
{issueDate ? formatLongDate(issueDate) : "—"}
|
||
</Text>
|
||
</View>
|
||
<View className="flex-row justify-between items-center">
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium">
|
||
Due
|
||
</Text>
|
||
<Text className="text-foreground text-[12px] font-sans-bold">
|
||
{dueDate ? formatLongDate(dueDate) : "—"}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
|
||
{/* Tabs */}
|
||
<View className="px-5 pt-12">
|
||
<View className="flex-row gap-6 border-b border-border">
|
||
<Pressable
|
||
onPress={() => setActiveTab("details")}
|
||
className="pb-2.5"
|
||
>
|
||
<Text
|
||
className={`text-[14px] font-sans-bold ${
|
||
activeTab === "details"
|
||
? "text-foreground"
|
||
: "text-muted-foreground"
|
||
}`}
|
||
>
|
||
Details
|
||
</Text>
|
||
{activeTab === "details" && (
|
||
<View className="absolute -bottom-px left-0 right-0 h-0.5 bg-foreground" />
|
||
)}
|
||
</Pressable>
|
||
<Pressable onPress={() => setActiveTab("items")} className="pb-2.5">
|
||
<Text
|
||
className={`text-[14px] font-sans-bold ${
|
||
activeTab === "items"
|
||
? "text-foreground"
|
||
: "text-muted-foreground"
|
||
}`}
|
||
>
|
||
Items
|
||
</Text>
|
||
{activeTab === "items" && (
|
||
<View className="absolute -bottom-px left-0 right-0 h-0.5 bg-foreground" />
|
||
)}
|
||
</Pressable>
|
||
</View>
|
||
</View>
|
||
|
||
{/* Tab content */}
|
||
{activeTab === "details" ? (
|
||
<View className="px-5 pt-5 gap-6">
|
||
{/* Customer */}
|
||
{proforma.customerName && (
|
||
<View>
|
||
<Text className="font-sans-bold text-xs uppercase tracking-widest text-muted-foreground mb-3">
|
||
Customer
|
||
</Text>
|
||
<View className="flex-row items-center gap-3">
|
||
<View className="h-10 w-10 rounded-full bg-primary/10 items-center justify-center">
|
||
<User color="#E46212" size={18} strokeWidth={2} />
|
||
</View>
|
||
<View className="flex-1">
|
||
<Text className="text-foreground font-sans-bold text-sm">
|
||
{customerName}
|
||
</Text>
|
||
{(proforma.customerEmail || proforma.customerPhone) && (
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium mt-0.5">
|
||
{proforma.customerEmail || proforma.customerPhone}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
)}
|
||
|
||
{/* Proforma Details */}
|
||
<View>
|
||
<Text className="font-sans-bold text-xs uppercase tracking-widest text-muted-foreground mb-3">
|
||
Proforma Details
|
||
</Text>
|
||
<View className="gap-4">
|
||
<View className="flex-row items-center gap-3">
|
||
<Hash size={15} color="#64748b" />
|
||
<View className="flex-1">
|
||
<Text className="text-[10px] uppercase tracking-wider text-muted-foreground font-sans-semibold">
|
||
Proforma Number
|
||
</Text>
|
||
<Text className="text-foreground font-sans-bold text-sm">
|
||
{proforma.proformaNumber ||
|
||
`PRF${(proforma.id || "").slice(0, 8).toUpperCase()}`}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View className="flex-row items-center gap-3">
|
||
<Calendar size={15} color="#64748b" />
|
||
<View className="flex-1">
|
||
<Text className="text-[10px] uppercase tracking-wider text-muted-foreground font-sans-semibold">
|
||
Issue Date
|
||
</Text>
|
||
<Text className="text-foreground font-sans-bold text-sm">
|
||
{issueDate ? issueDate.toLocaleString() : "—"}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View className="flex-row items-center gap-3">
|
||
<Clock size={15} color="#64748b" />
|
||
<View className="flex-1">
|
||
<Text className="text-[10px] uppercase tracking-wider text-muted-foreground font-sans-semibold">
|
||
Due Date
|
||
</Text>
|
||
<Text className="text-foreground font-sans-bold text-sm">
|
||
{dueDate ? dueDate.toLocaleString() : "—"}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
{proforma.description && (
|
||
<View className="flex-row items-start gap-3">
|
||
<FileText
|
||
size={15}
|
||
color="#64748b"
|
||
style={{ marginTop: 2 }}
|
||
/>
|
||
<View className="flex-1">
|
||
<Text className="text-[10px] uppercase tracking-wider text-muted-foreground font-sans-semibold">
|
||
Description
|
||
</Text>
|
||
<Text className="text-foreground font-sans-medium text-sm leading-5">
|
||
{proforma.description}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</View>
|
||
|
||
{/* Note (border only, no bg) */}
|
||
{proforma.notes && (
|
||
<View>
|
||
<Text className="text-foreground text-sm font-sans-bold mb-2">
|
||
Note
|
||
</Text>
|
||
<View className="rounded-[10px] border border-border p-4">
|
||
<Text className="text-foreground font-sans-medium text-[13px] leading-5">
|
||
{proforma.notes}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
) : (
|
||
<View className="px-5 pt-5">
|
||
{items.length > 0 ? (
|
||
<View>
|
||
<Text className="text-foreground text-[16px] font-sans-black tracking-tight mb-3">
|
||
Items
|
||
</Text>
|
||
<View>
|
||
{items.map((item: any, idx: number) => {
|
||
const qty = safeVal(item.quantity || 1);
|
||
const unitPrice = safeVal(item.unitPrice);
|
||
const lineTotal = safeVal(item.total || qty * unitPrice);
|
||
return (
|
||
<View
|
||
key={item.id || idx}
|
||
className={`flex-row items-center gap-3 py-3 ${
|
||
idx < items.length - 1 ? "border-b border-border" : ""
|
||
}`}
|
||
>
|
||
<View className="h-12 w-12 rounded-[8px] bg-muted items-center justify-center overflow-hidden">
|
||
<Package
|
||
size={20}
|
||
color="#94a3b8"
|
||
strokeWidth={1.5}
|
||
/>
|
||
</View>
|
||
<View className="flex-1">
|
||
<Text
|
||
className="text-foreground text-[14px] font-sans-bold"
|
||
numberOfLines={1}
|
||
>
|
||
{item.description || "No item"}
|
||
</Text>
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium mt-0.5">
|
||
{qty} ×{" "}
|
||
{unitPrice.toLocaleString("en-US", {
|
||
minimumFractionDigits: 2,
|
||
})}{" "}
|
||
{currency}
|
||
</Text>
|
||
</View>
|
||
<Text className="text-foreground text-[14px] font-sans-bold">
|
||
{lineTotal.toLocaleString("en-US", {
|
||
minimumFractionDigits: 2,
|
||
})}{" "}
|
||
{currency}
|
||
</Text>
|
||
</View>
|
||
);
|
||
})}
|
||
</View>
|
||
|
||
{/* Summary */}
|
||
<View className="mt-6 gap-2.5">
|
||
<View className="flex-row justify-between items-center">
|
||
<Text className="text-muted-foreground text-[14px] font-sans-medium">
|
||
Subtotal
|
||
</Text>
|
||
<Text className="text-foreground text-[14px] font-sans-bold">
|
||
{fmt(subtotal, currency)}
|
||
</Text>
|
||
</View>
|
||
{tax > 0 && (
|
||
<View className="flex-row justify-between items-center">
|
||
<Text className="text-muted-foreground text-[14px] font-sans-medium">
|
||
Tax
|
||
</Text>
|
||
<Text className="text-foreground text-[14px] font-sans-bold">
|
||
+{fmt(tax, currency)}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{discount > 0 && (
|
||
<View className="flex-row justify-between items-center">
|
||
<Text className="text-muted-foreground text-[14px] font-sans-medium">
|
||
Discount
|
||
</Text>
|
||
<Text className="text-foreground text-[14px] font-sans-bold">
|
||
-{fmt(discount, currency)}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
<View className="border-t border-border/60 pt-2.5 flex-row justify-between items-center">
|
||
<Text className="text-foreground font-sans-black text-base">
|
||
Total
|
||
</Text>
|
||
<Text className="text-primary font-sans-black text-base">
|
||
{fmt(amount, currency)}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
</View>
|
||
) : (
|
||
<EmptyState
|
||
title="No items yet"
|
||
description="This proforma doesn't have any items yet."
|
||
/>
|
||
)}
|
||
</View>
|
||
)}
|
||
</ScrollView>
|
||
|
||
{/* Sticky bottom bar — Send + Download PDF (like invoice detail) */}
|
||
<View
|
||
className="flex-row gap-3 px-5 py-3 border-t border-border"
|
||
style={{ backgroundColor: isDark ? "#0a0505" : "#ffffff" }}
|
||
>
|
||
<Pressable
|
||
onPress={() => setShowSendSheet(true)}
|
||
className="flex-1 h-12 rounded-[8px] border border-border items-center justify-center flex-row gap-2 bg-card"
|
||
>
|
||
<SendHorizonal color="#0f172a" size={16} strokeWidth={2.5} />
|
||
<Text className="text-foreground text-[13px] font-sans-bold">
|
||
Send
|
||
</Text>
|
||
</Pressable>
|
||
<Pressable
|
||
onPress={handleGetPdf}
|
||
className="flex-1 h-12 rounded-[8px] border border-border items-center justify-center flex-row gap-2 bg-card"
|
||
>
|
||
<Download color="#0f172a" size={16} strokeWidth={2.5} />
|
||
<Text className="text-foreground text-[13px] font-sans-bold">
|
||
Download PDF
|
||
</Text>
|
||
</Pressable>
|
||
</View>
|
||
|
||
{/* More bottom sheet */}
|
||
<Modal
|
||
visible={showMoreSheet}
|
||
transparent
|
||
animationType="slide"
|
||
onRequestClose={() => setShowMoreSheet(false)}
|
||
>
|
||
<Pressable
|
||
className="flex-1 bg-black/40"
|
||
onPress={() => setShowMoreSheet(false)}
|
||
>
|
||
<View className="flex-1 justify-end">
|
||
<Pressable
|
||
className="bg-card rounded-t-[36px] overflow-hidden border-t-[3px] border-border/20"
|
||
style={{ maxHeight: SCREEN_HEIGHT * 0.5 }}
|
||
onPress={(e) => e.stopPropagation()}
|
||
>
|
||
<View className="px-6 pb-4 pt-4 flex-row justify-between items-center">
|
||
<View className="w-10" />
|
||
<Text className="text-foreground font-sans-bold text-[18px]">
|
||
Proforma
|
||
</Text>
|
||
<Pressable
|
||
onPress={() => setShowMoreSheet(false)}
|
||
className="h-8 w-8 bg-secondary/80 rounded-full items-center justify-center border border-border/10"
|
||
>
|
||
<X
|
||
size={14}
|
||
color={isDark ? "#f1f5f9" : "#0f172a"}
|
||
strokeWidth={2.5}
|
||
/>
|
||
</Pressable>
|
||
</View>
|
||
|
||
<ScrollView
|
||
className="px-5"
|
||
showsVerticalScrollIndicator={false}
|
||
contentContainerStyle={{ paddingBottom: 40 }}
|
||
>
|
||
<ActionOption
|
||
icon={<Edit color="#E46212" size={18} strokeWidth={2} />}
|
||
label="Edit Proforma"
|
||
description="Update details, items, or dates"
|
||
onPress={() => {
|
||
setShowMoreSheet(false);
|
||
nav.go("proforma/edit", { id: proforma.id });
|
||
}}
|
||
/>
|
||
<ActionOption
|
||
icon={<Trash2 color="#ef4444" size={18} strokeWidth={2} />}
|
||
label="Delete Proforma"
|
||
description="Permanently remove this record"
|
||
onPress={() => {
|
||
setShowMoreSheet(false);
|
||
handleDelete();
|
||
}}
|
||
destructive
|
||
/>
|
||
</ScrollView>
|
||
</Pressable>
|
||
</View>
|
||
</Pressable>
|
||
</Modal>
|
||
|
||
{/* Send bottom sheet (Email / SMS) */}
|
||
<Modal
|
||
visible={showSendSheet}
|
||
transparent
|
||
animationType="slide"
|
||
onRequestClose={() => setShowSendSheet(false)}
|
||
>
|
||
<Pressable
|
||
className="flex-1 bg-black/40"
|
||
onPress={() => setShowSendSheet(false)}
|
||
>
|
||
<View className="flex-1 justify-end">
|
||
<Pressable
|
||
className="bg-card rounded-t-[36px] overflow-hidden border-t-[3px] border-border/20"
|
||
style={{ maxHeight: SCREEN_HEIGHT * 0.5 }}
|
||
onPress={(e) => e.stopPropagation()}
|
||
>
|
||
<View className="px-6 pb-4 pt-4 flex-row justify-between items-center">
|
||
<View className="w-10" />
|
||
<Text className="text-foreground font-sans-bold text-[18px]">
|
||
Send Proforma
|
||
</Text>
|
||
<Pressable
|
||
onPress={() => setShowSendSheet(false)}
|
||
className="h-8 w-8 bg-secondary/80 rounded-full items-center justify-center border border-border/10"
|
||
>
|
||
<X
|
||
size={14}
|
||
color={isDark ? "#f1f5f9" : "#0f172a"}
|
||
strokeWidth={2.5}
|
||
/>
|
||
</Pressable>
|
||
</View>
|
||
|
||
<ScrollView
|
||
className="px-5"
|
||
showsVerticalScrollIndicator={false}
|
||
contentContainerStyle={{ paddingBottom: 40 }}
|
||
>
|
||
<Text className="text-muted-foreground text-[12px] font-sans-medium mb-4">
|
||
Send a public, shortened link via yaltopia.com to your
|
||
customer's email or phone.
|
||
</Text>
|
||
<ActionOption
|
||
icon={<Mail color="#E46212" size={18} strokeWidth={2} />}
|
||
label="Send as Email"
|
||
description="Public accessible shortened link via yaltopia.com"
|
||
onPress={() => handleShare("email")}
|
||
/>
|
||
<ActionOption
|
||
icon={
|
||
<MessageSquare color="#E46212" size={18} strokeWidth={2} />
|
||
}
|
||
label="Send as SMS"
|
||
description="Public accessible shortened link via yaltopia.com"
|
||
onPress={() => handleShare("sms")}
|
||
/>
|
||
</ScrollView>
|
||
</Pressable>
|
||
</View>
|
||
</Pressable>
|
||
</Modal>
|
||
|
||
<ActionModal
|
||
visible={showDeleteModal}
|
||
onClose={() => setShowDeleteModal(false)}
|
||
onConfirm={confirmDelete}
|
||
title="Delete Proforma"
|
||
description="Are you sure you want to permanently delete this proforma? This action cannot be reversed."
|
||
confirmText="Delete"
|
||
confirmVariant="destructive"
|
||
icon={Trash2}
|
||
iconColor="#ef4444"
|
||
loading={deleting}
|
||
/>
|
||
</ScreenWrapper>
|
||
);
|
||
}
|