115 lines
5.9 KiB
TypeScript
115 lines
5.9 KiB
TypeScript
import React, { useState, useMemo } from "react";
|
|
import { View, Pressable, TextInput, useColorScheme, Modal, ScrollView } from "react-native";
|
|
import { useSirouRouter } from "@sirou/react-native";
|
|
import { AppRoutes } from "@/lib/routes";
|
|
import { Text } from "@/components/ui/text";
|
|
import { X, Search, FileText, ShieldCheck, Wallet, Receipt, Settings, User, HelpCircle, Briefcase, FolderOpen, BarChart3, DraftingCompass, Scan, Lock, Globe, History } from "@/lib/icons";
|
|
|
|
|
|
const ICON_COLOR = "#E46212";
|
|
|
|
interface Flow {
|
|
label: string;
|
|
keywords: string[];
|
|
route: string;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
const FLOWS: Flow[] = [
|
|
{ label: "Add Invoice", keywords: ["invoice", "create", "new", "bill"], route: "invoices/create", icon: <FileText size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Verify Payment", keywords: ["verify", "payment", "reference", "ft"], route: "verify-payment", icon: <ShieldCheck size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Create Payment", keywords: ["payment", "create", "new", "pay"], route: "payments/create", icon: <Wallet size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Add Receipt", keywords: ["receipt", "scan", "upload"], route: "add-receipt", icon: <Receipt size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Settings", keywords: ["settings", "preferences", "theme"], route: "settings", icon: <Settings size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Profile", keywords: ["profile", "account", "user"], route: "profile", icon: <User size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Help & Support", keywords: ["help", "support", "ticket"], route: "help", icon: <HelpCircle size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "FAQ", keywords: ["faq", "questions", "answers"], route: "faq", icon: <Globe size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Company", keywords: ["company", "business", "workers", "employees"], route: "company", icon: <Briefcase size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Documents", keywords: ["documents", "files", "docs"], route: "documents/index", icon: <FolderOpen size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Reports", keywords: ["reports", "analytics", "stats"], route: "reports/index", icon: <BarChart3 size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Scan Receipt", keywords: ["scan", "camera", "receipt", "ocr"], route: "(tabs)/scan", icon: <Scan size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Proforma", keywords: ["proforma", "estimate", "quote"], route: "(tabs)/proforma", icon: <DraftingCompass size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "News", keywords: ["news", "updates", "announcements"], route: "news/index", icon: <Globe size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Change PIN", keywords: ["pin", "password", "security", "change"], route: "set-pin", icon: <Lock size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Payment History", keywords: ["payments", "history", "transactions"], route: "history", icon: <History size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
{ label: "Invoices", keywords: ["invoices", "bills", "list"], route: "(tabs)", icon: <FileText size={18} color={ICON_COLOR} strokeWidth={2} /> },
|
|
];
|
|
|
|
interface CommandPaletteProps {
|
|
visible: boolean;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export function CommandPalette({ visible, onClose }: CommandPaletteProps) {
|
|
const nav = useSirouRouter<AppRoutes>();
|
|
const { colorScheme } = useColorScheme();
|
|
const isDark = colorScheme === "dark";
|
|
const [query, setQuery] = useState("");
|
|
|
|
const results = useMemo(() => {
|
|
if (!query.trim()) return FLOWS;
|
|
const q = query.toLowerCase();
|
|
return FLOWS.filter(
|
|
(f) =>
|
|
f.label.toLowerCase().includes(q) ||
|
|
f.keywords.some((k) => k.includes(q)),
|
|
);
|
|
}, [query]);
|
|
|
|
const handleSelect = (flow: Flow) => {
|
|
nav.go(flow.route as any);
|
|
onClose();
|
|
setQuery("");
|
|
};
|
|
|
|
return (
|
|
<Modal visible={visible} animationType="fade" transparent onRequestClose={onClose}>
|
|
<Pressable className="flex-1 bg-black/60 items-center justify-center" onPress={onClose}>
|
|
<Pressable
|
|
className="bg-background rounded-2xl w-[90%] max-h-[80%]"
|
|
onPress={() => {}}
|
|
>
|
|
<View className="flex-row items-center px-4 pt-4 pb-3 border-b border-border">
|
|
<Search size={18} color="#94a3b8" strokeWidth={2} />
|
|
<TextInput
|
|
className="flex-1 ml-3 text-foreground text-base py-2"
|
|
placeholder="Search flows..."
|
|
placeholderTextColor="#94a3b8"
|
|
value={query}
|
|
onChangeText={setQuery}
|
|
autoFocus
|
|
autoCapitalize="none"
|
|
/>
|
|
<Pressable onPress={onClose} className="h-8 w-8 rounded-lg items-center justify-center">
|
|
<X size={18} color={isDark ? "#f1f5f9" : "#0f172a"} strokeWidth={2} />
|
|
</Pressable>
|
|
</View>
|
|
<ScrollView className="px-3 pb-4 pt-2 max-h-[400px]">
|
|
{results.map((flow) => (
|
|
<Pressable
|
|
key={flow.route}
|
|
onPress={() => handleSelect(flow)}
|
|
className="flex-row items-center px-3 py-3 rounded-xl active:bg-card"
|
|
>
|
|
<View className="w-9 h-9 rounded-lg bg-primary/10 items-center justify-center mr-3">
|
|
{flow.icon}
|
|
</View>
|
|
<Text className="text-foreground font-sans-bold text-sm flex-1">
|
|
{flow.label}
|
|
</Text>
|
|
</Pressable>
|
|
))}
|
|
{results.length === 0 && (
|
|
<View className="py-8 items-center">
|
|
<Text variant="muted" className="text-sm">
|
|
No results found
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</ScrollView>
|
|
</Pressable>
|
|
</Pressable>
|
|
</Modal>
|
|
);
|
|
}
|