From de9bf86d94c0aaf368cb2fefab0c4391be0cdae8 Mon Sep 17 00:00:00 2001 From: Marcio Bevervanso Date: Mon, 26 Jan 2026 11:20:25 -0300 Subject: [PATCH] Initial commit --- .env | 2 + .gitignore | 24 + App.tsx | 147 ++ README.md | 20 + components/ErrorBoundary.tsx | 51 + components/Logo.tsx | 34 + components/MarketplaceAnalytic.tsx | 156 ++ context/CRMContext.tsx | 849 ++++++++ context/ThemeContext.tsx | 49 + index.css | 164 ++ index.html | 83 + index.tsx | 16 + layouts/Header.tsx | 105 + layouts/Sidebar.tsx | 109 + layouts/TopBar.tsx | 175 ++ metadata.json | 5 + migration.sql | 45 + n8n-scraping-workflow.json | 125 ++ package-lock.json | 3255 ++++++++++++++++++++++++++++ package.json | 29 + pages/Customers.tsx | 167 ++ pages/Dashboard.tsx | 164 ++ pages/Financial.tsx | 287 +++ pages/Inventory.tsx | 58 + pages/Login.tsx | 106 + pages/Orders.tsx | 270 +++ pages/Products.tsx | 261 +++ pages/Reports.tsx | 306 +++ pages/Sales.tsx | 558 +++++ pages/Settings.tsx | 452 ++++ pages/Sourcing.tsx | 234 ++ pages/Suppliers.tsx | 143 ++ pages/Users.tsx | 167 ++ paraguai.rar | Bin 0 -> 92690 bytes policies.sql | 26 + services/geminiService.ts | 314 +++ services/supabase.ts | 11 + supabase_schema.sql | 126 ++ tsconfig.json | 29 + types.ts | 167 ++ vite.config.ts | 23 + 41 files changed, 9312 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 App.tsx create mode 100644 README.md create mode 100644 components/ErrorBoundary.tsx create mode 100644 components/Logo.tsx create mode 100644 components/MarketplaceAnalytic.tsx create mode 100644 context/CRMContext.tsx create mode 100644 context/ThemeContext.tsx create mode 100644 index.css create mode 100644 index.html create mode 100644 index.tsx create mode 100644 layouts/Header.tsx create mode 100644 layouts/Sidebar.tsx create mode 100644 layouts/TopBar.tsx create mode 100644 metadata.json create mode 100644 migration.sql create mode 100644 n8n-scraping-workflow.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pages/Customers.tsx create mode 100644 pages/Dashboard.tsx create mode 100644 pages/Financial.tsx create mode 100644 pages/Inventory.tsx create mode 100644 pages/Login.tsx create mode 100644 pages/Orders.tsx create mode 100644 pages/Products.tsx create mode 100644 pages/Reports.tsx create mode 100644 pages/Sales.tsx create mode 100644 pages/Settings.tsx create mode 100644 pages/Sourcing.tsx create mode 100644 pages/Suppliers.tsx create mode 100644 pages/Users.tsx create mode 100644 paraguai.rar create mode 100644 policies.sql create mode 100644 services/geminiService.ts create mode 100644 services/supabase.ts create mode 100644 supabase_schema.sql create mode 100644 tsconfig.json create mode 100644 types.ts create mode 100644 vite.config.ts diff --git a/.env b/.env new file mode 100644 index 0000000..0a80675 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +VITE_SUPABASE_URL=https://cnattjitonpejcviwbdg.supabase.co +VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNuYXR0aml0b25wZWpjdml3YmRnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjgzOTY2MDMsImV4cCI6MjA4Mzk3MjYwM30.cLlDq2NowgeN-IN55E5Bq0ZM035DS6Vs4ICvFxMNSG8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/App.tsx b/App.tsx new file mode 100644 index 0000000..6fcee23 --- /dev/null +++ b/App.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom'; +import TopBar from './layouts/TopBar'; +import Dashboard from './pages/Dashboard'; +import Sourcing from './pages/Sourcing'; +import Orders from './pages/Orders'; +import Inventory from './pages/Inventory'; +import Financial from './pages/Financial'; +import Customers from './pages/Customers'; +import Sales from './pages/Sales'; +import Suppliers from './pages/Suppliers'; +import Users from './pages/Users'; +import Login from './pages/Login'; +import Products from './pages/Products'; // New +import Reports from './pages/Reports'; +import Settings from './pages/Settings'; +import { CRMProvider, useCRM } from './context/CRMContext'; + +import Sidebar from './layouts/Sidebar'; +import Header from './layouts/Header'; +import { useTheme } from './context/ThemeContext'; + +// Simple Layout Wrapper to handle Sidebar/Header display +const AppLayout: React.FC = () => { + const location = useLocation(); + const { session, authLoading } = useCRM(); + const { theme } = useTheme(); + const isLoginPage = location.pathname === '/login'; + + if (authLoading) { + return ( +
+
+
+ ); + } + + if (!session && !isLoginPage) { + return ; + } + + if (session && isLoginPage) { + return ; + } + + if (isLoginPage) { + return ( +
+ + } /> + +
+ ); + } + + // LAYOUT STRATEGIES + + // 1. SIMPLE / CLEAN (Light) -> Uses TopBar (ERP Style) + if (theme === 'light') { + return ( +
+ +
+
+ +
+
+
+ ); + } + + // 2. OCEAN / COMPLEX -> Uses Compact Sidebar (To be refined) or Modified TopBar + if (theme === 'ocean') { + return ( +
+ {/* Sidebar for Ocean */} + + +
+
+
+
+ +
+
+
+
+ ); + } + + // 3. DARK / PRO (Default) -> Sidebar + Header (Glassmorphism) + const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false); + + return ( +
+ setIsMobileMenuOpen(false)} /> +
+ {/* Background Blob Effect for Pro Theme */} +
+ +
setIsMobileMenuOpen(!isMobileMenuOpen)} /> +
+ +
+
+
+ ); +}; + +// Extracted Routes to avoid duplication +const AppRoutes = () => ( + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +); + +import ErrorBoundary from './components/ErrorBoundary'; +import { ThemeProvider } from './context/ThemeContext'; + +const App: React.FC = () => { + return ( + + + + {/* Main Router Setup */} + + + + + + + ); +}; + +export default App; diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f342db --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +
+GHBanner +
+ +# Run and deploy your AI Studio app + +This contains everything you need to run your app locally. + +View your app in AI Studio: https://ai.studio/apps/drive/1SvuBB4oPz2uAYIkGNsKO0Y44_Y_khSrI + +## Run Locally + +**Prerequisites:** Node.js + + +1. Install dependencies: + `npm install` +2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key +3. Run the app: + `npm run dev` diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx new file mode 100644 index 0000000..a062b1b --- /dev/null +++ b/components/ErrorBoundary.tsx @@ -0,0 +1,51 @@ +import React, { Component, ErrorInfo, ReactNode } from 'react'; + +interface Props { + children?: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +class ErrorBoundary extends Component { + public state: State = { + hasError: false, + error: null + }; + + public static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error("Uncaught error:", error, errorInfo); + } + + public render() { + if (this.state.hasError) { + return ( +
+

Software Crash Detected

+
+

{this.state.error?.name}: {this.state.error?.message}

+
+                            {this.state.error?.stack}
+                        
+
+ +
+ ); + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/components/Logo.tsx b/components/Logo.tsx new file mode 100644 index 0000000..76769f7 --- /dev/null +++ b/components/Logo.tsx @@ -0,0 +1,34 @@ + +import React from 'react'; + +interface LogoProps { + className?: string; + showText?: boolean; + size?: 'sm' | 'md' | 'lg'; +} + +const Logo: React.FC = ({ className = "", showText = true, size = 'md' }) => { + // Sizes + const dim = size === 'sm' ? 24 : size === 'md' ? 32 : 48; + const textSize = size === 'sm' ? "text-lg" : size === 'md' ? "text-xl" : "text-3xl"; + + return ( +
+ {/* GEOMETRIC LOGO MARK - Abstract 'A' / Graph Up */} + + + + + + + {showText && ( +
+

ARBITRA

+ {size !== 'sm' &&

PRO SYSTEM

} +
+ )} +
+ ); +}; + +export default Logo; diff --git a/components/MarketplaceAnalytic.tsx b/components/MarketplaceAnalytic.tsx new file mode 100644 index 0000000..7fa8f32 --- /dev/null +++ b/components/MarketplaceAnalytic.tsx @@ -0,0 +1,156 @@ + +import React from 'react'; +import { Product, PLATFORMS, CalculationResult } from '../types'; +import { Info, Target, TrendingUp, AlertCircle, CheckCircle2, ExternalLink } from 'lucide-react'; + +interface Props { + product: Product; + overheadPercent: number; + useOverhead: boolean; +} + +const MarketplaceAnalytic: React.FC = ({ product, overheadPercent, useOverhead }) => { + const costParaguay = product.priceBRL; + const overhead = useOverhead ? (costParaguay * (overheadPercent / 100)) : 0; + const totalCost = costParaguay + overhead; + + const results: CalculationResult[] = PLATFORMS.map(platform => { + let platformPrice = product.marketPriceBRL || (totalCost * 1.5); + let platformUrl = ''; + + if (platform.name === 'Amazon' && product.amazonPrice) { + platformPrice = product.amazonPrice; + platformUrl = product.amazonUrl || ''; + } else if (platform.name === 'Mercado Livre' && product.mlPrice) { + platformPrice = product.mlPrice; + platformUrl = product.mlUrl || ''; + } else if (platform.name === 'Shopee' && product.shopeePrice) { + platformPrice = product.shopeePrice; + platformUrl = product.shopeeUrl || ''; + } else if (platform.name === 'Facebook') { + platformPrice = product.marketPriceBRL || (totalCost * 1.6); // Meta de venda + } + + const fees = (platformPrice * platform.commission) + platform.fixedFee; + const netProfit = platformPrice - fees - totalCost; + const margin = (netProfit / platformPrice) * 100; + + return { + platform: platform.name, + marketPrice: platformPrice, + totalCost, + fees, + netProfit, + margin, + url: platformUrl + }; + }); + + return ( +
+
+ {/* Header de Info do Produto */} +
+
+
+ Análise de Margem por Canal +

{product.name}

+

Referência Paraguay: {product.store}

+
+
+ Média Brasil + R$ {(product.marketPriceBRL || 0).toLocaleString('pt-BR')} +
+
+
+ +
+ {/* Breakdown de Custos */} +
+
+

Custo PY

+

R$ {costParaguay.toLocaleString('pt-BR')}

+
+
+

Custos Log/Fixo ({useOverhead ? `+${overheadPercent}%` : 'OFF'})

+

R$ {overhead.toLocaleString('pt-BR')}

+
+
+

Custo Final BR

+

R$ {totalCost.toLocaleString('pt-BR')}

+
+
+ +
+

Comparação em Tempo Real

+ CLIQUE NO PREÇO PARA VER O ANÚNCIO +
+ + +
+ +
+
+ +
+

+ As margens exibidas consideram seu custo operacional fixo de {useOverhead ? `${overheadPercent}%` : '0%'}. Os preços dos marketplaces são extraídos em tempo real via busca web. +

+
+
+
+ ); +}; + +export default MarketplaceAnalytic; diff --git a/context/CRMContext.tsx b/context/CRMContext.tsx new file mode 100644 index 0000000..13b38b9 --- /dev/null +++ b/context/CRMContext.tsx @@ -0,0 +1,849 @@ + +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; +import { Product, ShoppingListItem, Order, InventoryItem, Supplier, User, PlatformFee, PLATFORMS, Customer, Transaction, FinancialSummary, AuctionLot, BiddingTender, Sale, SalesChannel, PaymentMethod, OrderStatus } from '../types'; +import { searchProducts } from '../services/geminiService'; +import { supabase } from '../services/supabase'; +import { Session, User as SupabaseUser } from '@supabase/supabase-js'; + +interface CRMContextType { + // Auth State + user: User | null; + session: Session | null; + authLoading: boolean; + isAdmin: boolean; // New + signIn: (email: string, password: string) => Promise<{ error: any }>; + signOut: () => Promise; + updateUserRole: (userId: string, role: 'admin' | 'user') => Promise; // New action + + // Data State + products: Product[]; + shoppingList: ShoppingListItem[]; + orders: Order[]; + inventory: InventoryItem[]; + suppliers: Supplier[]; + users: User[]; + customers: Customer[]; + transactions: Transaction[]; + searchTerm: string; + loading: boolean; + error: string | null; + searchLoading: boolean; // New separate loading state for search + searchError: string | null; // New separate error state for search + selectedProduct: Product | null; + searchType: 'specific' | 'opportunity'; + setSearchType: (type: 'specific' | 'opportunity') => void; + overheadPercent: number; + exchangeRate: number; + activeOrderId: string | null; + useOverhead: boolean; + setUseOverhead: (use: boolean) => void; + + // Actions + setSearchTerm: (term: string) => void; + setSelectedProduct: (product: Product | null) => void; + handleSearch: (e: React.FormEvent) => Promise; + handleOpportunitySearch: (category: string) => Promise; + addToShoppingList: (product: Product) => void; + removeFromShoppingList: (id: string) => void; + updateShoppingItemQuantity: (id: string, delta: number) => void; + updateOrderStatus: (orderId: string, newStatus: OrderStatus) => void; + saveOrderAsQuotation: () => void; + resumeOrder: (orderId: string) => void; + deleteOrder: (orderId: string) => Promise; + calculateShoppingTotals: () => { + totalParaguayBRL: number; + totalCostWithOverhead: number; + totalUSD: number; + totalApproxProfit: number; + }; + + // New Actions + addTransaction: (tx: Omit) => Promise; + updateTransaction: (id: string, updates: Partial) => Promise; + deleteTransaction: (id: string) => Promise; + addCustomer: (customer: Omit) => Promise; // Updated return type + updateCustomer: (id: string, updates: Partial) => Promise; + deleteCustomer: (id: string) => void; + getFinancialSummary: () => FinancialSummary; + + // Sales Management + sales: Sale[]; + importSales: (channel: SalesChannel) => Promise; + updateSale: (id: string, updates: Partial) => void; + + // Supplier Actions + addSupplier: (supplier: Omit) => void; + updateSupplier: (id: string, updates: Partial) => void; + deleteSupplier: (id: string) => void; + + registerSale: (items: { id: string, quantity: number, salePrice: number }[], customerId?: string, paymentMethod?: PaymentMethod) => Promise; + + // Inventory Management + addProduct: (product: Omit) => Promise; + updateProduct: (id: string, updates: Partial) => Promise; + deleteProduct: (id: string) => Promise; + + // Settings + settings: any; // We will define a stronger type later if needed + updateSettings: (newSettings: any) => Promise; +} + +const CRMContext = createContext(undefined); + +export const CRMProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + // --- AUTH STATE --- + const [user, setUser] = useState(null); + const [session, setSession] = useState(null); + const [authLoading, setAuthLoading] = useState(true); + const [isAdmin, setIsAdmin] = useState(false); // New State + + // --- APP STATE --- + const [searchTerm, setSearchTerm] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [searchLoading, setSearchLoading] = useState(false); + const [searchError, setSearchError] = useState(null); + const [products, setProducts] = useState([]); + const [selectedProduct, setSelectedProduct] = useState(null); + const [searchType, setSearchType] = useState<'specific' | 'opportunity'>('specific'); + const [shoppingList, setShoppingList] = useState([]); + const [activeOrderId, setActiveOrderId] = useState(null); + const [useOverhead, setUseOverhead] = useState(true); + + // SUPABASE DATA + const [orders, setOrders] = useState([]); + const [inventory, setInventory] = useState([]); + const [suppliers, setSuppliers] = useState([]); + const [customers, setCustomers] = useState([]); + const [transactions, setTransactions] = useState([]); + const [sales, setSales] = useState([]); + const [settings, setSettings] = useState({ + defaultOverhead: 20, + defaultExchange: 5.65, + companyName: 'Arbitra System' + }); + + // MOCKED DATA + const [users, setUsers] = useState([]); + + const overheadPercent = settings.defaultOverhead || 20; + const exchangeRate = settings.defaultExchange || 5.65; + + // --- AUTH & INITIAL LOAD --- + useEffect(() => { + const handleAuthSession = async (currentSession: Session | null) => { + setSession(currentSession); + if (currentSession?.user) { + // Fetch Role from profiles table + const { data: profile, error: profileError } = await supabase + .from('profiles') + .select('role') + .eq('id', currentSession.user.id) + .single(); + + if (profileError) { + console.error("Error fetching user profile:", profileError); + } + + const role = profile?.role || 'user'; + setIsAdmin(role === 'admin'); + + setUser({ + id: currentSession.user.id, + email: currentSession.user.email!, + name: currentSession.user.user_metadata?.full_name || currentSession.user.email!, + role: role as 'admin' | 'user', + status: 'Active', // Default or fetch from profile + avatar: currentSession.user.user_metadata?.avatar_url || "https://api.dicebear.com/7.x/avataaars/svg?seed=Felix" + }); + fetchData(); + } else { + setUser(null); + setIsAdmin(false); + // Optional: Clear state on logout + setOrders([]); + setInventory([]); + } + setAuthLoading(false); + }; + + // Check active session on mount + supabase.auth.getSession().then(({ data: { session } }) => { + handleAuthSession(session); + }); + + // Listen for auth changes + const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { + handleAuthSession(session); + }); + + return () => subscription.unsubscribe(); + }, []); + + const signIn = async (email: string, password: string) => { + const { error } = await supabase.auth.signInWithPassword({ email, password }); + return { error }; + }; + + const signOut = async () => { + await supabase.auth.signOut(); + setUser(null); + setIsAdmin(false); + }; + + const updateUserRole = async (userId: string, role: 'admin' | 'user') => { + if (!isAdmin) { + alert("Você não tem permissão para atualizar funções de usuário."); + return; + } + + const { error } = await supabase.from('profiles').update({ role }).eq('id', userId); + if (error) { + console.error("Error updating role:", error); + alert("Erro ao atualizar função do usuário."); + } else { + alert("Função atualizada com sucesso."); + // Update local state immediately + setUsers(prev => prev.map(u => u.id === userId ? { ...u, role } : u)); + } + }; + + // --- SUPABASE FETCH --- + const fetchData = async () => { + setLoading(true); + try { + // Orders + const { data: ordersData } = await supabase.from('orders').select('*').order('created_at', { ascending: false }); + if (ordersData) { + const parsedOrders = ordersData.map(o => ({ + id: o.id, + date: o.date || o.created_at, + items: typeof o.items === 'string' ? JSON.parse(o.items) : (o.items || []), + totalUSD: o.total_usd || 0, + totalBRL: o.total_brl || 0, + totalCostWithOverhead: o.total_cost_with_overhead || 0, + estimatedProfit: o.estimated_profit || 0, + status: o.status, + supplierName: o.supplier_name || 'Desconhecido' + })); + setOrders(parsedOrders || []); + } + + // Inventory + const { data: inventoryData } = await supabase.from('inventory').select('*').order('created_at', { ascending: false }); + if (inventoryData) { + setInventory(inventoryData.map((i: any) => ({ + id: i.id, + name: i.name, + sku: i.sku, + ean: i.ean, + quantity: i.quantity, + avgCostBRL: i.avg_cost_brl || 0, + marketPriceBRL: i.market_price_brl || 0, + lastSupplier: i.last_supplier + }))); + } + + // Users (Admin Only) - Moved logic inside check + if (isAdmin) { + const { data: usersData } = await supabase.from('profiles').select('*').order('created_at', { ascending: false }); + if (usersData) { + setUsers(usersData.map((u: any) => ({ + id: u.id, + name: u.full_name || u.email, // Fallback if full_name is empty + email: u.email, + role: u.role, + status: u.status, + avatar: u.avatar_url || `https://api.dicebear.com/7.x/initials/svg?seed=${u.email}`, + lastAccess: u.last_access + }))); + } + } + + // Settings + const { data: settingsData } = await supabase.from('settings').select('*').single(); + if (settingsData) { + setSettings({ + companyName: settingsData.company_name, + cnpj: settingsData.cnpj, + ie: settingsData.ie, + defaultOverhead: settingsData.default_overhead, + defaultExchange: settingsData.default_exchange, + geminiKey: settingsData.gemini_key, + melhorEnvioToken: settingsData.melhor_envio_token, + blingToken: settingsData.bling_token, + tinyToken: settingsData.tiny_token, + certificatePassword: settingsData.certificate_password, + nfeSerie: settingsData.nfe_serie, + nfeNumber: settingsData.nfe_number, + nfeEnvironment: settingsData.nfe_environment, + smtpHost: settingsData.smtp_host, + smtpPort: settingsData.smtp_port, + smtpUser: settingsData.smtp_user, + smtpPass: settingsData.smtp_pass, + autoSyncSales: settingsData.auto_sync_sales, + autoSyncStock: settingsData.auto_sync_stock, + }); + } + } catch (err) { + console.error("Error fetching data:", err); + } finally { + setLoading(false); + } + }; + + const updateSettings = async (newSettings: any) => { + // Determine if we need to insert or update (assuming single row for settings for now per user or global) + // For simplicity, we'll try to update the first row found, or insert if empty. + + const dbSettings = { + company_name: newSettings.companyName, + cnpj: newSettings.cnpj, + ie: newSettings.ie, + default_overhead: newSettings.defaultOverhead, + default_exchange: newSettings.defaultExchange, + gemini_key: newSettings.geminiKey, + melhor_envio_token: newSettings.melhorEnvioToken, + bling_token: newSettings.blingToken, + tiny_token: newSettings.tinyToken, + certificate_password: newSettings.certificatePassword, + nfe_serie: newSettings.nfeSerie, + nfe_number: newSettings.nfeNumber, + nfe_environment: newSettings.nfeEnvironment, + smtp_host: newSettings.smtpHost, + smtp_port: newSettings.smtpPort, + smtp_user: newSettings.smtpUser, + smtp_pass: newSettings.smtpPass, + auto_sync_sales: newSettings.autoSyncSales, + auto_sync_stock: newSettings.autoSyncStock + }; + + // Check if settings exist + const { data } = await supabase.from('settings').select('id').single(); + + if (data) { + // Update + await supabase.from('settings').update(dbSettings).eq('id', data.id); + } else { + // Insert + await supabase.from('settings').insert([dbSettings]); + } + + setSettings(newSettings); + }; + + // --- ACTIONS --- + const handleSearch = async (e: React.FormEvent) => { + e.preventDefault(); + if (!searchTerm.trim()) return; + setSearchLoading(true); + setSearchError(null); + setSelectedProduct(null); + setProducts([]); // Clear previous results + try { + const { products: result } = await searchProducts(searchTerm); + setProducts(result); + if (result.length === 0) { + setSearchError("Não encontramos nenhum produto com esse nome. Tente termos mais gerais (ex: 'iPhone' em vez de 'iPhone 15 Pro Max 256GB Azul')."); + } + } catch (err: any) { + console.error(err); + if (err.message && err.message.includes("VITE_GOOGLE_API_KEY")) { + setSearchError("Erro de Configuração: API Key do Google não encontrada. Verifique o arquivo .env"); + } else if (err.message && (err.message.includes("fetch") || err.message.includes("network"))) { + setSearchError("Erro de Conexão: Não foi possível conectar ao servidor. Verifique sua internet."); + } else { + setSearchError("Ocorreu um erro ao consultar a inteligência artificial. Por favor, tente novamente em alguns instantes."); + } + } finally { + setSearchLoading(false); + } + }; + + const handleOpportunitySearch = async (category: string) => { + if (!category.trim()) return; + setSearchLoading(true); + setSearchError(null); + setSelectedProduct(null); + setProducts([]); // Clear previous results + try { + const { searchOpportunities } = await import('../services/geminiService'); + const { products: result } = await searchOpportunities(category, useOverhead); + + setProducts(result); + if (result.length === 0) { + setSearchError("Nenhuma oportunidade encontrada com >25% de margem nesta categoria no momento. Tente outra categoria."); + } + } catch (err: any) { + console.error(err); + if (err.message && err.message.includes("VITE_GOOGLE_API_KEY")) { + setSearchError("Erro de Configuração: API Key do Google não encontrada. Verifique o arquivo .env"); + } else if (err.message && (err.message.includes("fetch") || err.message.includes("network"))) { + setSearchError("Erro de Conexão: Não foi possível conectar ao servidor. Verifique sua internet."); + } else { + setSearchError("A IA demorou muito para responder ou encontrou um erro. Tente novamente, geralmente funciona na segunda tentativa."); + } + } finally { + setSearchLoading(false); + } + }; + + const addToShoppingList = (p: Product) => { + setShoppingList(prev => { + const existing = prev.find(item => item.name === p.name && item.store === p.store); + if (existing) return prev.map(item => item.id === existing.id ? { ...item, quantity: item.quantity + 1 } : item); + return [...prev, { + id: crypto.randomUUID(), + name: p.name, + store: p.store, + priceUSD: p.priceUSD, + priceBRL: p.priceBRL, + quantity: 1, + marketPriceBRL: p.marketPriceBRL || (p.priceBRL * 1.6) + }]; + }); + }; + + const removeFromShoppingList = (id: string) => { + setShoppingList(prev => prev.filter(i => i.id !== id)); + }; + + const updateShoppingItemQuantity = (id: string, delta: number) => { + setShoppingList(prev => prev.map(i => i.id === id ? { ...i, quantity: Math.max(1, i.quantity + delta) } : i)); + }; + + const updateOrderStatus = async (orderId: string, newStatus: OrderStatus) => { + const order = orders.find(o => o.id === orderId); + if (!order) return; + + // Database Update + await supabase.from('orders').update({ status: newStatus }).eq('id', orderId); + + // Inventory Logic if Received + if (newStatus === 'Received' && order.status !== 'Received') { + const newInventoryItems: InventoryItem[] = order.items.map(item => ({ + id: crypto.randomUUID(), + name: item.name, + sku: `SKU-${Math.random().toString(36).substr(2, 4).toUpperCase()}`, + quantity: item.quantity, + avgCostBRL: item.priceBRL * (1 + overheadPercent / 100), + marketPriceBRL: item.marketPriceBRL, + lastSupplier: item.store + })); + + await supabase.from('inventory').insert(newInventoryItems.map(i => ({ + id: i.id, name: i.name, sku: i.sku, quantity: i.quantity, + avg_cost_brl: i.avgCostBRL, market_price_brl: i.marketPriceBRL, last_supplier: i.lastSupplier + }))); + + // Refresh Inventory + const { data } = await supabase.from('inventory').select('*').order('created_at', { ascending: false }); + if (data) setInventory(data); + } + + // Local Update + setOrders(prev => prev.map(o => o.id === orderId ? { ...o, status: newStatus } : o)); + }; + + const deleteOrder = async (orderId: string) => { + // Database Delete + const { error } = await supabase.from('orders').delete().eq('id', orderId); + if (error) { + console.error("Error deleting order:", error); + alert("Erro ao excluir pedido."); + } else { + // Local Update + setOrders(prev => prev.filter(o => o.id !== orderId)); + } + }; + + const calculateShoppingTotals = () => { + const totalParaguayBRL = shoppingList.reduce((acc, item) => acc + (item.priceBRL * item.quantity), 0); + const overheadMultiplier = useOverhead ? (1 + overheadPercent / 100) : 1; + const totalCostWithOverhead = totalParaguayBRL * overheadMultiplier; + const totalUSD = shoppingList.reduce((acc, item) => acc + (item.priceUSD * item.quantity), 0); + const totalApproxProfit = shoppingList.reduce((acc, item) => { + const costWithOverhead = item.priceBRL * overheadMultiplier; + const profitPerUnit = item.marketPriceBRL - costWithOverhead; + return acc + (profitPerUnit * item.quantity); + }, 0); + return { totalParaguayBRL, totalCostWithOverhead, totalUSD, totalApproxProfit }; + }; + + const resumeOrder = (orderId: string) => { + const order = orders.find(o => o.id === orderId); + if (!order) return; + setShoppingList(order.items); + setActiveOrderId(order.id); + }; + + const saveOrderAsQuotation = async () => { + if (shoppingList.length === 0) return; + const totals = calculateShoppingTotals(); + + if (activeOrderId) { + // Update + const updatePayload = { + items: shoppingList, // Helper handles jsonb + total_usd: totals.totalUSD, + total_brl: totals.totalParaguayBRL, + total_cost_with_overhead: totals.totalCostWithOverhead, + estimated_profit: totals.totalApproxProfit, + supplier_name: shoppingList[0]?.store || 'Múltiplos' + }; + + await supabase.from('orders').update(updatePayload).eq('id', activeOrderId); + + setOrders(prev => prev.map(o => o.id === activeOrderId ? { + ...o, items: shoppingList, totalUSD: totals.totalUSD, totalBRL: totals.totalParaguayBRL, + totalCostWithOverhead: totals.totalCostWithOverhead, estimatedProfit: totals.totalApproxProfit, + supplierName: updatePayload.supplier_name + } : o)); + setActiveOrderId(null); + + } else { + // Create + const newOrder: Order = { + id: crypto.randomUUID(), + date: new Date().toISOString(), + items: [...shoppingList], + totalUSD: totals.totalUSD, + totalBRL: totals.totalParaguayBRL, + totalCostWithOverhead: totals.totalCostWithOverhead, + estimatedProfit: totals.totalApproxProfit, + status: 'Pending', + supplierName: shoppingList[0]?.store || 'Múltiplos' + }; + + // Map to DB columns + const dbOrder = { + id: newOrder.id, + date: newOrder.date, + status: newOrder.status, + total_usd: newOrder.totalUSD, + total_brl: newOrder.totalBRL, + total_cost_with_overhead: newOrder.totalCostWithOverhead, + estimated_profit: newOrder.estimatedProfit, + items: newOrder.items, + supplier_name: newOrder.supplierName + }; + + const { error } = await supabase.from('orders').insert([dbOrder]); + + if (error) { + console.error("Error saving order:", error); + alert(`Erro ao salvar pedido: ${error.message} (${error.details || ''})`); + return; + } + + setOrders([newOrder, ...orders]); + alert("Pedido salvo com sucesso!"); + } + setShoppingList([]); + }; + + // --- FINANCIAL ACTIONS --- + const addTransaction = async (tx: Omit) => { + const id = crypto.randomUUID(); + const newTx = { + ...tx, + id, + user_id: user?.id, + status: tx.status || 'Pending', + payment_method: tx.paymentMethod || 'Cash', + due_date: tx.dueDate || new Date().toISOString() + }; + // Map camelCase to snake_case for DB if needed, but Supabase JS client handles it if keys match DB columns + // or if we map explicitly. Let's map explicitly to be safe. + const dbTx = { + id: newTx.id, + date: newTx.date, + type: newTx.type, + category: newTx.category, + amount: newTx.amount, + description: newTx.description, + status: newTx.status, + payment_method: newTx.paymentMethod, // DB column: payment_method + due_date: newTx.dueDate, // DB column: due_date + user_id: newTx.user_id + }; + + const { error } = await supabase.from('transactions').insert([dbTx]); + if (error) console.error("Error adding transaction:", error); + + // We update local state with the camelCase version for UI + setTransactions(prev => [{ ...tx, id: newTx.id, status: newTx.status, paymentMethod: newTx.paymentMethod, dueDate: newTx.dueDate }, ...prev]); + }; + + const updateTransaction = async (id: string, updates: Partial) => { + // Map updates + const dbUpdates: any = { ...updates }; + if (updates.paymentMethod) dbUpdates.payment_method = updates.paymentMethod; + if (updates.dueDate) dbUpdates.due_date = updates.dueDate; + + const { error } = await supabase.from('transactions').update(dbUpdates).eq('id', id); + if (error) { + console.error("Error updating transaction:", error); + alert("Erro ao atualizar transação"); + } else { + setTransactions(prev => prev.map(t => t.id === id ? { ...t, ...updates } : t)); + } + }; + + const deleteTransaction = async (id: string) => { + await supabase.from('transactions').delete().eq('id', id); + setTransactions(prev => prev.filter(t => t.id !== id)); + }; + + const addCustomer = async (customer: Omit): Promise => { + const id = crypto.randomUUID(); + const newCustomer = { ...customer, id, totalPurchased: 0 }; + + const dbCustomer = { + id: newCustomer.id, + name: newCustomer.name, + email: newCustomer.email, + phone: newCustomer.phone, + city: newCustomer.city, + status: newCustomer.status, + total_purchased: 0, + user_id: user?.id + }; + + const { data, error } = await supabase.from('customers').insert([dbCustomer]).select(); + + if (error) { + console.error("Error adding customer:", error); + return null; + } + + setCustomers(prev => [...prev, newCustomer]); + return newCustomer; // Return the object + }; + + const updateCustomer = async (id: string, updates: Partial) => { + // Map updates to snake_case if necessary, or pass mostly as is if Keys match + const dbUpdates: any = { ...updates }; + if (updates.totalPurchased) dbUpdates.total_purchased = updates.totalPurchased; + + await supabase.from('customers').update(dbUpdates).eq('id', id); + setCustomers(prev => prev.map(c => c.id === id ? { ...c, ...updates } : c)); + }; + + const deleteCustomer = async (id: string) => { + await supabase.from('customers').delete().eq('id', id); + setCustomers(prev => prev.filter(c => c.id !== id)); + }; + + const getFinancialSummary = () => { + const totalIncome = transactions.filter(t => t.type === 'Income').reduce((acc, t) => acc + t.amount, 0); + const totalExpense = transactions.filter(t => t.type === 'Expense').reduce((acc, t) => acc + t.amount, 0); + return { + totalIncome, + totalExpense, + balance: totalIncome - totalExpense, + recentTransactions: transactions.slice(0, 5) + }; + }; + + // --- SUPPLIER ACTIONS --- + const addSupplier = async (supplier: Omit) => { + const id = crypto.randomUUID(); + const newSupplier = { ...supplier, id, user_id: user?.id }; + await supabase.from('suppliers').insert([newSupplier]); + setSuppliers(prev => [...prev, newSupplier]); + }; + + const updateSupplier = async (id: string, updates: Partial) => { + await supabase.from('suppliers').update(updates).eq('id', id); + setSuppliers(prev => prev.map(s => s.id === id ? { ...s, ...updates } : s)); + }; + + const deleteSupplier = async (id: string) => { + await supabase.from('suppliers').delete().eq('id', id); + setSuppliers(prev => prev.filter(s => s.id !== id)); + }; + + // PRODUCT ACTIONS + const addProduct = async (product: Omit) => { + if (!user) { + alert("Erro: Usuário não autenticado. O sistema perdeu a conexão ou você não está logado."); + return; + } + console.log("Attempting to add product:", product); + const id = crypto.randomUUID(); + const newProduct = { ...product, id }; + + // In a real app, 'products' and 'inventory' might be separate tables. + // Here we treat 'inventory' as the master product list for simplicity as per current structure. + const { data, error } = await supabase.from('inventory').insert([{ + id: newProduct.id, + name: newProduct.name, + sku: newProduct.sku, + ean: newProduct.ean, + quantity: newProduct.quantity, + avg_cost_brl: newProduct.avgCostBRL, + market_price_brl: newProduct.marketPriceBRL, + last_supplier: newProduct.lastSupplier, + user_id: user?.id + }]).select(); + + if (error) { + console.error("Supabase Error adding product:", error); + throw error; + } + + console.log("Product added successfully, DB response:", data); + + setInventory(prev => [newProduct, ...prev]); + }; + + const updateProduct = async (id: string, updates: Partial) => { + // Map to snake_case for DB + const dbUpdates: any = { ...updates }; + if (updates.avgCostBRL) dbUpdates.avg_cost_brl = updates.avgCostBRL; + if (updates.marketPriceBRL) dbUpdates.market_price_brl = updates.marketPriceBRL; + if (updates.lastSupplier) dbUpdates.last_supplier = updates.lastSupplier; + + await supabase.from('inventory').update(dbUpdates).eq('id', id); + setInventory(prev => prev.map(p => p.id === id ? { ...p, ...updates } : p)); + }; + + const deleteProduct = async (id: string) => { + await supabase.from('inventory').delete().eq('id', id); + setInventory(prev => prev.filter(p => p.id !== id)); + }; + + // SALES LOGIC & MANAGEMENT + const importSales = async (channel: SalesChannel) => { + setLoading(true); + // MOCK DELAY & DATA + await new Promise(r => setTimeout(r, 1500)); + + const newSales: Sale[] = Array.from({ length: Math.floor(Math.random() * 3) + 2 }).map((_, i) => ({ + id: crypto.randomUUID(), + date: new Date().toISOString(), + customerName: `Cliente ${channel} ${Math.floor(Math.random() * 1000)}`, + items: [ + { id: 'mock-item', name: 'Produto Importado ' + (i + 1), quantity: 1, salePrice: Math.random() * 200 + 50 } + ], + total: 0, // Calculated below + status: 'Pending' as const, + channel: channel, + externalId: `MLB-${Math.floor(Math.random() * 1000000)}`, + isStockLaunched: false, + isFinancialLaunched: false + })).map(s => ({ ...s, total: s.items.reduce((acc, i) => acc + (i.quantity * i.salePrice), 0) })); + + setSales(prev => [...newSales, ...prev]); // Add to top + setLoading(false); + }; + + const updateSale = (id: string, updates: Partial) => { + setSales(prev => prev.map(s => { + if (s.id !== id) return s; + const updatedSale = { ...s, ...updates }; + + // Sincronização Automática (Mock logic linking checkboxes to actual actions) + if (updates.isStockLaunched && !s.isStockLaunched) { + // Trigger stock deduction logic theoretically + console.log("Stock launched for sale", id); + } + if (updates.isFinancialLaunched && !s.isFinancialLaunched) { + addTransaction({ + date: new Date().toISOString(), + type: 'Income', + category: 'Venda Marketplace', + amount: updatedSale.total, + description: `Venda ${updatedSale.channel} #${updatedSale.externalId || id}`, + status: 'Paid', + paymentMethod: 'Other' + }); + } + + return updatedSale; + })); + }; + + const registerSale = async (items: { id: string, quantity: number, salePrice: number }[], customerId?: string, paymentMethod: PaymentMethod = 'Cash') => { + // 1. Deduct from Inventory + const updatedInventory = inventory.map(invItem => { + const soldItem = items.find(i => i.id === invItem.id); + if (soldItem) { + return { ...invItem, quantity: Math.max(0, invItem.quantity - soldItem.quantity) }; + } + return invItem; + }); + setInventory(updatedInventory); + + // 2. Add Transaction (Income) + const totalAmount = items.reduce((acc, item) => acc + (item.quantity * item.salePrice), 0); + const cust = customers.find(c => c.id === customerId); + const transaction: Omit = { + date: new Date().toISOString(), + type: 'Income', + category: 'Venda de Produtos', + amount: totalAmount, + description: `Venda balcão - Cliente: ${cust?.name || 'Não Id.'}`, + status: 'Paid', + paymentMethod: paymentMethod, // Now correctly using the argument + dueDate: new Date().toISOString() + }; + + await addTransaction(transaction); // This is good, but Sales.tsx is calling registerSale which handles logic. + // ACTUALLY: registerSale needs to accept payment method or we need to update the transaction logic inside it. + // Modifying registerSale signature to accept PaymentMethod would be best practice. + + // 3. Register as a Sale Record + const newSale: Sale = { + id: crypto.randomUUID(), + date: new Date().toISOString(), + customerId: customerId, + customerName: cust ? cust.name : 'Cliente Balcão', + items: items.map(i => { + const inv = inventory.find(inv => inv.id === i.id); + return { + id: i.id, + name: inv?.name || 'Item', + quantity: i.quantity, + salePrice: i.salePrice, + costPrice: inv?.avgCostBRL || 0 // Capture cost at moment of sale + }; + }), + total: totalAmount, + status: 'Completed', + channel: 'Local', + isStockLaunched: true, // Auto-launched for POS + isFinancialLaunched: true // Auto-launched for POS + }; + setSales(prev => [newSale, ...prev]); + + // 4. Update Customer (if exists) - Mocked for now + if (customerId) { + console.log(`Updated customer ${customerId} total purchased`); + } + }; + + const value = { + // Auth + user, session, authLoading, signIn, signOut, isAdmin, updateUserRole, + // Data + products, shoppingList, orders, inventory, suppliers, users, searchTerm, loading, error, selectedProduct, overheadPercent, exchangeRate, activeOrderId, + searchLoading, searchError, searchType, setSearchType, + customers, transactions, + setSearchTerm, setSelectedProduct, handleSearch, handleOpportunitySearch, addToShoppingList, removeFromShoppingList, updateShoppingItemQuantity, updateOrderStatus, saveOrderAsQuotation, calculateShoppingTotals, resumeOrder, deleteOrder, + addTransaction, deleteTransaction, updateTransaction, addCustomer, updateCustomer, deleteCustomer, getFinancialSummary, + addSupplier, updateSupplier, deleteSupplier, registerSale, + addProduct, updateProduct, deleteProduct, settings, updateSettings, + sales, importSales, updateSale, + useOverhead, setUseOverhead + }; + + return {children}; +}; + +export const useCRM = () => { + const context = useContext(CRMContext); + if (!context) throw new Error('useCRM must be used within a CRMProvider'); + return context; +}; diff --git a/context/ThemeContext.tsx b/context/ThemeContext.tsx new file mode 100644 index 0000000..943009d --- /dev/null +++ b/context/ThemeContext.tsx @@ -0,0 +1,49 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; + +type Theme = 'dark' | 'light' | 'ocean'; + +interface ThemeContextType { + theme: Theme; + setTheme: (theme: Theme) => void; +} + +const ThemeContext = createContext(undefined); + +export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [theme, setTheme] = useState(() => { + // Check local storage or system preference + const saved = localStorage.getItem('theme'); + if (saved) return saved as Theme; + return 'dark'; // Default + }); + + useEffect(() => { + const root = window.document.documentElement; + + // Remove old classes + root.classList.remove('theme-light', 'theme-ocean', 'dark'); + + // Add new class + if (theme === 'dark') { + root.classList.add('dark'); + } else { + root.classList.add(`theme-${theme}`); + } + + localStorage.setItem('theme', theme); + }, [theme]); + + return ( + + {children} + + ); +}; + +export const useTheme = () => { + const context = useContext(ThemeContext); + if (context === undefined) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +}; diff --git a/index.css b/index.css new file mode 100644 index 0000000..80559da --- /dev/null +++ b/index.css @@ -0,0 +1,164 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + + /* Dark Theme (Default) */ + :root { + --background: 240 10% 4%; + /* #0a0a0c - Deeper black/zinc */ + --foreground: 0 0% 98%; + + --card: 240 10% 6%; + /* Slightly lighter than bg */ + --card-foreground: 0 0% 98%; + + --popover: 240 10% 6%; + --popover-foreground: 0 0% 98%; + + --primary: 263 70% 50%; + /* Deep Purple/Indigo */ + --primary-foreground: 210 40% 98%; + + --secondary: 240 4% 16%; + --secondary-foreground: 0 0% 98%; + + --muted: 240 4% 16%; + --muted-foreground: 240 5% 65%; + + --accent: 240 4% 16%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + + --border: 240 4% 16%; + --input: 240 4% 16%; + --ring: 240 4.9% 83.9%; + --radius: 1rem; + } + + /* Light Theme (Simple/Clean) */ + .theme-light { + --background: 210 40% 98%; + /* Soft blue-white */ + --foreground: 222 47% 11%; + /* Dark Navy Text */ + + --card: 0 0% 100%; + /* Pure White */ + --card-foreground: 222 47% 11%; + + --popover: 0 0% 100%; + --popover-foreground: 222 47% 11%; + + --primary: 221 83% 53%; + /* Classic Blue */ + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222 47% 11%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215 16% 47%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222 47% 11%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + + --border: 214 32% 91%; + --input: 214 32% 91%; + --ring: 221 83% 53%; + } + + /* Ocean Theme (Deep Blue) */ + .theme-ocean { + --background: 222 47% 11%; + /* Navy BG */ + --foreground: 210 40% 98%; + + --card: 217 33% 17%; + /* Lighter Navy Card */ + --card-foreground: 210 40% 98%; + + --popover: 217 33% 17%; + --popover-foreground: 210 40% 98%; + + --primary: 199 89% 48%; + /* Cyan/Blue */ + --primary-foreground: 210 40% 98%; + + --secondary: 217 19% 27%; + --secondary-foreground: 210 40% 98%; + + --muted: 217 19% 27%; + --muted-foreground: 215 20% 65%; + + --accent: 217 19% 27%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + + --border: 217 19% 27%; + --input: 217 19% 27%; + --ring: 199 89% 48%; + } +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + } +} + +/* Custom Scrollbar for Premium Feel */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: hsl(var(--border)); + border-radius: 99px; +} + +::-webkit-scrollbar-thumb:hover { + background: hsl(var(--muted-foreground)); +} + +/* Glassmorphism Utilities */ +.glass-panel { + background: hsl(var(--background) / 0.6); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid hsl(var(--border) / 0.5); +} + +.glass-card { + background: hsl(var(--card)); + border: 1px solid hsl(var(--border)); +} + +/* Light Theme Overrides for Glass */ +.theme-light .glass-card { + background: white; + box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.05); + border: 1px solid #e2e8f0; +} + +.theme-light .glass-panel { + background: rgba(255, 255, 255, 0.8); + border: 1px solid #e2e8f0; +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..094cbfe --- /dev/null +++ b/index.html @@ -0,0 +1,83 @@ + + + + + + + Arbitra System + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/index.tsx b/index.tsx new file mode 100644 index 0000000..aaa0c6e --- /dev/null +++ b/index.tsx @@ -0,0 +1,16 @@ + +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Could not find root element to mount to"); +} + +const root = ReactDOM.createRoot(rootElement); +root.render( + + + +); diff --git a/layouts/Header.tsx b/layouts/Header.tsx new file mode 100644 index 0000000..adfe7ae --- /dev/null +++ b/layouts/Header.tsx @@ -0,0 +1,105 @@ + +import React from 'react'; +import { TrendingUp, Bell, Search, Command, Menu } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import { useLocation } from 'react-router-dom'; +import clsx from 'clsx'; + +interface HeaderProps { + onMenuClick?: () => void; +} + +const Header: React.FC = ({ onMenuClick }) => { + const { exchangeRate, orders } = useCRM(); + const location = useLocation(); + + // Mapping English paths to proper titles if needed, or keeping English for 'Premium' feel + const getTitle = () => { + const path = location.pathname.substring(1); + if (!path) return 'Dashboard'; + + const titles: Record = { + 'sales': 'Vendas', + 'sourcing': 'Sourcing', // or Arbitragem + 'products': 'Produtos', + 'orders': 'Pedidos', + 'financial': 'Financeiro', + 'customers': 'Clientes', + 'inventory': 'Estoque', + 'suppliers': 'Fornecedores', + 'reports': 'Relatórios', + 'users': 'Usuários', + 'settings': 'Configurações' + }; + + return titles[path] || path.charAt(0).toUpperCase() + path.slice(1); + }; + + const totalProfit = orders + .filter(o => o.status === 'Received') + .reduce((acc, o) => acc + o.estimatedProfit, 0); + + return ( +
+ {/* Left: Breadcrumb/Title */} +
+ +
+
+ App + / + {getTitle()} +
+

{getTitle()}

+
+
+ + {/* Right: Actions */} +
+ + {/* Search Bar (Visual Only) */} +
+ + Buscar... +
+
+ K +
+
+ +
+ + {/* Stats */} +
+
+ Dólar Hoje + R$ {exchangeRate.toFixed(2)} +
+ +
+
+ Lucro Total + 0 ? "text-emerald-400 bg-emerald-400/10" : "text-foreground" + )}> + R$ {totalProfit.toLocaleString('pt-BR')} + +
+
+
+ + +
+
+ ); +}; + +export default Header; diff --git a/layouts/Sidebar.tsx b/layouts/Sidebar.tsx new file mode 100644 index 0000000..0633547 --- /dev/null +++ b/layouts/Sidebar.tsx @@ -0,0 +1,109 @@ +import React from 'react'; +import { NavLink } from 'react-router-dom'; +import { LayoutDashboard, Search, History, Boxes, Store, Users, TrendingUp, LogOut, Wallet, ShoppingCart, Package, BarChart3, Settings } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import clsx from 'clsx'; + +interface SidebarProps { + isOpen?: boolean; + onClose?: () => void; +} + +const Sidebar: React.FC = ({ isOpen = false, onClose }) => { + const { user, isAdmin, signOut } = useCRM(); + const currentUser = user; + + const navItems = [ + { to: '/', icon: LayoutDashboard, label: 'Dashboard' }, + { to: '/sales', icon: ShoppingCart, label: 'Vendas' }, + { to: '/sourcing', icon: Search, label: 'Sourcing' }, + { to: '/products', icon: Package, label: 'Produtos' }, + { to: '/orders', icon: History, label: 'Pedidos' }, + { to: '/financial', icon: Wallet, label: 'Financeiro' }, + { to: '/customers', icon: Users, label: 'Clientes' }, + { to: '/inventory', icon: Boxes, label: 'Estoque' }, + { to: '/suppliers', icon: Store, label: 'Fornecedores' }, + { to: '/reports', icon: BarChart3, label: 'Relatórios' }, + { to: '/users', icon: Users, label: 'Usuários' }, + { to: '/settings', icon: Settings, label: 'Configurações' } + ]; + + return ( + <> + {/* Mobile Overlay */} + {isOpen && ( +
+ )} + + + + ); +}; + +export default Sidebar; diff --git a/layouts/TopBar.tsx b/layouts/TopBar.tsx new file mode 100644 index 0000000..56b2000 --- /dev/null +++ b/layouts/TopBar.tsx @@ -0,0 +1,175 @@ +import React, { useState } from 'react'; +import { NavLink, useNavigate } from 'react-router-dom'; +import { + LayoutDashboard, Search, History, Boxes, Store, Users, TrendingUp, LogOut, Wallet, + ChevronDown, Package, FileText, Factory, Menu, ShoppingCart, Settings, BarChart3 +} from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import clsx from 'clsx'; +import Logo from '../components/Logo'; + +const TopBar: React.FC = () => { + const { users, signOut } = useCRM(); + const currentUser = users[0]; + const navigate = useNavigate(); + + // Dropdown States + const [openMenu, setOpenMenu] = useState(null); + + const handleSignOut = async () => { + await signOut(); + navigate('/login'); + }; + + const navStructure = [ + { + label: 'Dashboard', + to: '/', + icon: LayoutDashboard, + type: 'link' + }, + { + label: 'Vendas', + to: '/sales', + icon: ShoppingCart, + type: 'link' + }, + { + label: 'Arbitragem', + icon: Search, + type: 'dropdown', + items: [ + { label: 'Sourcing Intel', to: '/sourcing', icon: Search }, + { label: 'Minhas Ordens', to: '/orders', icon: History }, + ] + }, + { + label: 'Cadastros', + icon: FileText, + type: 'dropdown', + items: [ + { label: 'Clientes', to: '/customers', icon: Users }, + { label: 'Fornecedores', to: '/suppliers', icon: Store }, + { label: 'Produtos', to: '/products', icon: Package }, + { label: 'Estoque', to: '/inventory', icon: Boxes }, + { label: 'Usuários', to: '/users', icon: Users }, + ] + }, + { + label: 'Produção', + icon: Factory, + type: 'nav-link', // Future placeholder + to: '#', // Placeholder + items: [], // Empty for now or future items + disabled: true + }, + { + label: 'Relatórios', + to: '/reports', + icon: BarChart3, + type: 'link' + }, + { + label: 'Financeiro', + to: '/financial', + icon: Wallet, + type: 'link' + } + ]; + + return ( +
+ + {/* LOGO */} +
navigate('/')} className="cursor-pointer"> + +
+ + {/* NAV MENU CASCATA */} + + + {/* USER ACTIONS */} +
+ + +
+
+

{currentUser?.name || 'Admin'}

+

Online

+
+
+ Avatar +
+
+
+
+ ); +}; + +export default TopBar; diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..7532ee1 --- /dev/null +++ b/metadata.json @@ -0,0 +1,5 @@ +{ + "name": "Paraguay Arbitrage CRM", + "description": "Professional ERP/CRM solution for Paraguay arbitrage. Features real-time sourcing via Gemini, order tracking, profit history (Facebook-based), and supplier management.", + "requestFramePermissions": [] +} \ No newline at end of file diff --git a/migration.sql b/migration.sql new file mode 100644 index 0000000..27906f7 --- /dev/null +++ b/migration.sql @@ -0,0 +1,45 @@ +-- MIGRATION SCRIPT +-- Run this to update your existing tables with the new columns + +-- 1. Add 'ean' to inventory (This was the error you saw) +alter table public.inventory add column if not exists ean text; + +-- 2. Add 'user_id' to all key tables (For the authentication fix I made) +alter table public.inventory add column if not exists user_id uuid references auth.users; +alter table public.suppliers add column if not exists user_id uuid references auth.users; +alter table public.customers add column if not exists user_id uuid references auth.users; +alter table public.orders add column if not exists user_id uuid references auth.users; +alter table public.transactions add column if not exists user_id uuid references auth.users; + +-- 3. Ensure Settings table exists (in case it wasn't created yet) +create table if not exists public.settings ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()), + company_name text, + cnpj text, + ie text, + default_overhead numeric default 20, + default_exchange numeric default 5.65, + brazil_api_token text, + melhor_envio_token text, + bling_token text, + tiny_token text, + gemini_key text, + certificate_password text, + nfe_serie text default '1', + nfe_number text, + nfe_environment text default 'homologacao', + smtp_host text, + smtp_port text, + smtp_user text, + smtp_pass text, + auto_sync_sales boolean default true, + auto_sync_stock boolean default true, + user_id uuid references auth.users +); + +-- 4. Re-apply RLS policies just in case +alter table public.inventory enable row level security; +drop policy if exists "Enable all for authenticated users" on public.inventory; +create policy "Enable all for authenticated users" on public.inventory for all to authenticated using (true) with check (true); diff --git a/n8n-scraping-workflow.json b/n8n-scraping-workflow.json new file mode 100644 index 0000000..76b0c5b --- /dev/null +++ b/n8n-scraping-workflow.json @@ -0,0 +1,125 @@ +{ + "name": "Scrape Compras Paraguai", + "nodes": [ + { + "parameters": { + "path": "search", + "responseMode": "lastNode", + "options": {} + }, + "type": "n8n-nodes-base.webhook", + "typeVersion": 1, + "position": [ + 0, + 0 + ], + "id": "webhook-trigger", + "name": "Webhook" + }, + { + "parameters": { + "url": "=https://www.comprasparaguai.com.br/busca/?q={{ $json.query.query.replace(/ /g, '+') }}", + "options": {} + }, + "type": "n8n-nodes-base.httpRequest", + "typeVersion": 4.1, + "position": [ + 220, + 0 + ], + "id": "http-request", + "name": "Fetch HTML" + }, + { + "parameters": { + "mode": "html", + "dataPropertyName": "data", + "selectors": [ + { + "name": "products", + "selector": ".product-item", + "returnArray": true, + "properties": [ + { + "name": "name", + "selector": ".product-title", + "value": "text" + }, + { + "name": "priceUSD", + "selector": ".price-dolar", + "value": "text" + }, + { + "name": "store", + "selector": ".store-name", + "value": "text" + }, + { + "name": "link", + "selector": "a", + "attribute": "href" + } + ] + } + ] + }, + "type": "n8n-nodes-base.htmlExtract", + "typeVersion": 1, + "position": [ + 440, + 0 + ], + "id": "html-extract", + "name": "Extract Data" + }, + { + "parameters": { + "jsCode": "const products = $input.all()[0].json.products || [];\n\nconst cleanProducts = products.map(p => ({\n name: p.name.trim(),\n priceUSD: parseFloat(p.priceUSD.replace('US$ ', '').replace(',', '.').trim()),\n priceBRL: parseFloat(p.priceUSD.replace('US$ ', '').replace(',', '.').trim()) * 5.75,\n store: p.store.trim(),\n url: 'https://www.comprasparaguai.com.br' + p.link\n})).filter(p => !isNaN(p.priceUSD));\n\nreturn cleanProducts;" + }, + "type": "n8n-nodes-base.code", + "typeVersion": 2, + "position": [ + 660, + 0 + ], + "id": "cleanup-code", + "name": "Clean Data" + } + ], + "connections": { + "Webhook": { + "main": [ + [ + { + "node": "Fetch HTML", + "type": "main", + "index": 0 + } + ] + ] + }, + "Fetch HTML": { + "main": [ + [ + { + "node": "Extract Data", + "type": "main", + "index": 0 + } + ] + ] + }, + "Extract Data": { + "main": [ + [ + { + "node": "Clean Data", + "type": "main", + "index": 0 + } + ] + ] + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..6c7025c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3255 @@ +{ + "name": "paraguay-arbitrage-crm", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "paraguay-arbitrage-crm", + "version": "0.0.0", + "dependencies": { + "@google/genai": "^1.35.0", + "@supabase/supabase-js": "^2.90.1", + "clsx": "^2.1.1", + "framer-motion": "^12.26.1", + "lucide-react": "^0.562.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-router-dom": "^7.12.0", + "recharts": "^3.6.0", + "tailwind-merge": "^3.4.0" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@google/genai": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.35.0.tgz", + "integrity": "sha512-ZC1d0PSM5eS73BpbVIgL3ZsmXeMKLVJurxzww1Z9axy3B2eUB3ioEytbQt4Qu0Od6qPluKrTDew9pSi9kEuPaw==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^10.3.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.24.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.3", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz", + "integrity": "sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", + "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", + "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", + "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", + "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", + "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", + "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", + "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", + "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", + "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", + "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", + "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", + "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", + "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", + "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", + "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", + "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", + "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", + "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", + "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", + "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", + "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", + "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", + "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", + "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", + "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.90.1.tgz", + "integrity": "sha512-vxb66dgo6h3yyPbR06735Ps+dK3hj0JwS8w9fdQPVZQmocSTlKUW5MfxSy99mN0XqCCuLMQ3jCEiIIUU23e9ng==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.90.1.tgz", + "integrity": "sha512-x9mV9dF1Lam9qL3zlpP6mSM5C9iqMPtF5B/tU1Jj/F0ufX5mjDf9ghVBaErVxmrQJRL4+iMKWKY2GnODkpS8tw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.90.1.tgz", + "integrity": "sha512-jh6vqzaYzoFn3raaC0hcFt9h+Bt+uxNRBSdc7PfToQeRGk7PDPoweHsbdiPWREtDVTGKfu+PyPW9e2jbK+BCgQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.90.1.tgz", + "integrity": "sha512-PWbnEMkcQRuor8jhObp4+Snufkq8C6fBp+MchVp2qBPY1NXk/c3Iv3YyiFYVzo0Dzuw4nAlT4+ahuPggy4r32w==", + "license": "MIT", + "dependencies": { + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.90.1.tgz", + "integrity": "sha512-GHY+Ps/K/RBfRj7kwx+iVf2HIdqOS43rM2iDOIDpapyUnGA9CCBFzFV/XvfzznGykd//z2dkGZhlZZprsVFqGg==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.90.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.90.1.tgz", + "integrity": "sha512-U8KaKGLUgTIFHtwEW1dgw1gK7XrdpvvYo7nzzqPx721GqPe8WZbAiLh/hmyKLGBYQ/mmQNr20vU9tWSDZpii3w==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.90.1", + "@supabase/functions-js": "2.90.1", + "@supabase/postgrest-js": "2.90.1", + "@supabase/realtime-js": "2.90.1", + "@supabase/storage-js": "2.90.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.5.tgz", + "integrity": "sha512-HfF8+mYcHPcPypui3w3mvzuIErlNOh2OAG+BCeBZCEwyiD5ls2SiCwEyT47OELtf7M3nHxBdu0FsmzdKxkN52Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", + "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/es-toolkit": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.43.0.tgz", + "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==", + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/framer-motion": { + "version": "12.26.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.26.1.tgz", + "integrity": "sha512-Uzc8wGldU4FpmGotthjjcj0SZhigcODjqvKT7lzVZHsmYkzQMFfMIv0vHQoXCeoe/Ahxqp4by4A6QbzFA/lblw==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.24.11", + "motion-utils": "^12.24.10", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gaxios": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2", + "rimraf": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-auth-library": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^8.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.562.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", + "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/motion-dom": { + "version": "12.24.11", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.11.tgz", + "integrity": "sha512-DlWOmsXMJrV8lzZyd+LKjG2CXULUs++bkq8GZ2Sr0R0RRhs30K2wtY+LKiTjhmJU3W61HK+rB0GLz6XmPvTA1A==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.24.10" + } + }, + "node_modules/motion-utils": { + "version": "12.24.10", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.24.10.tgz", + "integrity": "sha512-x5TFgkCIP4pPsRLpKoI86jv/q8t8FQOiM/0E8QKBzfMozWHfkKap2gA1hOki+B5g3IsBNpxbUnfOum1+dgvYww==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.3" + } + }, + "node_modules/react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "license": "MIT", + "peer": true + }, + "node_modules/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.12.0.tgz", + "integrity": "sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.12.0.tgz", + "integrity": "sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA==", + "license": "MIT", + "dependencies": { + "react-router": "7.12.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/recharts": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", + "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==", + "license": "MIT", + "workspaces": [ + "www" + ], + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", + "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.1", + "@rollup/rollup-android-arm64": "4.55.1", + "@rollup/rollup-darwin-arm64": "4.55.1", + "@rollup/rollup-darwin-x64": "4.55.1", + "@rollup/rollup-freebsd-arm64": "4.55.1", + "@rollup/rollup-freebsd-x64": "4.55.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", + "@rollup/rollup-linux-arm-musleabihf": "4.55.1", + "@rollup/rollup-linux-arm64-gnu": "4.55.1", + "@rollup/rollup-linux-arm64-musl": "4.55.1", + "@rollup/rollup-linux-loong64-gnu": "4.55.1", + "@rollup/rollup-linux-loong64-musl": "4.55.1", + "@rollup/rollup-linux-ppc64-gnu": "4.55.1", + "@rollup/rollup-linux-ppc64-musl": "4.55.1", + "@rollup/rollup-linux-riscv64-gnu": "4.55.1", + "@rollup/rollup-linux-riscv64-musl": "4.55.1", + "@rollup/rollup-linux-s390x-gnu": "4.55.1", + "@rollup/rollup-linux-x64-gnu": "4.55.1", + "@rollup/rollup-linux-x64-musl": "4.55.1", + "@rollup/rollup-openbsd-x64": "4.55.1", + "@rollup/rollup-openharmony-arm64": "4.55.1", + "@rollup/rollup-win32-arm64-msvc": "4.55.1", + "@rollup/rollup-win32-ia32-msvc": "4.55.1", + "@rollup/rollup-win32-x64-gnu": "4.55.1", + "@rollup/rollup-win32-x64-msvc": "4.55.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwind-merge": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3c9a9b --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "paraguay-arbitrage-crm", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@google/genai": "^1.35.0", + "@supabase/supabase-js": "^2.90.1", + "clsx": "^2.1.1", + "framer-motion": "^12.26.1", + "lucide-react": "^0.562.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "react-router-dom": "^7.12.0", + "recharts": "^3.6.0", + "tailwind-merge": "^3.4.0" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "~5.8.2", + "vite": "^6.2.0" + } +} diff --git a/pages/Customers.tsx b/pages/Customers.tsx new file mode 100644 index 0000000..a7fa2ac --- /dev/null +++ b/pages/Customers.tsx @@ -0,0 +1,167 @@ + +import React, { useState } from 'react'; +import { Users, Plus, Edit2, Trash2, Mail, Phone, MapPin } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import { Customer } from '../types'; + +const Customers: React.FC = () => { + const { customers, addCustomer, updateCustomer, deleteCustomer } = useCRM(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [formData, setFormData] = useState>({ + name: '', email: '', phone: '', city: '', status: 'Active' + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (editingId) { + updateCustomer(editingId, formData); + } else { + addCustomer(formData as any); + } + closeModal(); + }; + + const openModal = (customer?: Customer) => { + if (customer) { + setEditingId(customer.id); + setFormData(customer); + } else { + setEditingId(null); + setFormData({ name: '', email: '', phone: '', city: '', status: 'Active' }); + } + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + setEditingId(null); + }; + + return ( +
+ {/* HEADER */} +
+
+

Carteira de Clientes

+

Gestão de Relacionamento (CRM)

+
+ +
+ + {/* LIST */} +
+ + + + + + + + + + + + {customers.map(c => ( + + + + + + + + ))} + +
ClienteStatusContatoLTV (Total Pago)Ações
+
+
+ {c.name.charAt(0)} +
+
+

{c.name}

+

{c.city || 'Localização n/a'}

+
+
+
+ + {c.status === 'Active' ? 'ATIVO' : c.status === 'Prospect' ? 'PROSPECT' : 'INATIVO'} + + +
+ {c.email && ( +
+ {c.email} +
+ )} + {c.phone && ( +
+ {c.phone} +
+ )} +
+
+

R$ {c.totalPurchased?.toLocaleString('pt-BR') || '0,00'}

+
+
+ + +
+
+ {customers.length === 0 && ( +
NO CUSTOMERS FOUND.
+ )} +
+ + {/* MODAL */} + {isModalOpen && ( +
+
+

{editingId ? 'Editar Cliente' : 'Novo Cliente'}

+
+
+ + setFormData({ ...formData, name: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" /> +
+
+
+ + setFormData({ ...formData, phone: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" /> +
+
+ + +
+
+
+ + setFormData({ ...formData, email: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" /> +
+
+ + setFormData({ ...formData, city: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" /> +
+
+ + +
+
+
+
+ )} +
+ ); +}; + +export default Customers; diff --git a/pages/Dashboard.tsx b/pages/Dashboard.tsx new file mode 100644 index 0000000..4732ef3 --- /dev/null +++ b/pages/Dashboard.tsx @@ -0,0 +1,164 @@ + +import React from 'react'; +import { useCRM } from '../context/CRMContext'; +import { useNavigate } from 'react-router-dom'; +import clsx from 'clsx'; +import { TrendingUp, TrendingDown, DollarSign, Activity, ArrowRight, Package } from 'lucide-react'; + +const Dashboard: React.FC = () => { + const { orders, inventory, suppliers } = useCRM(); + const navigate = useNavigate(); + + // Financial Calculation + const activeOrders = orders.filter(o => o.status === 'Pending' || o.status === 'Paid'); + const totalInvested = activeOrders.reduce((acc, o) => acc + o.totalCostWithOverhead, 0); + // Mocking a growth metric for demonstration + const totalProfitHistory = orders.filter(o => o.status === 'Received').reduce((acc, o) => acc + o.estimatedProfit, 0); + const inventoryValue = inventory.reduce((acc, i) => acc + ((Number(i.marketPriceBRL) || 0) * (Number(i.quantity) || 0)), 0); + + // "Am I winning?" - Net Result (Mocked relative to investment for delta) + const netResultDelta = totalInvested > 0 ? (totalProfitHistory / totalInvested) * 100 : 0; + const isWinning = netResultDelta >= 0; + + return ( +
+ + {/* HERDER: "Am I winning?" Context */} +
+
+

Visão Geral

+

Performance financeira e operacional.

+
+
+

Resultado Líquido (YTD)

+
+ {isWinning ? : } + R$ {totalProfitHistory.toLocaleString('pt-BR')} +
+
+
+ + {/* HIGH PRECISION METRICS GRID */} +
+ {/* Metric 1: Capital Allocation */} +
+
+

Capital Alocado

+ +
+

R$ {totalInvested.toLocaleString('pt-BR')}

+
+ +12% vs. mês ant. +
+
+ + {/* Metric 2: Stock Value (Liquidity) */} +
+
+

Valor em Estoque

+ +
+

R$ {inventoryValue.toLocaleString('pt-BR')}

+
+ Giro: 15 dias +
+
+ + {/* Metric 3: Margin */} +
+
+

Margem Média

+ +
+

24.5%

+
+ -2.1% (Pressão de Custo) +
+
+ + {/* Metric 4: Active Orders */} +
+
+

Ordens Ativas

+ +
+

{activeOrders.length}

+
+ +
+
+
+ + {/* DATA DENSITY TABLE (Bloomberg Style) */} +
+ {/* ORDER FLOW */} +
+
+

Fluxo de Pedidos

+ +
+ + + + + + + + + + + {orders.length === 0 ? ( + + ) : ( + orders.slice(0, 8).map(o => ( + + + + + + + )) + )} + +
IDFornecedorValorStatus
Sem dados recentes.
{o.id.substring(0, 8)}...{o.supplierName}R$ {o.totalCostWithOverhead.toLocaleString('pt-BR')} + + {o.status.toUpperCase()} + +
+
+ + {/* SUPPLIER PERFORMANCE (Compact List) */} +
+
+

Top Fornecedores

+
+
+ {suppliers.slice(0, 6).map((s, i) => ( +
+
+ {i + 1} +
+

{s.name}

+

Eletrônicos • PY

+
+
+
+

+ R$ 45k

+
+
+ ))} +
+
+
+
+ ); +}; + +export default Dashboard; diff --git a/pages/Financial.tsx b/pages/Financial.tsx new file mode 100644 index 0000000..ca5afec --- /dev/null +++ b/pages/Financial.tsx @@ -0,0 +1,287 @@ +import React, { useState } from 'react'; +import { useCRM } from '../context/CRMContext'; +import { ArrowUpRight, ArrowDownRight, TrendingUp, DollarSign, Calendar, CheckCircle, AlertCircle, Plus } from 'lucide-react'; +import clsx from 'clsx'; +import { PaymentMethod, Transaction } from '../types'; + +const Financial: React.FC = () => { + const { transactions, getFinancialSummary, addTransaction, updateTransaction, loading } = useCRM(); + const { totalIncome, totalExpense, balance } = getFinancialSummary(); + + const [activeTab, setActiveTab] = useState<'payable' | 'receivable' | 'dashboard'>('dashboard'); + const [showAddModal, setShowAddModal] = useState(false); + + // New Bill State + const [newBill, setNewBill] = useState>({ + type: 'Expense', + status: 'Pending', + paymentMethod: 'Boleto', + date: new Date().toISOString().split('T')[0], + dueDate: new Date().toISOString().split('T')[0] + }); + + const pendingPayables = transactions.filter(t => t.type === 'Expense' && t.status === 'Pending').sort((a, b) => new Date(a.dueDate || '').getTime() - new Date(b.dueDate || '').getTime()); + const paidPayables = transactions.filter(t => t.type === 'Expense' && t.status === 'Paid'); + + // Sort Receivables by date desc + const receivables = transactions.filter(t => t.type === 'Income').sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());; + + const handleSaveBill = async () => { + if (!newBill.description || !newBill.amount) return alert("Preencha descrição e valor"); + + await addTransaction({ + type: 'Expense', + category: 'Contas', + description: newBill.description, + amount: Number(newBill.amount), + date: new Date(newBill.date!).toISOString(), + dueDate: new Date(newBill.dueDate!).toISOString(), + status: 'Pending', // Force pending + paymentMethod: newBill.paymentMethod as PaymentMethod || 'Boleto' + }); + + setShowAddModal(false); + setNewBill({ type: 'Expense', status: 'Pending', paymentMethod: 'Boleto', date: new Date().toISOString().split('T')[0], dueDate: new Date().toISOString().split('T')[0] }); + }; + + const togglePaid = async (t: Transaction) => { + const newStatus = t.status === 'Paid' ? 'Pending' : 'Paid'; + await updateTransaction(t.id, { status: newStatus }); + } + + const renderDashboard = () => ( +
+ {/* Summary Cards */} +
+
+
+ +
+
+

Entradas (Total)

+

R$ {totalIncome.toLocaleString('pt-BR')}

+
+ +12% este mês +
+
+
+ +
+
+ +
+
+

Saídas (Total)

+

R$ {totalExpense.toLocaleString('pt-BR')}

+
+ Estável +
+
+
+ +
+
+ +
+
+

Saldo Atual

+

= 0 ? "text-emerald-400" : "text-rose-400")}> + R$ {balance.toLocaleString('pt-BR')} +

+
+ Balanço Geral +
+
+
+
+ + {/* Upcoming Bills Alert */} + {pendingPayables.length > 0 && ( +
+
+ +
+
+

Contas a Pagar Pendentes

+

Você tem {pendingPayables.length} contas pendentes. Verifique a aba "A Pagar".

+
+ +
+ )} +
+ ); + + const renderPayables = () => ( +
+
+

Contas a Pagar

+ +
+ +
+ + + + + + + + + + + + + {pendingPayables.concat(paidPayables).map(t => { + const isOverdue = new Date(t.dueDate || '') < new Date() && t.status !== 'Paid'; + return ( + + + + + + + + + ); + })} + +
VencimentoDescriçãoValorViaStatusAção
+
+ + {t.dueDate ? new Date(t.dueDate).toLocaleDateString() : '-'} +
+
{t.description}- R$ {t.amount.toLocaleString('pt-BR')}{t.paymentMethod} + + {isOverdue ? 'Vencido' : t.status === 'Paid' ? 'Pago' : 'Pendente'} + + + +
+ {transactions.filter(t => t.type === 'Expense').length === 0 && ( +
Nenhuma conta registrada.
+ )} +
+
+ ); + + const renderReceivables = () => ( +
+

Contas a Receber (Vendas)

+
+ + + + + + + + + + + + {receivables.map(t => ( + + + + + + + + ))} + +
DataOrigemValorForma PagtoStatus
{new Date(t.date).toLocaleDateString()}{t.description}+ R$ {t.amount.toLocaleString('pt-BR')}{t.paymentMethod} + + {t.status || 'Paid'} + +
+ {receivables.length === 0 && ( +
Nenhuma venda registrada.
+ )} +
+
+ ); + + return ( +
+ {/* Tabs */} +
+ + + +
+ + {activeTab === 'dashboard' && renderDashboard()} + {activeTab === 'payable' && renderPayables()} + {activeTab === 'receivable' && renderReceivables()} + + {/* Add Bill Modal */} + {showAddModal && ( +
+
+

Agendar Pagamento

+
+ setNewBill({ ...newBill, description: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-sm text-white focus:border-indigo-500 outline-none" + /> + setNewBill({ ...newBill, amount: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-sm text-white focus:border-indigo-500 outline-none" + /> +
+
+ + setNewBill({ ...newBill, dueDate: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-sm text-white focus:border-indigo-500 outline-none" + /> +
+
+ + +
+
+ + +
+
+
+ )} +
+ ); +}; + +export default Financial; diff --git a/pages/Inventory.tsx b/pages/Inventory.tsx new file mode 100644 index 0000000..e46af5d --- /dev/null +++ b/pages/Inventory.tsx @@ -0,0 +1,58 @@ + +import React from 'react'; +import { Boxes } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; + +const Inventory: React.FC = () => { + const { inventory } = useCRM(); + + return ( +
+
+
+

Estoque & Armazenamento

+

Visão geral dos ativos em custódia.

+
+
+ +
+ {inventory.length > 0 ? inventory.map((item, idx) => ( +
+
+ +
+ +
+ {item.sku} + QTD: {item.quantity} +
+ +

{item.name}

+ +
+
+ Custo Base + R$ {item.avgCostBRL.toFixed(2)} +
+
+ Preço Alvo + R$ {item.marketPriceBRL.toFixed(2)} +
+
+ + +
+ )) : ( +
+ +

Nenhum item em estoque

+
+ )} +
+
+ ); +}; + +export default Inventory; diff --git a/pages/Login.tsx b/pages/Login.tsx new file mode 100644 index 0000000..0ee4e28 --- /dev/null +++ b/pages/Login.tsx @@ -0,0 +1,106 @@ +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Hexagon, ArrowRight, Lock, Mail } from 'lucide-react'; +import clsx from 'clsx'; +import { useCRM } from '../context/CRMContext'; +import Logo from '../components/Logo'; + +const Login: React.FC = () => { + const navigate = useNavigate(); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [loading, setLoading] = useState(false); + + const { signIn } = useCRM(); + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + const { error } = await signIn(email, password); + + if (error) { + alert("Erro ao entrar: " + error.message); + setLoading(false); + } else { + // Context logic handles redirect via Session state or we can force it here + navigate('/'); + } + }; + + return ( +
+ {/* Background Ambient Effects */} +
+
+ + {/* Login Card */} +
+
+ + {/* Header */} +
+ +
+ + {/* Form */} +
+
+ +
+
+ +
+ setEmail(e.target.value)} + className="w-full bg-secondary/30 border border-white/5 hover:border-white/10 focus:border-white/20 rounded-2xl py-4 pl-12 pr-4 text-sm text-foreground placeholder:text-muted-foreground/50 outline-none transition-all" + placeholder="admin@arbitra.com" + /> +
+
+ +
+ +
+
+ +
+ setPassword(e.target.value)} + className="w-full bg-secondary/30 border border-white/5 hover:border-white/10 focus:border-white/20 rounded-2xl py-4 pl-12 pr-4 text-sm text-foreground placeholder:text-muted-foreground/50 outline-none transition-all" + placeholder="••••••••" + /> +
+
+ + +
+ + {/* Footer */} +
+

+ Acesso Restrito • Segurança Criptografada +

+
+
+
+
+ ); +}; + +export default Login; diff --git a/pages/Orders.tsx b/pages/Orders.tsx new file mode 100644 index 0000000..b28b602 --- /dev/null +++ b/pages/Orders.tsx @@ -0,0 +1,270 @@ + +import React from 'react'; +import { Store, Trash2, FileEdit, FileCheck, Printer } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import { OrderStatus } from '../types'; + +const Orders: React.FC = () => { + const { orders, updateOrderStatus, resumeOrder, deleteOrder } = useCRM(); + + const handleDelete = async (id: string) => { + if (window.confirm("Tem certeza que deseja excluir esse pedido?")) { + await deleteOrder(id); + } + }; + + const handlePrint = (order: any) => { + const printWindow = window.open('', '_blank'); + if (printWindow) { + printWindow.document.write(` + + + Pedido #${order.id.substring(0, 8)} + + + +
+
+

Paraguai Imports

+

Relatório de Pedido

+
+
+

Data: ${new Date(order.date).toLocaleDateString()}

+

ID: #${order.id}

+

Status: ${order.status}

+
+
+ +
+

Fornecedor: ${order.supplierName}

+
+ + + + + + + + + + + + + ${order.items.map((item: any) => ` + + + + + + + + `).join('')} + +
ProdutoQtdPreço Unit. (USD)Total (USD)Total (BRL)
${item.name}${item.quantity}$ ${item.priceUSD.toFixed(2)}$ ${(item.priceUSD * item.quantity).toFixed(2)}R$ ${(item.priceBRL * item.quantity).toFixed(2)}
+ +
+

Total USD: $ ${order.totalUSD.toFixed(2)}

+

Total BRL (Sem Taxas): R$ ${order.totalBRL.toFixed(2)}

+

Custo Final Estimado: R$ ${order.totalCostWithOverhead.toLocaleString('pt-BR')}

+

Lucro Estimado: R$ ${order.estimatedProfit.toLocaleString('pt-BR')}

+
+ + + + + `); + printWindow.document.close(); + printWindow.print(); + } + }; + + return ( +
+
+
+

Fluxo de Pedidos (Order Flow)

+

Gestão de ciclo de vida e conciliação.

+
+
+ + {/* Desktop Table */} +
+ + + + + + + + + + + + + + {orders.map(o => ( + + + + + + + + + + ))} + +
ID / DataFornecedorCompra (USD)Custo BR (R$)Delta ($)StatusAções
+

{o.id.substring(0, 8)}...

+

{new Date(o.date).toLocaleDateString()}

+
+
+ + {o.supplierName} +
+
US$ {o.totalUSD.toFixed(2)}R$ {o.totalCostWithOverhead.toLocaleString('pt-BR')} + + {o.estimatedProfit.toLocaleString('pt-BR')} + + + +
+ + + + +
+
+
+ + {/* Mobile Card View */} +
+ {orders.map(o => ( +
+
+
+

Pedido

+

{o.id.substring(0, 8)}...

+

{new Date(o.date).toLocaleDateString()}

+
+
+ {o.status} +
+
+ +
+
+

Fornecedor

+
+ + {o.supplierName} +
+
+
+

Total (BRL)

+

R$ {o.totalCostWithOverhead.toLocaleString('pt-BR')}

+
+
+ +
+ + +
+ + + +
+
+
+ ))} +
+ + {orders.length === 0 && ( +
NO ACTIVE ORDERS.
+ )} +
+ ); +}; + +export default Orders; diff --git a/pages/Products.tsx b/pages/Products.tsx new file mode 100644 index 0000000..fb8b661 --- /dev/null +++ b/pages/Products.tsx @@ -0,0 +1,261 @@ + +import React, { useState } from 'react'; +import { useCRM } from '../context/CRMContext'; +import { Plus, Search, Edit2, Trash2, Save, X, ScanBarcode, Package } from 'lucide-react'; +import { InventoryItem } from '../types'; +import clsx from 'clsx'; + +const Products: React.FC = () => { + const { inventory, addProduct, updateProduct, deleteProduct } = useCRM(); + const [searchTerm, setSearchTerm] = useState(''); + const [isModalOpen, setIsModalOpen] = useState(false); + const [editingProduct, setEditingProduct] = useState(null); + + // Form State + const [formData, setFormData] = useState>({ + name: '', + sku: '', + ean: '', + quantity: 0, + avgCostBRL: 0, + marketPriceBRL: 0, // This is Sale Price + lastSupplier: 'Manual' + }); + + const filteredProducts = inventory.filter(p => + p.name.toLowerCase().includes(searchTerm.toLowerCase()) || + p.sku.toLowerCase().includes(searchTerm.toLowerCase()) || + (p.ean && p.ean.includes(searchTerm)) + ); + + const handleOpenModal = (product?: InventoryItem) => { + if (product) { + setEditingProduct(product); + setFormData(product); + } else { + setEditingProduct(null); + setFormData({ + name: '', + sku: '', + ean: '', + quantity: 0, + avgCostBRL: 0, + marketPriceBRL: 0, + lastSupplier: 'Manual' + }); + } + setIsModalOpen(true); + }; + + const handleSave = async () => { + if (!formData.name || !formData.sku) { + alert('Nome e SKU são obrigatórios.'); + return; + } + + try { + if (editingProduct) { + await updateProduct(editingProduct.id, formData); + } else { + await addProduct(formData as Omit); + } + setIsModalOpen(false); + } catch (error: any) { + console.error(error); + alert(`Erro ao salvar produto: ${error.message || JSON.stringify(error)}`); + } + }; + + const handleDelete = async (id: string) => { + if (confirm('Tem certeza que deseja excluir este produto?')) { + await deleteProduct(id); + } + }; + + return ( +
+ {/* Validating EAN logic exists in types from previous turn? Yes if applied. */} + +
+
+

Produtos

+

Gerencie o catálogo de produtos, SKUs e EANs.

+
+ +
+ + {/* Search */} +
+ + setSearchTerm(e.target.value)} + className="bg-transparent border-none text-white text-sm focus:ring-0 w-full p-3 placeholder:text-slate-600" + /> +
+ + {/* Table */} +
+ + + + + + + + + + + + {filteredProducts.map((p) => ( + + + + + + + + ))} + +
ProdutoCódigosEstoquePreço Venda (BRL)Ações
+

{p.name}

+

{p.lastSupplier}

+
+
+
+ SKU + {p.sku} +
+ {p.ean && ( +
+ EAN + {p.ean} +
+ )} +
+
+ {p.quantity} + + R$ {p.marketPriceBRL?.toLocaleString('pt-BR', { minimumFractionDigits: 2 })} + +
+ + +
+
+ {filteredProducts.length === 0 && ( +
+ +

Nenhum produto encontrado.

+
+ )} +
+ + {/* Modal */} + {isModalOpen && ( +
+
+
+

{editingProduct ? 'Editar Produto' : 'Novo Produto'}

+ +
+ +
+ {/* Basic Info */} +
+ + setFormData({ ...formData, name: e.target.value })} + placeholder="Ex: iPhone 15 Pro Max 256GB" + /> +
+ +
+
+ + setFormData({ ...formData, sku: e.target.value })} + placeholder="Ex: IPH-15-PM-256" + /> +
+
+ +
+ + setFormData({ ...formData, ean: e.target.value })} + placeholder="Ex: 789..." + /> +
+
+
+ + {/* Prices & Stock */} +
+
+ + setFormData({ ...formData, quantity: parseInt(e.target.value) || 0 })} + /> +
+
+ + setFormData({ ...formData, avgCostBRL: parseFloat(e.target.value) || 0 })} + /> +
+
+ + setFormData({ ...formData, marketPriceBRL: parseFloat(e.target.value) || 0 })} + /> +
+
+ +
+ + setFormData({ ...formData, lastSupplier: e.target.value })} + placeholder="Ex: Apple Store PY" + /> +
+ + +
+
+
+ )} +
+ ); +}; + +export default Products; diff --git a/pages/Reports.tsx b/pages/Reports.tsx new file mode 100644 index 0000000..8d85016 --- /dev/null +++ b/pages/Reports.tsx @@ -0,0 +1,306 @@ + +import React, { useState, useMemo } from 'react'; +import { useCRM } from '../context/CRMContext'; +import { + BarChart3, TrendingUp, PieChart, Package, ArrowUpRight, ArrowDownRight, + Download, Calendar, Filter, DollarSign, Activity, AlertCircle, Boxes +} from 'lucide-react'; +import { + BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, + LineChart, Line, AreaChart, Area +} from 'recharts'; +import clsx from 'clsx'; + +const Reports: React.FC = () => { + const { orders, inventory, transactions, sales } = useCRM(); + const [period, setPeriod] = useState<'30d' | '3m' | '6m' | 'ytd' | '1y' | 'all'>('30d'); + const [activeTab, setActiveTab] = useState<'financial' | 'products' | 'inventory' | 'sales'>('financial'); + + // --- FILTERING LOGIC --- + const filteredSales = useMemo(() => { + const now = new Date(); + const cutoff = new Date(); + + if (period === '30d') cutoff.setDate(now.getDate() - 30); + else if (period === '3m') cutoff.setMonth(now.getMonth() - 3); + else if (period === '6m') cutoff.setMonth(now.getMonth() - 6); + else if (period === 'ytd') cutoff.setMonth(0, 1); // Jan 1st of current year + else if (period === '1y') cutoff.setFullYear(now.getFullYear() - 1); + else if (period === 'all') return sales; // No filter + + return sales.filter(s => new Date(s.date) >= cutoff); + }, [sales, period]); + + // 1. Financial Metrics + const financialStats = useMemo(() => { + // Revenue + const totalRevenue = filteredSales.reduce((acc, s) => acc + s.total, 0); + + // Cost of Goods Sold (COGS) based on captured costPrice at time of sale + const totalCost = filteredSales.reduce((acc, s) => { + const saleCost = s.items.reduce((iAcc, item) => iAcc + ((item.costPrice || 0) * item.quantity), 0); + return acc + saleCost; + }, 0); + + const totalProfit = totalRevenue - totalCost; + const margin = totalRevenue > 0 ? (totalProfit / totalRevenue) * 100 : 0; + + return { totalRevenue, totalCost, totalProfit, margin }; + }, [filteredSales]); + + // 2. Product/Arbitrage Metrics + const productStats = useMemo(() => { + const productPerformance: Record = {}; + + filteredSales.forEach(s => { + s.items.forEach(item => { + if (!productPerformance[item.name]) { + productPerformance[item.name] = { profit: 0, revenue: 0, quantity: 0 }; + } + const itemRevenue = item.salePrice; + const itemCost = item.costPrice || 0; + + productPerformance[item.name].profit += (itemRevenue - itemCost) * item.quantity; + productPerformance[item.name].revenue += itemRevenue * item.quantity; + productPerformance[item.name].quantity += item.quantity; + }); + }); + + const sortedProducts = Object.entries(productPerformance) + .map(([name, stats]) => ({ name, ...stats })) + .sort((a, b) => b.profit - a.profit); + + return { + topProducts: sortedProducts.slice(0, 5), + lowMarginProducts: sortedProducts.filter(p => p.revenue > 0 && (p.profit / p.revenue) < 0.10).slice(0, 5) + }; + }, [filteredSales]); + + // 3. Inventory Stats (Unchanged) + const inventoryStats = useMemo(() => { + const totalValue = inventory.reduce((acc, i) => acc + (i.avgCostBRL * i.quantity), 0); + const totalItems = inventory.reduce((acc, i) => acc + i.quantity, 0); + const deadStock = inventory.filter(i => i.quantity > 0).slice(0, 3); + return { totalValue, totalItems, deadStock }; + }, [inventory]); + + // 4. Chart Data - Responsive to Period + const chartData = useMemo(() => { + const dataMap: Record = {}; + + filteredSales.forEach(sale => { + if (sale.status === 'Cancelled' || sale.status === 'Returned') return; + const d = new Date(sale.date); + + // Dynamic grouping key + let key = ''; + if (period === '30d') { + key = d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' }); // Daily for 30d + } else { + key = d.toLocaleDateString('pt-BR', { month: 'short', year: '2-digit' }); // Monthly for others + } + + if (!dataMap[key]) dataMap[key] = { receita: 0, lucro: 0, date: d.getTime() }; + + const saleCost = sale.items.reduce((acc, item) => acc + ((item.costPrice || 0) * item.quantity), 0); + + dataMap[key].receita += sale.total; + dataMap[key].lucro += (sale.total - saleCost); + }); + + return Object.entries(dataMap) + .map(([name, vals]) => ({ name, ...vals })) + .sort((a, b) => a.date - b.date); // Sort chronologically + }, [filteredSales, period]); // Recalc on filter change + + + // --- EXPORT --- + const handleExport = () => { + const csvContent = "data:text/csv;charset=utf-8," + + "Categoria,Valor\n" + + `Receita Total,${financialStats.totalRevenue}\n` + + `Custo Total,${financialStats.totalCost}\n` + + `Lucro Liquido,${financialStats.totalProfit}\n`; + + const encodedUri = encodeURI(csvContent); + const link = document.createElement("a"); + link.setAttribute("href", encodedUri); + link.setAttribute("download", "relatorio_arbitra.csv"); + document.body.appendChild(link); + link.click(); + }; + + + return ( +
+ + {/* HEADER */} +
+
+

Relatórios & Analytics

+

Visão geral de performance, financeiro e estoque.

+
+ +
+
+ {[ + { id: '30d', label: '30D' }, + { id: '3m', label: '3M' }, + { id: '6m', label: '6M' }, + { id: 'ytd', label: 'YTD' }, + { id: '1y', label: '1 Ano' }, + { id: 'all', label: 'Tudo' } + ].map((p) => ( + + ))} +
+ +
+
+ + {/* KPI CARDS */} +
+ {[ + { label: 'Receita Total', value: `R$ ${financialStats.totalRevenue.toLocaleString('pt-BR')}`, icon: DollarSign, color: 'text-emerald-400', sub: '+12% vs. mês anterior' }, + { label: 'Lucro Líquido', value: `R$ ${financialStats.totalProfit.toLocaleString('pt-BR')}`, icon: TrendingUp, color: 'text-indigo-400', sub: `${financialStats.margin.toFixed(1)}% Margem` }, + { label: 'Valor em Estoque', value: `R$ ${inventoryStats.totalValue.toLocaleString('pt-BR')}`, icon: Package, color: 'text-blue-400', sub: `${inventoryStats.totalItems} iten(s)` }, + { label: 'Pedidos Realizados', value: orders.length, icon: Activity, color: 'text-amber-400', sub: 'Volume total' }, + ].map((stat, idx) => ( +
+
+
+ +
+ + +2.5% + +
+

{stat.value}

+

{stat.label}

+

{stat.sub}

+
+ ))} +
+ + {/* TABS */} +
+ {[ + { id: 'financial', label: 'Financeiro', icon: DollarSign }, + { id: 'products', label: 'Produtos', icon: Package }, + { id: 'inventory', label: 'Estoque', icon: Boxes }, + { id: 'sales', label: 'Vendas', icon: BarChart3 } + ].map(tab => ( + + ))} +
+ + {/* CONTENT AREA */} +
+ + {/* LEFT: MAIN CHART using Recharts */} +
+

Performance Financeira

+
+ + + + + + + + + + + + + + + `R$${value / 1000}k`} /> + + + + + +
+
+ + {/* RIGHT: METRIC LISTS */} +
+ {/* TOP PRODUCTS */} +
+

Produtos + Lucrativos

+
+ {productStats.topProducts.length > 0 ? productStats.topProducts.map((p, i) => ( +
+
+
+ {i + 1} +
+
+

{p.name}

+

{p.quantity} vendidos

+
+
+ R$ {p.profit.toLocaleString('pt-BR', { maximumFractionDigits: 0 })} +
+ )) : ( +

Sem dados de venda.

+ )} +
+
+ + {/* ALERTS */} +
+

Alertas de Estoque

+
+
+ +
+

Baixo Giro

+

3 produtos não vendem há 30 dias.

+
+
+
+ +
+

Reabastecer

+

5 SKUs abaixo do estoque mínimo.

+
+
+
+
+
+
+
+ ); +}; + +export default Reports; diff --git a/pages/Sales.tsx b/pages/Sales.tsx new file mode 100644 index 0000000..d2aa3f7 --- /dev/null +++ b/pages/Sales.tsx @@ -0,0 +1,558 @@ + +import React, { useState } from 'react'; +import { useCRM } from '../context/CRMContext'; +import { Search, ShoppingCart, Trash2, Plus, Minus, CreditCard, ChevronRight, Calculator, Package, AlertTriangle, Download, History, Store, CheckSquare, Square, UserPlus, Pencil, X, Banknote, CreditCard as CardIcon } from 'lucide-react'; +import { InventoryItem, Sale, SalesChannel, Customer, PaymentMethod } from '../types'; +import clsx from 'clsx'; + +const Sales: React.FC = () => { + const { inventory, customers, registerSale, sales, importSales, updateSale, loading, addCustomer, updateProduct, isAdmin } = useCRM(); + + // State + const [viewMode, setViewMode] = useState<'list' | 'pos'>('list'); // 'list' = Pedidos, 'pos' = Frente de Caixa + + // POS State + const [searchTerm, setSearchTerm] = useState(''); + const [cart, setCart] = useState<{ item: InventoryItem; quantity: number; price: number }[]>([]); + const [selectedCustomer, setSelectedCustomer] = useState(''); + const [paymentMethod, setPaymentMethod] = useState('Cash'); // New state + const [isFinalizing, setIsFinalizing] = useState(false); + + // Import Modal State + const [showImportModal, setShowImportModal] = useState(false); + const [importChannel, setImportChannel] = useState('Mercado Livre'); + + // Quick Customer Modal + const [showCustomerModal, setShowCustomerModal] = useState(false); + const [newCustomer, setNewCustomer] = useState({ name: '', email: '', phone: '' }); + + // Quick Edit Product Modal + const [editingProduct, setEditingProduct] = useState(null); + const [editForm, setEditForm] = useState({ price: 0, stock: 0 }); + + // Filter Inventory + const filteredInventory = inventory.filter(i => + i.name.toLowerCase().includes(searchTerm.toLowerCase()) || + i.sku.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + // Cart Actions + const addToCart = (item: InventoryItem) => { + const existing = cart.find(c => c.item.id === item.id); + if (existing) { + if (existing.quantity >= item.quantity) return; // Max stock reached + setCart(cart.map(c => c.item.id === item.id ? { ...c, quantity: c.quantity + 1 } : c)); + } else { + if (item.quantity <= 0) return; + setCart([...cart, { item, quantity: 1, price: Number(item.marketPriceBRL) || 0 }]); + } + }; + + const updateQuantity = (id: string, delta: number) => { + setCart(cart.map(c => { + if (c.item.id === id) { + const newQty = c.quantity + delta; + if (newQty <= 0) return c; // Don't remove here, need explicit remove + if (newQty > c.item.quantity) return c; + return { ...c, quantity: newQty }; + } + return c; + })); + }; + + const removeFromCart = (id: string) => { + setCart(cart.filter(c => c.item.id !== id)); + }; + + const updatePrice = (id: string, newPrice: number) => { + setCart(cart.map(c => c.item.id === id ? { ...c, price: newPrice } : c)); + }; + + // Totals + const totalAmount = cart.reduce((acc, c) => acc + (c.price * c.quantity), 0); + const totalItems = cart.reduce((acc, c) => acc + c.quantity, 0); + + const [showSuccessModal, setShowSuccessModal] = useState(false); + const [lastSaleTotal, setLastSaleTotal] = useState(0); + + const handleFinalize = async () => { + if (cart.length === 0) return; + setIsFinalizing(true); + + const saleTotal = cart.reduce((acc, c) => acc + (c.price * c.quantity), 0); + setLastSaleTotal(saleTotal); + + await registerSale( + cart.map(c => ({ id: c.item.id, quantity: c.quantity, salePrice: c.price })), + selectedCustomer || undefined, + paymentMethod // Pass new arg + ); + + setCart([]); + setSelectedCustomer(''); + setPaymentMethod('Cash'); // Reset + setIsFinalizing(false); + setShowSuccessModal(true); + }; + + const handleNewSale = () => { + setShowSuccessModal(false); + }; + + const handlePrintReceipt = () => { + alert("Impressão de Recibo iniciada... (Mock)"); + // window.print(); + }; + + const handleSendEmail = () => { + const email = customers.find(c => c.id === selectedCustomer)?.email || "cliente@exemplo.com"; + alert(`Comprovante enviado para ${email} (Mock)`); + }; + + // --- IMPORT HANDLERS --- + const handleImport = async () => { + await importSales(importChannel); + setShowImportModal(false); + }; + + // --- QUICK ACTIONS --- + const handleSaveCustomer = async () => { + if (!newCustomer.name) return alert("Nome é obrigatório"); + try { + const created = await addCustomer({ ...newCustomer, status: 'Active', city: 'Foz do Iguaçu' } as any); + if (created) { + setNewCustomer({ name: '', email: '', phone: '' }); + setShowCustomerModal(false); + setSelectedCustomer(created.id); // Auto-select + } + } catch (error) { + console.error(error); + alert("Erro ao cadastrar cliente. Verifique o console."); + } + }; + + const handleSaveProductEdit = async () => { + if (!editingProduct) return; + await updateProduct(editingProduct.id, { + marketPriceBRL: editForm.price, + quantity: editForm.stock + }); + setEditingProduct(null); + }; + + // --- UI RENDERERS --- + + // 1. SALES LIST VIEW + const renderSalesList = () => ( +
+
+
+

Histórico de Pedidos

+

Gerencie vendas locais e importadas.

+
+
+ + +
+
+ +
+ + + + + + + + + + + + + + {sales.map(sale => ( + + + + + + + + + + ))} + +
Data / IDCanalClienteTotalStatusBaixar EstoqueLançar Financeiro
+
{new Date(sale.date).toLocaleDateString()}
+
#{sale.externalId || sale.id.substring(0, 8)}
+
+
+ {sale.channel === 'Mercado Livre' && MeLi} + {sale.channel === 'Shopee' && Shopee} + {sale.channel === 'Amazon' && Amazon} + {sale.channel === 'Local' && Balcão} +
+
+ {sale.customerName || 'Cliente Consumidor'} + + R$ {sale.total.toLocaleString('pt-BR', { minimumFractionDigits: 2 })} + + + {sale.status} + + + + + +
+ {sales.length === 0 && ( +
+ +

Nenhuma venda registrada

+
+ )} +
+ + {/* IMPORT MODAL */} + {showImportModal && ( +
+
+

Importar Pedidos

+ +
+
+ + +
+ +
+
+ +
+
+ Simularemos a conexão com a API do {importChannel} para buscar novos pedidos pendentes. +
+
+
+ +
+ + +
+
+
+ )} +
+ ); + + + return ( +
+ {viewMode === 'list' ? renderSalesList() : ( +
+ +
+ {/* LEFT: POS / INVENTORY */} +
+ {/* ... Search Bar and Grid kept as children ... */} + {/* RE-INSERTING ORIGINAL POS CONTENT WRAPPED IN FRAGMENT OR DIV IF NEEDED */} + + {/* Search Bar */} +
+
+ + setSearchTerm(e.target.value)} + placeholder="Buscar produto por nome ou SKU..." + className="w-full bg-slate-900/50 border border-white/10 rounded-2xl py-4 pl-12 pr-4 text-white focus:border-indigo-500 outline-none transition-all placeholder:text-slate-600" + /> +
+
+ + {/* Grid */} +
+
+ {filteredInventory.map(item => ( +
addToCart(item)} + className={clsx( + "p-5 rounded-[24px] border transition-all cursor-pointer group relative overflow-hidden", + item.quantity === 0 ? "opacity-50 grayscale border-white/5 bg-white/5 cursor-not-allowed" : "bg-white/5 border-white/5 hover:border-indigo-500/50 hover:bg-white/10 active:scale-95" + )} + > +
+ {item.sku} + {item.quantity <= 3 && item.quantity > 0 && ( +
+ Baixo Estoque +
+ )} + {isAdmin && ( + + )} +
+

{item.name}

+
+
+

Preço Venda

+

R$ {(Number(item.marketPriceBRL) || 0).toLocaleString('pt-BR')}

+
+
+

Estoque

+

{item.quantity}

+
+
+
+ ))} +
+
+
+ + {/* RIGHT: CART / CHECKOUT */} +
+
+ + {/* Header */} +
+
+
+ +
+
+

Carrinho

+

Nova Venda

+
+
+ {totalItems} itens +
+ + {/* Customer Select (Optional) */} +
+ + +
+ + {/* Cart Items */} +
+ {cart.length === 0 ? ( +
+ +

Carrinho Vazio

+
+ ) : ( + cart.map((c, idx) => ( +
+
+

{c.item.name}

+ +
+ +
+
+ + {c.quantity} + +
+ +
+
+ R$ + updatePrice(c.item.id, parseFloat(e.target.value))} + className="w-full bg-black/30 border border-white/10 rounded-lg py-1.5 pl-8 pr-2 text-right text-xs font-bold text-white focus:border-indigo-500 outline-none" + /> +
+
+
+
+ Subtotal: + R$ {(c.price * c.quantity).toLocaleString('pt-BR')} +
+
+ )) + )} +
+ + {/* Summary & Action */} +
+
+
+ Itens + {totalItems} UN +
+
+ Total Geral + R$ {totalAmount.toLocaleString('pt-BR')} +
+
+ + {/* Payment Method Selector */} +
+

Forma de Pagamento

+
+ {['Cash', 'Pix', 'Credit Card'].map(method => ( + + ))} +
+
+ + +
+ +
+
+
+
+ )} + {/* SUCCESS MODAL */} + {showSuccessModal && ( +
+
+ {/* Confetti or success decoration could go here */} +
+ +
+
+ +
+

Venda Realizada!

+

Total processado com sucesso.

+
R$ {lastSaleTotal.toLocaleString('pt-BR')}
+
+ +
+ + + + + +
+
+
+ )} +
+ ); +}; + +export default Sales; diff --git a/pages/Settings.tsx b/pages/Settings.tsx new file mode 100644 index 0000000..1e2e954 --- /dev/null +++ b/pages/Settings.tsx @@ -0,0 +1,452 @@ + +import React, { useState } from 'react'; +import { Settings as SettingsIcon, Truck, Key, Building, Users, Save, Globe, Smartphone, Bell, Shield, FileCheck, ShoppingBag, RefreshCw, AlertCircle, Upload, Palette } from 'lucide-react'; +import clsx from 'clsx'; +import { useCRM } from '../context/CRMContext'; +import { useTheme } from '../context/ThemeContext'; + +const ThemeSelector = () => { + const { theme, setTheme } = useTheme(); + + return ( +
+

Tema do Sistema

+ +
+ {[ + { id: 'light', name: 'Simples (Clean)', color: 'bg-slate-50 border-2 border-slate-200', accent: 'bg-blue-600', text: 'Visual limpo e direto.' }, + { id: 'ocean', name: 'Complexo (Ocean)', color: 'bg-[#0f172a]', accent: 'bg-cyan-500', text: 'Alta densidade e contraste.' }, + { id: 'dark', name: 'Completo (Pro)', color: 'bg-[#0a0a0c]', accent: 'bg-indigo-600', text: 'Interface imersiva padrão.' }, + ].map(t => ( + + ))} +
+
+ ); +}; + +const Settings: React.FC = () => { + const { overheadPercent, exchangeRate, settings, updateSettings } = useCRM(); + const [activeTab, setActiveTab] = useState<'general' | 'fiscal' | 'marketplaces' | 'logistics' | 'email' | 'notifications'>('general'); + + // Local state for form, initialized with context settings + const [config, setConfig] = useState(settings || { + companyName: 'Arbitra System', + cnpj: '', + ie: '', + defaultOverhead: 20, + defaultExchange: 5.65, + certificateName: '', + certificatePassword: '', + geminiKey: 'sk-....................', + melhorEnvioToken: '', + blingToken: '', + tinyToken: '', + whatsappNumber: '', + nfeSerie: '1', + nfeNumber: '159', + autoSyncSales: true, + autoSyncStock: true, + smtpHost: '', + smtpPort: '587', + smtpUser: '', + smtpPass: '', + }); + + // Sync state when settings load from DB + React.useEffect(() => { + if (settings) { + setConfig(prev => ({ ...prev, ...settings })); + } + }, [settings]); + + const handleSave = async () => { + try { + await updateSettings(config); + alert('Configurações salvas com sucesso!'); + } catch (error) { + console.error(error); + alert('Erro ao salvar configurações.'); + } + }; + + const tabs = [ + { id: 'general', label: 'Geral', icon: Building, description: 'Dados cadastrais' }, + { id: 'fiscal', label: 'Fiscal & NFe', icon: FileCheck, description: 'Certificado e tributação' }, + { id: 'appearance', label: 'Aparência', icon: Palette, description: 'Temas e Cores' }, + { id: 'marketplaces', label: 'Marketplaces', icon: ShoppingBag, description: 'Integrações de vendas' }, + { id: 'logistics', label: 'Logística', icon: Truck, description: 'Fretes e entregas' }, + { id: 'email', label: 'Email SMTP', icon: Globe, description: 'Configuração de envio' }, + { id: 'notifications', label: 'Notificações', icon: Bell, description: 'Alertas e avisos' }, + ]; + + return ( +
+ +
+
+

Configurações do Sistema

+

Gerencie todos os parâmetros fiscais, integrações e regras de negócio.

+
+ +
+ +
+ + {/* Denser Sidebar */} +
+ {tabs.map(tab => ( + + ))} +
+ + {/* Content Area - Denser Forms */} +
+
+ + {activeTab === 'general' && ( +
+ {/* Section */} +
+

Identificação da Empresa

+
+
+ + setConfig({ ...config, cnpj: e.target.value })} + placeholder="00.000.000/0000-00" + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all font-mono" + /> +
+
+ + setConfig({ ...config, ie: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all" + /> +
+
+ + setConfig({ ...config, companyName: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all" + /> +
+
+
+ + {/* Section */} +
+

Parâmetros Financeiros

+
+
+ +
+ R$ + setConfig({ ...config, defaultExchange: parseFloat(e.target.value) })} + className="w-full bg-black/20 border border-white/10 rounded-lg pl-8 pr-3 py-2 text-sm text-slate-200 focus:border-emerald-500 outline-none transition-all font-mono" + /> +
+
+
+ +
+ % + setConfig({ ...config, defaultOverhead: parseFloat(e.target.value) })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all font-mono" + /> +
+
+
+
+
+ )} + + {activeTab === 'appearance' && ( +
+ +
+ )} + + {activeTab === 'fiscal' && ( +
+ {/* Certificate */} +
+

Certificado Digital (A1)

+ +
+ +

Clique para selecionar o Certificado .PFX

+

Sua senha será solicitada após o upload

+
+ +
+
+ + setConfig({ ...config, certificatePassword: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all" + /> +
+
+ +
+ --/--/---- + +
+
+
+
+ + {/* NFe Config */} +
+

Configuração de Emissão (NFe 4.0)

+
+
+ + setConfig({ ...config, nfeNumber: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all font-mono" + /> +
+
+ + setConfig({ ...config, nfeSerie: e.target.value })} + className="w-full bg-black/20 border border-white/10 rounded-lg px-3 py-2 text-sm text-slate-200 focus:border-indigo-500 outline-none transition-all font-mono" + /> +
+
+ + +
+
+
+
+ )} + + {activeTab === 'marketplaces' && ( +
+ {/* Major Marketplaces */} +
+

Hub de Integração (Mais de 30 canais)

+ +
+ {[ + { name: 'Mercado Livre', color: 'bg-yellow-500', status: 'connected', sales: 1240 }, + { name: 'Shopee', color: 'bg-orange-600', status: 'error', sales: 0 }, + { name: 'Amazon Seller', color: 'bg-slate-800', status: 'disconnected', sales: 0 }, + { name: 'Magalu', color: 'bg-blue-600', status: 'disconnected', sales: 0 }, + { name: 'Via Varejo', color: 'bg-green-600', status: 'disconnected', sales: 0 }, + { name: 'B2W (Americanas)', color: 'bg-red-600', status: 'disconnected', sales: 0 }, + { name: 'Shein', color: 'bg-black', status: 'disconnected', sales: 0 }, + { name: 'AliExpress', color: 'bg-red-500', status: 'disconnected', sales: 0 }, + ].map(mk => ( +
+
+
+
+ {mk.name.substring(0, 2)} +
+
+

{mk.name}

+ {mk.status === 'connected' &&

● Online

} + {mk.status === 'error' &&

● Erro

} + {mk.status === 'disconnected' &&

● Offline

} +
+
+ + {/* Toggle Switch Mockup */} +
+
+
+
+ +
+
+ {mk.status === 'connected' ? ( + <> +

Vendas Hoje

+

R$ {mk.sales.toLocaleString('pt-BR')}

+ + ) : ( +

Clique para configurar

+ )} +
+ +
+
+ ))} +
+
+ + {/* Custom Sites / E-commerce Platforms */} +
+

Lojas Virtuais & Sites Próprios

+
+ {[ + { name: 'WooCommerce', color: 'bg-purple-600' }, + { name: 'Shopify', color: 'bg-emerald-500' }, + { name: 'Nuvemshop', color: 'bg-blue-500' }, + { name: 'Vtex', color: 'bg-pink-600' }, + { name: 'API Personalizada', color: 'bg-slate-700', isCustom: true }, + ].map(platform => ( +
+
+
+ {platform.isCustom ? : platform.name.substring(0, 1)} +
+ {platform.name} +
+
+
+
+
+ ))} +
+

* Para API Personalizada, consulte a documentação /docs/api-v1

+
+ + {/* Automation Settings */} +
+
+

Regras de Sincronização

+
+
+
+

Importar Pedidos

+

Baixar vendas novas a cada 5 min.

+
+ setConfig({ ...config, autoSyncSales: !config.autoSyncSales })} className="accent-emerald-500 w-4 h-4" /> +
+
+
+

Atualizar Estoque

+

Enviar saldo local para os canais.

+
+ setConfig({ ...config, autoSyncStock: !config.autoSyncStock })} className="accent-emerald-500 w-4 h-4" /> +
+
+
+
+
+ )} + + {activeTab === 'logistics' && ( +
+
+

Hubs de Frete

+ +
+
+
+
+ + Melhor Envio +
+ Ativo +
+ setConfig({ ...config, melhorEnvioToken: e.target.value })} + placeholder="Token de Produção" + type="password" + className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-slate-400 text-xs focus:border-indigo-500 outline-none transition-all font-mono" + /> +
+
+
+
+ + Frenet +
+ Inativo +
+ +
+
+
+
+ )} +
+
+
+
+ ); +}; + +export default Settings; diff --git a/pages/Sourcing.tsx b/pages/Sourcing.tsx new file mode 100644 index 0000000..153e915 --- /dev/null +++ b/pages/Sourcing.tsx @@ -0,0 +1,234 @@ + +import React from 'react'; +import { Search, Package, Plus, Calculator, Trash2, Minus, ShoppingCart, CheckCircle2, Facebook, Tag, ToggleLeft, ToggleRight } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import MarketplaceAnalytic from '../components/MarketplaceAnalytic'; + +const Sourcing: React.FC = () => { + const { + searchTerm, setSearchTerm, handleSearch, handleOpportunitySearch, products, + selectedProduct, setSelectedProduct, addToShoppingList, + shoppingList, removeFromShoppingList, + updateShoppingItemQuantity, saveOrderAsQuotation, + calculateShoppingTotals, overheadPercent, + useOverhead, setUseOverhead, + searchLoading, searchError, searchType, setSearchType + } = useCRM(); + + const onSearchSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (searchType === 'specific') { + handleSearch(e); + } else { + handleOpportunitySearch(searchTerm); + } + }; + + const handleUpdateQuantity = (item: any, delta: number) => { + updateShoppingItemQuantity(item.id, delta); + }; + + const handleRemoveItem = (id: string) => { + removeFromShoppingList(id); + }; + + return ( +
+ {/* COLUNA 1: BUSCA */} +
+
+ {/* TABS */} +
+ + +
+ +
+ {searchType === 'specific' ? : } +

+ {searchType === 'specific' ? 'Sourcing Real-Time' : 'Caçador de Margem (>25%)'} +

+
+ +
+ setSearchTerm(e.target.value)} + placeholder={searchType === 'specific' ? "Ex: iPhone 16 Pro Max" : "Ex: Celulares, Drones, Games"} + className={`w-full bg-slate-900/50 border border-white/10 rounded-2xl py-4 pl-12 pr-4 text-sm font-semibold outline-none transition-all text-white placeholder-slate-600 shadow-inner ${searchType === 'specific' ? 'focus:border-indigo-500' : 'focus:border-emerald-500'}`} + /> + + + + {searchError && ( +
+
+

{searchError}

+
+ )} + +
+ {searchLoading ? ( +
+
+

Consultando Compras Paraguai...

+
+ ) : products.map((p, idx) => ( +
setSelectedProduct(p)} + className={`p-5 rounded-3xl border-2 cursor-pointer transition-all hover:scale-[1.02] active:scale-95 ${selectedProduct?.name === p.name + ? 'border-indigo-500 bg-indigo-500/10 shadow-md' + : 'border-white/5 bg-white/5 hover:border-white/10 hover:bg-white/10' + }`} + > +
+ {p.store} +
+

{p.name}

+
+
+

US$ {p.priceUSD.toFixed(2)}

+

R$ {p.priceBRL.toLocaleString('pt-BR')}

+
+
+ {p.salesVolume && ( + + {p.salesVolume} + + )} + +
+
+
+ ))} + {!searchLoading && products.length === 0 && ( +
+ +

Aguardando seu input

+
+ )} +
+
+
+ + {/* COLUNA 2: ANÁLISE DETALHADA */} +
+ {selectedProduct ? ( + + ) : ( +
+
+ +
+

Analítica de Arbitragem

+

Selecione um produto para ver a comparação de margem real entre os marketplaces do Brasil e sua venda direta no Facebook.

+
+ )} +
+ + {/* COLUNA 3: COTAÇÃO / CRM CHECKOUT */} +
+
+
+
+

Minha Cotação

+

Sourcing Ativo

+
+ {shoppingList.length} +
+ +
+ {shoppingList.length > 0 ? ( + shoppingList.map(item => ( +
+
+
+ {item.store} +

{item.name}

+
+ +
+
+

US$ {item.priceUSD.toFixed(2)}

+
+ + {item.quantity} + +
+
+
+ )) + ) : ( +
+ +

Carrinho Vazio

+
+ )} +
+ +
+
+
+ + + R$ {calculateShoppingTotals().totalCostWithOverhead.toLocaleString('pt-BR')} + +
+
+ Subtotal Paraguai + US$ {calculateShoppingTotals().totalUSD.toLocaleString('en-US')} +
+
+
+ Lucro Est. Final + +
+ R$ {calculateShoppingTotals().totalApproxProfit.toLocaleString('pt-BR')} +
+
+ +
+
+
+
+ ); +}; + +export default Sourcing; diff --git a/pages/Suppliers.tsx b/pages/Suppliers.tsx new file mode 100644 index 0000000..84e64c1 --- /dev/null +++ b/pages/Suppliers.tsx @@ -0,0 +1,143 @@ + +import React, { useState } from 'react'; +import { Store, Plus, Edit2, Trash2, Truck, Phone, Star } from 'lucide-react'; +import { useCRM } from '../context/CRMContext'; +import { Supplier } from '../types'; + +const Suppliers: React.FC = () => { + const { suppliers, addSupplier, updateSupplier, deleteSupplier } = useCRM(); + const [isModalOpen, setIsModalOpen] = useState(false); + const [editingId, setEditingId] = useState(null); + const [formData, setFormData] = useState>({ + name: '', contact: '', rating: 5 + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (editingId) { + updateSupplier(editingId, formData); + } else { + addSupplier(formData as any); + } + closeModal(); + }; + + const openModal = (supplier?: Supplier) => { + if (supplier) { + setEditingId(supplier.id); + setFormData(supplier); + } else { + setEditingId(null); + setFormData({ name: '', contact: '', rating: 5 }); + } + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + setEditingId(null); + }; + + return ( +
+ {/* HEADER */} +
+
+

Parceiros Logísticos

+

Gestão de Transporte e Fornecedores

+
+ +
+ + {/* LIST */} +
+ {suppliers.map(s => ( +
+ + {/* Watermark Icon */} +
+ +
+ +
+
+
+ +
+
+

{s.name}

+

Parceiro

+
+
+
+ {[1, 2, 3, 4, 5].map(star => ( +
+ ))} +
+
+ +
+ {s.contact && ( +
+ + {s.contact} +
+ )} +
+ +
+ + +
+
+ ))} +
+ + {/* MODAL */} + {isModalOpen && ( +
+
+

{editingId ? 'Editar Parceiro' : 'Novo Parceiro'}

+
+
+ + setFormData({ ...formData, name: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" placeholder="Ex: João Freteiro" /> +
+
+ + setFormData({ ...formData, contact: e.target.value })} className="w-full bg-black/40 border border-white/10 rounded-lg px-3 py-2 text-sm text-white focus:border-indigo-500 outline-none transition-all" placeholder="+55 45 9..." /> +
+
+ +
+ {[1, 2, 3, 4, 5].map(star => ( + + ))} +
+
+ +
+ + +
+
+
+
+ )} +
+ ); +}; + +export default Suppliers; diff --git a/pages/Users.tsx b/pages/Users.tsx new file mode 100644 index 0000000..29d79d9 --- /dev/null +++ b/pages/Users.tsx @@ -0,0 +1,167 @@ +import React, { useState } from 'react'; +import { useCRM } from '../context/CRMContext'; +import { Shield, ShieldAlert, User as UserIcon, Check, X, MoreVertical, Edit2 } from 'lucide-react'; +import { User } from '../types'; + +const Users: React.FC = () => { + const { users, isAdmin, updateUserRole } = useCRM(); + const [searchTerm, setSearchTerm] = useState(''); + const [showRoleModal, setShowRoleModal] = useState(false); + const [selectedUser, setSelectedUser] = useState(null); + const [newRole, setNewRole] = useState<'admin' | 'user'>('user'); + + // Filter users + const filteredUsers = users.filter(user => + (user.name?.toLowerCase().includes(searchTerm.toLowerCase()) || + user.email.toLowerCase().includes(searchTerm.toLowerCase())) + ); + + const handleRoleUpdateClick = (user: User) => { + setSelectedUser(user); + setNewRole(user.role === 'admin' ? 'user' : 'admin'); + setShowRoleModal(true); + }; + + const confirmRoleUpdate = async () => { + if (selectedUser) { + await updateUserRole(selectedUser.id, newRole); + setShowRoleModal(false); + setSelectedUser(null); + } + }; + + if (!isAdmin) { + return ( +
+
+ +
+

Acesso Restrito

+

+ Você precisa de privilégios de administrador para gerenciar usuários e permissões do sistema. +

+
+ ); + } + + return ( +
+
+
+

Gerenciamento de Usuários

+

Controle total sobre acesso e permissões da equipe.

+
+
+ setSearchTerm(e.target.value)} + className="w-full md:w-80 bg-black/20 border border-white/10 rounded-xl px-4 py-3 text-sm text-slate-300 focus:border-indigo-500 outline-none transition-all placeholder:text-slate-600" + /> +
+
+ +
+
+ + + + + + + + + + + + + {filteredUsers.length > 0 ? filteredUsers.map(user => ( + + + + + + + + + )) : ( + + + + )} + +
UsuárioEmailStatusPermissãoÚltimo AcessoAções
+
+ {user.avatar ? ( + {user.name} + ) : ( +
+ +
+ )} +
+
{user.name || 'Sem Nome'}
+
ID: {user.id.substring(0, 8)}
+
+
+
{user.email} + + {user.status || 'Active'} + + + + {user.role === 'admin' && } + {user.role || 'User'} + + + {user.lastAccess ? new Date(user.lastAccess).toLocaleDateString() : '-'} + + +
+ +

Nenhum usuário encontrado

+
+
+
+ + {/* Role Update Modal */} + {showRoleModal && selectedUser && ( +
+
+
+ +

Alterar Permissão

+

+ Você tem certeza que deseja alterar o nível de acesso de {selectedUser.name} para {newRole}? +

+ +
+ + +
+
+
+ )} +
+ ); +}; + +export default Users; diff --git a/paraguai.rar b/paraguai.rar new file mode 100644 index 0000000000000000000000000000000000000000..24f722f4e5fbf090f0097e21d1bb7f6341a41158 GIT binary patch literal 92690 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 literal 0 HcmV?d00001 diff --git a/policies.sql b/policies.sql new file mode 100644 index 0000000..8b32362 --- /dev/null +++ b/policies.sql @@ -0,0 +1,26 @@ +-- 1. Ensure RLS is enabled (consistency) +alter table public.inventory enable row level security; +alter table public.suppliers enable row level security; +alter table public.customers enable row level security; +alter table public.orders enable row level security; +alter table public.transactions enable row level security; +alter table public.settings enable row level security; + +-- 2. Drop existing policies to avoid conflicts if re-run +drop policy if exists "Enable all for authenticated users" on public.inventory; +drop policy if exists "Enable all for authenticated users" on public.suppliers; +drop policy if exists "Enable all for authenticated users" on public.customers; +drop policy if exists "Enable all for authenticated users" on public.orders; +drop policy if exists "Enable all for authenticated users" on public.transactions; +drop policy if exists "Enable all for authenticated users" on public.settings; + +-- 3. Create Permissive Policies for Authenticated Users +-- This allows any logged-in user to Select, Insert, Update, Delete ONLY if they are authenticated. +-- In a SaaS, you would restrict 'using (user_id = auth.uid())', but for this internal tool, we allow all authenticated access. + +create policy "Enable all for authenticated users" on public.inventory for all to authenticated using (true) with check (true); +create policy "Enable all for authenticated users" on public.suppliers for all to authenticated using (true) with check (true); +create policy "Enable all for authenticated users" on public.customers for all to authenticated using (true) with check (true); +create policy "Enable all for authenticated users" on public.orders for all to authenticated using (true) with check (true); +create policy "Enable all for authenticated users" on public.transactions for all to authenticated using (true) with check (true); +create policy "Enable all for authenticated users" on public.settings for all to authenticated using (true) with check (true); diff --git a/services/geminiService.ts b/services/geminiService.ts new file mode 100644 index 0000000..424e246 --- /dev/null +++ b/services/geminiService.ts @@ -0,0 +1,314 @@ +import { GoogleGenAI, Type } from "@google/genai"; +import { Product, AuctionLot, BiddingTender } from "../types"; + +// Vite: variáveis precisam começar com VITE_ +const API_KEY = import.meta.env.VITE_GOOGLE_API_KEY as string | undefined; + +if (!API_KEY) { + // Erro explícito pra não “falhar silencioso” + throw new Error( + "Missing VITE_GOOGLE_API_KEY. Add it to your .env.local (VITE_GOOGLE_API_KEY=...) and restart the dev server." + ); +} + +const ai = new GoogleGenAI({ apiKey: API_KEY }); + +/** + * Helper: converte File em Part (inlineData) para o Gemini + */ +async function fileToPart(file: File) { + return new Promise<{ inlineData: { data: string; mimeType: string } }>((resolve, reject) => { + const reader = new FileReader(); + reader.onerror = () => reject(new Error("Failed to read file")); + reader.onloadend = () => { + const result = reader.result as string | null; + if (!result) return reject(new Error("Empty file result")); + const base64Data = result.split(",")[1] || ""; + resolve({ + inlineData: { + data: base64Data, + mimeType: file.type || "application/octet-stream", + }, + }); + }; + reader.readAsDataURL(file); + }); +} + +/** + * Sourcing Otimizado: Foco no MENOR PREÇO de compra (PY) + * e MENOR PREÇO de venda competitiva (BR - "Mais Vendidos") + */ +export async function searchProducts( + query: string +): Promise<{ products: Product[]; sources: any[] }> { + // try { // Removed to propagate errors + const prompt = `OBJETIVO: Análise de Arbitragem Profissional. + +1. BUSCA REAL OBRIGATÓRIA: +Você DEVE usar a tool googleSearch para buscar dados REAIS e ATUAIS no site www.comprasparaguai.com.br. +Busque os 20 itens com o MENOR PREÇO ABSOLUTO para "${query}". +Foque em lojas de grande renome (Nissei, Cellshop, Atacado Games, Mega Eletronicos). + +2. BUSCA BRASIL (REFERÊNCIA): +Para cada item, encontre o MENOR PREÇO de venda no Mercado Livre e Amazon Brasil. + +3. REGRAS CRÍTICAS (ANTI-ALUCINAÇÃO): +- JAMAIS invente produtos ou preços. +- JAMAIS retorne itens com "(Exemplo)", "(Example)" ou dados fictícios. +- Se não encontrar dados REAIS, retorne lista vazia. +- Use a cotação de dólar turismo/paralelo atual (~5.75 BRL/USD). + +4. RETORNO: +Apenas JSON conforme o schema.`; + + const response = await ai.models.generateContent({ + model: "gemini-3-pro-preview", // Stable fast model + contents: prompt, + config: { + tools: [{ googleSearch: {} }], + responseMimeType: "application/json", + responseSchema: { + type: Type.ARRAY, + items: { + type: Type.OBJECT, + properties: { + name: { type: Type.STRING, description: "Nome técnico do produto" }, + priceUSD: { type: Type.NUMBER, description: "Menor preço em USD no Paraguai" }, + priceBRL: { type: Type.NUMBER, description: "Preço USD convertido para BRL" }, + store: { type: Type.STRING, description: "Loja do Paraguai (Nissei, Cellshop, etc)" }, + url: { type: Type.STRING, description: "Link da oferta no Paraguai" }, + marketPriceBRL: { + type: Type.NUMBER, + description: "Menor preço encontrado entre os MAIS VENDIDOS no Brasil", + }, + amazonPrice: { type: Type.NUMBER, description: "Menor preço Amazon (Best Seller)" }, + amazonUrl: { type: Type.STRING }, + mlPrice: { type: Type.NUMBER, description: "Menor preço Mercado Livre (Mais Vendido)" }, + mlUrl: { type: Type.STRING }, + }, + required: ["name", "priceUSD", "priceBRL", "store", "url", "marketPriceBRL"], + }, + }, + }, + }); + + const text = response.text || "[]"; + const products = JSON.parse(text); + const sources = response.candidates?.[0]?.groundingMetadata?.groundingChunks || []; + + // Ordena para mostrar primeiro as melhores oportunidades (menor custo em BRL) + const sortedProducts = Array.isArray(products) + ? products.sort((a: any, b: any) => (a.priceBRL ?? 0) - (b.priceBRL ?? 0)) + : []; + + return { products: sortedProducts as Product[], sources }; + // } catch (error) { + // console.error("Erro na busca de arbitrage:", error); + // return { products: [], sources: [] }; + // } +} + +/** + * Busca Oportunidades (>25% Margem) por Categoria + */ +export async function searchOpportunities(category: string, includeOverhead: boolean = true): Promise<{ products: Product[]; sources: any[] }> { + // try { + const prompt = `OBJETIVO: Encontrar Oportunidades de Arbitragem (>25% Lucro) na categoria "${category}". + +1. BUSCA PARAGUAI: +Vasculhe as principais lojas do Paraguai (Nissei, Cellshop, Mega, Atacado Games) em busca de produtos da categoria "${category}" que estejam com preços muito atrativos ou em oferta. +Identifique pelo menos 20 produtos promissores. + +2. CÁLCULO DE MARGEM: +Para cada produto, estime o custo total em BRL (Preço PY * 5.75 * ${includeOverhead ? '1.20' : '1.00'} de taxas). +Compare com o MENOR preço de venda real no Brasil (Mercado Livre/Amazon - Filtro "Mais Vendidos"). +Margem = (Preço Venda BR - Custo Total) / Preço Venda BR. + +3. FILTRO RIGOROSO: +Retorne APENAS produtos onde a Margem estimada seja SUPERIOR a 20-25%. +Se não encontrar nada com 25%, mostre os que tiverem a maior margem possível. + +4. RETORNO: +Apenas JSON conforme o schema.`; + + const response = await ai.models.generateContent({ + model: "gemini-3-pro-preview", + contents: prompt, + config: { + tools: [{ googleSearch: {} }], + responseMimeType: "application/json", + responseSchema: { + type: Type.ARRAY, + items: { + type: Type.OBJECT, + properties: { + name: { type: Type.STRING, description: "Nome técnico do produto" }, + priceUSD: { type: Type.NUMBER, description: "Menor preço em USD no Paraguai" }, + priceBRL: { type: Type.NUMBER, description: "Preço USD convertido para BRL" }, + store: { type: Type.STRING, description: "Loja do Paraguai (Nissei, Cellshop, etc)" }, + url: { type: Type.STRING, description: "Link da oferta no Paraguai" }, + marketPriceBRL: { + type: Type.NUMBER, + description: "Menor preço encontrado entre os MAIS VENDIDOS no Brasil", + }, + amazonPrice: { type: Type.NUMBER, description: "Menor preço Amazon (Best Seller)" }, + amazonUrl: { type: Type.STRING }, + mlPrice: { type: Type.NUMBER, description: "Menor preço Mercado Livre (Mais Vendido)" }, + mlUrl: { type: Type.STRING }, + }, + required: ["name", "priceUSD", "priceBRL", "store", "url", "marketPriceBRL"], + }, + }, + }, + }); + + const text = response.text || "[]"; + const products = JSON.parse(text); + const sources = response.candidates?.[0]?.groundingMetadata?.groundingChunks || []; + + // Ordena por maior margem (diferença entre venda BR e custo PY) + const sortedProducts = Array.isArray(products) + ? products.sort((a: any, b: any) => { + const marginA = (a.marketPriceBRL - a.priceBRL) / a.marketPriceBRL; + const marginB = (b.marketPriceBRL - b.priceBRL) / b.marketPriceBRL; + return marginB - marginA; // Maior margem primeiro + }) + : []; + + return { products: sortedProducts as Product[], sources }; + // } catch (error) { + // console.error("Erro na busca de oportunidades:", error); + // return { products: [], sources: [] }; + // } +} + +/** + * Leilões: Analisa texto, link ou ARQUIVO PDF + */ +export async function analyzeAuctionData( + input: string | File +): Promise<{ lot: AuctionLot | null; sources: any[] }> { + try { + const promptPrefix = "Analise este lote de leilão da Receita Federal."; + + const parts: any[] = []; + + if (input instanceof File) { + const filePart = await fileToPart(input); + parts.push(filePart); + parts.push({ + text: `${promptPrefix} +Leia o documento PDF anexo e extraia TODOS os itens da tabela de mercadorias. + +REGRAS: +1. Identifique os itens reais. +2. Descubra o MENOR valor real de mercado no Brasil (baseado nos itens mais vendidos) via Google Search. +3. Use URLs REAIS dos sites de busca.`, + }); + } else { + parts.push({ + text: `${promptPrefix} +Analise este conteúdo: "${input}". + +REGRAS: +1. Identifique os itens reais. +2. Descubra o MENOR valor real de mercado no Brasil (baseado nos itens mais vendidos) via Google Search. +3. Use URLs REAIS dos sites de busca.`, + }); + } + + const analysisResponse = await ai.models.generateContent({ + model: "gemini-3-pro-preview", + contents: { parts }, // padronizado + config: { tools: [{ googleSearch: {} }] }, + }); + + const analysisText = analysisResponse.text || ""; + const sources = analysisResponse.candidates?.[0]?.groundingMetadata?.groundingChunks || []; + + // Segunda passada: força JSON limpo + const extraction = await ai.models.generateContent({ + model: "gemini-3-pro-preview", + contents: `Transforme em JSON conforme o schema. Texto base: ${analysisText}`, + config: { + responseMimeType: "application/json", + responseSchema: { + type: Type.OBJECT, + properties: { + id: { type: Type.STRING }, + title: { type: Type.STRING }, + location: { type: Type.STRING }, + items: { type: Type.ARRAY, items: { type: Type.STRING } }, + currentBid: { type: Type.NUMBER }, + minBid: { type: Type.NUMBER }, + mlMarketPrice: { type: Type.NUMBER }, + }, + required: ["id", "title", "items", "minBid", "mlMarketPrice"], + }, + }, + }); + + const lotData = JSON.parse(extraction.text || "{}"); + + return { + lot: { + ...lotData, + status: "Analyzing" as any, + url: typeof input === "string" && input.startsWith("http") ? input : "", + }, + sources, + }; + } catch (error) { + console.error("Erro ao analisar leilão:", error); + return { lot: null, sources: [] }; + } +} + +/** + * Licitações Otimizadas + */ +export async function searchBiddings( + query: string +): Promise<{ tenders: BiddingTender[]; sources: any[] }> { + try { + const prompt = `Pesquise licitações ativas para "${query}" no Brasil. +Priorize PNCP e Compras.gov.br. +Retorne apenas JSON conforme o schema.`; + + const searchResponse = await ai.models.generateContent({ + model: "gemini-3-pro-preview", + contents: prompt, + config: { + tools: [{ googleSearch: {} }], + responseMimeType: "application/json", + responseSchema: { + type: Type.ARRAY, + items: { + type: Type.OBJECT, + properties: { + id: { type: Type.STRING }, + org: { type: Type.STRING }, + object: { type: Type.STRING }, + estimatedValue: { type: Type.NUMBER }, + deadline: { type: Type.STRING }, + items: { type: Type.ARRAY, items: { type: Type.STRING } }, + requirements: { type: Type.ARRAY, items: { type: Type.STRING } }, + marketReferencePrice: { type: Type.NUMBER }, + url: { type: Type.STRING }, + }, + required: ["id", "org", "object", "url"], + }, + }, + }, + }); + + const tenders = JSON.parse(searchResponse.text || "[]"); + const sources = searchResponse.candidates?.[0]?.groundingMetadata?.groundingChunks || []; + + return { tenders: (Array.isArray(tenders) ? tenders : []) as BiddingTender[], sources }; + } catch (error) { + console.error("Erro na busca de licitações:", error); + return { tenders: [], sources: [] }; + } +} diff --git a/services/supabase.ts b/services/supabase.ts new file mode 100644 index 0000000..4e5714b --- /dev/null +++ b/services/supabase.ts @@ -0,0 +1,11 @@ + +import { createClient } from '@supabase/supabase-js'; + +const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; +const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY; + +if (!supabaseUrl || !supabaseAnonKey) { + console.error('Missing Supabase environment variables'); +} + +export const supabase = createClient(supabaseUrl, supabaseAnonKey); diff --git a/supabase_schema.sql b/supabase_schema.sql new file mode 100644 index 0000000..d0c8073 --- /dev/null +++ b/supabase_schema.sql @@ -0,0 +1,126 @@ +-- Enable UUID extension +create extension if not exists "uuid-ossp"; + +-- 1. USERS & PROFILES (Managed by Supabase Auth, but we can have a public profile) +create table if not exists public.profiles ( + id uuid references auth.users not null primary key, + email text, + full_name text, + role text default 'Buyer', + created_at timestamp with time zone default timezone('utc'::text, now()) not null +); + +-- 2. INVENTORY (Products) +create table if not exists public.inventory ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + name text not null, + sku text, + ean text, + quantity integer default 0, + avg_cost_brl numeric default 0, + market_price_brl numeric default 0, + last_supplier text, + user_id uuid references auth.users +); + +-- 3. SUPPLIERS +create table if not exists public.suppliers ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + name text not null, + contact text, + rating integer default 0, + last_purchase timestamp with time zone, + user_id uuid references auth.users +); + +-- 4. CUSTOMERS +create table if not exists public.customers ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + name text not null, + email text, + phone text, + city text, + status text default 'Active', -- 'Active', 'Inactive', 'Prospect' + total_purchased numeric default 0, + user_id uuid references auth.users +); + +-- 5. ORDERS +create table if not exists public.orders ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + date timestamp with time zone, + status text default 'Pending', -- 'Pending', 'Paid', 'Received', 'Cancelled' + total_usd numeric default 0, + total_brl numeric default 0, + total_cost_with_overhead numeric default 0, + estimated_profit numeric default 0, + items jsonb, -- Stores the JSON array of items + supplier_name text, + user_id uuid references auth.users +); + +-- 6. TRANSACTIONS (Financial) +create table if not exists public.transactions ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + date timestamp with time zone, + type text, -- 'Income', 'Expense' + category text, + amount numeric default 0, + description text, + user_id uuid references auth.users +); + +-- 7. SETTINGS (Global Config) +create table if not exists public.settings ( + id uuid default uuid_generate_v4() primary key, + created_at timestamp with time zone default timezone('utc'::text, now()) not null, + updated_at timestamp with time zone default timezone('utc'::text, now()), + + -- Company Info + company_name text, + cnpj text, + ie text, + + -- Financial Defaults + default_overhead numeric default 20, + default_exchange numeric default 5.65, + + -- Integration Tokens + brazil_api_token text, + melhor_envio_token text, + bling_token text, + tiny_token text, + gemini_key text, + + -- Fiscal / NFe + certificate_password text, -- CAUTION: Storing plain text/base64? Ideally encrypted. + nfe_serie text default '1', + nfe_number text, + nfe_environment text default 'homologacao', + + -- Email SMTP + smtp_host text, + smtp_port text, + smtp_user text, + smtp_pass text, + + -- Automation + auto_sync_sales boolean default true, + auto_sync_stock boolean default true, + + user_id uuid references auth.users +); + +-- RLS POLICIES (Optional but recommended) +alter table public.inventory enable row level security; +alter table public.suppliers enable row level security; +alter table public.customers enable row level security; +alter table public.orders enable row level security; +alter table public.transactions enable row level security; +alter table public.settings enable row level security; +-- (Policies would need to be added to allow read/write for authenticated users) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c6eed5 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ES2022", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": [ + "ES2022", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + "types": [ + "node" + ], + "moduleResolution": "bundler", + "isolatedModules": true, + "moduleDetection": "force", + "allowJs": true, + "jsx": "react-jsx", + "paths": { + "@/*": [ + "./*" + ] + }, + "allowImportingTsExtensions": true, + "noEmit": true + } +} \ No newline at end of file diff --git a/types.ts b/types.ts new file mode 100644 index 0000000..6273b72 --- /dev/null +++ b/types.ts @@ -0,0 +1,167 @@ + +export interface Product { + name: string; + priceUSD: number; + priceBRL: number; + store: string; + url: string; + marketPriceBRL?: number; + salesVolume?: string; // New: e.g. "10k+ vendidos" + rating?: number; // New: 0-5 + amazonPrice?: number; + amazonUrl?: string; + mlPrice?: number; + mlUrl?: string; + shopeePrice?: number; + shopeeUrl?: string; +} + +export interface ShoppingListItem { + id: string; + name: string; + store: string; + priceUSD: number; + priceBRL: number; + quantity: number; + marketPriceBRL: number; +} + +export type OrderStatus = 'Quotation' | 'Pending' | 'Paid' | 'Received' | 'Cancelled'; +export type SalesChannel = 'Local' | 'Mercado Livre' | 'Shopee' | 'Amazon' | 'Facebook'; + +export interface Sale { + id: string; + date: string; + customerId?: string; // Optional linkage + customerName?: string; // Flattened for easier display + items: { id: string; name: string; quantity: number; salePrice: number; costPrice?: number }[]; + total: number; + status: 'Pending' | 'Paid' | 'Shipped' | 'Completed' | 'Cancelled' | 'Returned'; + channel: SalesChannel; + externalId?: string; // Mercado Livre ID, etc. + isStockLaunched: boolean; + isFinancialLaunched: boolean; +} + +export interface Order { + id: string; + date: string; + items: ShoppingListItem[]; + totalUSD: number; + totalBRL: number; + totalCostWithOverhead: number; + estimatedProfit: number; + status: OrderStatus; + supplierName: string; +} + +export interface InventoryItem { + id: string; + name: string; + sku: string; + ean?: string; + quantity: number; + avgCostBRL: number; + marketPriceBRL: number; + lastSupplier: string; +} + +export interface Supplier { + id: string; + name: string; + contact?: string; + rating: number; + lastPurchase?: string; +} + +export interface User { + id: string; + name?: string; + email: string; + phone?: string; + role?: 'admin' | 'user'; // New + status: 'Active' | 'Inactive'; + lastAccess?: string; + avatar: string; +} + +export interface PlatformFee { + name: string; + commission: number; + fixedFee: number; +} + +export interface CalculationResult { + platform: string; + marketPrice: number; + totalCost: number; + fees: number; + netProfit: number; + margin: number; + url?: string; +} + +export const PLATFORMS: PlatformFee[] = [ + { name: 'Amazon', commission: 0.15, fixedFee: 0 }, + { name: 'Mercado Livre', commission: 0.18, fixedFee: 6 }, + { name: 'Shopee', commission: 0.20, fixedFee: 3 }, + { name: 'Facebook', commission: 0.0, fixedFee: 0 }, +]; + +export interface Customer { + id: string; + name: string; + email?: string; + phone?: string; + city?: string; + status: 'Active' | 'Inactive' | 'Prospect'; + totalPurchased: number; +} + +export type TransactionType = 'Income' | 'Expense'; + +export interface Transaction { + id: string; + date: string; + type: TransactionType; + category: string; // Hotel, Combustível, Alimentação, Venda, etc. + amount: number; + description: string; + status: 'Pending' | 'Paid' | 'Overdue' | 'Cancelled'; + paymentMethod: PaymentMethod; + dueDate?: string; + relatedSaleId?: string; +} + +export type PaymentMethod = 'Pix' | 'Credit Card' | 'Debit Card' | 'Cash' | 'Boleto' | 'Transfer' | 'Other'; + +export interface FinancialSummary { + totalIncome: number; + totalExpense: number; + balance: number; + recentTransactions: Transaction[]; +} + +export interface AuctionLot { + id: string; + title: string; + location: string; + items: string[]; + currentBid: number; + minBid: number; + mlMarketPrice: number; + status: 'Analyzing' | 'Open' | 'Closed'; + url: string; +} + +export interface BiddingTender { + id: string; + org: string; + object: string; + estimatedValue: number; + deadline: string; + items: string[]; + requirements: string[]; + marketReferencePrice: number; + url: string; +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..e3611db --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +import path from 'path'; +import { defineConfig, loadEnv } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, '.', ''); + return { + server: { + port: 3000, + host: '0.0.0.0', + }, + plugins: [react()], + define: { + 'process.env.API_KEY': JSON.stringify(env.API_KEY || env.VITE_GOOGLE_API_KEY || env.GEMINI_API_KEY), + 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY) + }, + resolve: { + alias: { + '@': path.resolve(__dirname, '.'), + } + } + }; +});