@burdenoff/microfe-kadaikodi 2026.704.4 → 2026.704.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/kadaikodi/KadaikodiRoutes.js +30 -26
  2. package/dist/kadaikodi/KadaikodiRoutes.js.map +1 -1
  3. package/dist/kadaikodi/components/viz/AllocationBar.js +68 -0
  4. package/dist/kadaikodi/components/viz/AllocationBar.js.map +1 -0
  5. package/dist/kadaikodi/components/viz/AreaChart.js +102 -0
  6. package/dist/kadaikodi/components/viz/AreaChart.js.map +1 -0
  7. package/dist/kadaikodi/components/viz/AvatarStack.js +36 -0
  8. package/dist/kadaikodi/components/viz/AvatarStack.js.map +1 -0
  9. package/dist/kadaikodi/components/viz/BarChart.js +83 -0
  10. package/dist/kadaikodi/components/viz/BarChart.js.map +1 -0
  11. package/dist/kadaikodi/components/viz/CertificateCard.js +4 -0
  12. package/dist/kadaikodi/components/viz/DonutChart.js +109 -0
  13. package/dist/kadaikodi/components/viz/DonutChart.js.map +1 -0
  14. package/dist/kadaikodi/components/viz/HBarFunnel.js +54 -0
  15. package/dist/kadaikodi/components/viz/HBarFunnel.js.map +1 -0
  16. package/dist/kadaikodi/components/viz/Heatmap.js +3 -0
  17. package/dist/kadaikodi/components/viz/KanbanBoard.js +50 -0
  18. package/dist/kadaikodi/components/viz/KanbanBoard.js.map +1 -0
  19. package/dist/kadaikodi/components/viz/LineChart.js +4 -0
  20. package/dist/kadaikodi/components/viz/OrgTree.js +2 -0
  21. package/dist/kadaikodi/components/viz/PathSteps.js +3 -0
  22. package/dist/kadaikodi/components/viz/ProgressRing.js +68 -0
  23. package/dist/kadaikodi/components/viz/ProgressRing.js.map +1 -0
  24. package/dist/kadaikodi/components/viz/RatingStars.js +51 -0
  25. package/dist/kadaikodi/components/viz/RatingStars.js.map +1 -0
  26. package/dist/kadaikodi/components/viz/SegmentedTabs.js +69 -0
  27. package/dist/kadaikodi/components/viz/SegmentedTabs.js.map +1 -0
  28. package/dist/kadaikodi/components/viz/SkillBar.js +3 -0
  29. package/dist/kadaikodi/components/viz/Sparkline.js +74 -0
  30. package/dist/kadaikodi/components/viz/Sparkline.js.map +1 -0
  31. package/dist/kadaikodi/components/viz/StatCard.js +60 -0
  32. package/dist/kadaikodi/components/viz/StatCard.js.map +1 -0
  33. package/dist/kadaikodi/components/viz/StepProgress.js +64 -0
  34. package/dist/kadaikodi/components/viz/StepProgress.js.map +1 -0
  35. package/dist/kadaikodi/components/viz/Timeline.js +66 -0
  36. package/dist/kadaikodi/components/viz/Timeline.js.map +1 -0
  37. package/dist/kadaikodi/components/viz/index.js +23 -0
  38. package/dist/kadaikodi/components/viz/lib/Avatar.js +30 -0
  39. package/dist/kadaikodi/components/viz/lib/Avatar.js.map +1 -0
  40. package/dist/kadaikodi/components/viz/lib/CartesianFrame.js +119 -0
  41. package/dist/kadaikodi/components/viz/lib/CartesianFrame.js.map +1 -0
  42. package/dist/kadaikodi/components/viz/lib/ResponsiveSvg.js +35 -0
  43. package/dist/kadaikodi/components/viz/lib/ResponsiveSvg.js.map +1 -0
  44. package/dist/kadaikodi/components/viz/lib/format.js +21 -0
  45. package/dist/kadaikodi/components/viz/lib/format.js.map +1 -0
  46. package/dist/kadaikodi/components/viz/lib/geometry.js +61 -0
  47. package/dist/kadaikodi/components/viz/lib/geometry.js.map +1 -0
  48. package/dist/kadaikodi/components/viz/lib/hooks.js +39 -0
  49. package/dist/kadaikodi/components/viz/lib/hooks.js.map +1 -0
  50. package/dist/kadaikodi/components/viz/lib/palette.js +27 -0
  51. package/dist/kadaikodi/components/viz/lib/palette.js.map +1 -0
  52. package/dist/kadaikodi/hooks/useKadaikodiApi.js +18 -10
  53. package/dist/kadaikodi/hooks/useKadaikodiApi.js.map +1 -1
  54. package/dist/kadaikodi/pages/HomePage.js +373 -631
  55. package/dist/kadaikodi/pages/HomePage.js.map +1 -1
  56. package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js +304 -169
  57. package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js.map +1 -1
  58. package/dist/kadaikodi/pages/admin/AdminDashboard.js +255 -271
  59. package/dist/kadaikodi/pages/admin/AdminDashboard.js.map +1 -1
  60. package/dist/kadaikodi/pages/admin/AdminFinancialPage.js +185 -125
  61. package/dist/kadaikodi/pages/admin/AdminFinancialPage.js.map +1 -1
  62. package/dist/kadaikodi/pages/admin/AdminOrdersPage.js +141 -102
  63. package/dist/kadaikodi/pages/admin/AdminOrdersPage.js.map +1 -1
  64. package/dist/kadaikodi/pages/admin/CartManagementPage.js +558 -718
  65. package/dist/kadaikodi/pages/admin/CartManagementPage.js.map +1 -1
  66. package/dist/kadaikodi/pages/admin/UserManagementPage.js.map +1 -1
  67. package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js +142 -55
  68. package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
  69. package/dist/kadaikodi/pages/cart-owner/CartOwnerDashboard.js +129 -164
  70. package/dist/kadaikodi/pages/cart-owner/CartOwnerDashboard.js.map +1 -1
  71. package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js +379 -361
  72. package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js.map +1 -1
  73. package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js +266 -166
  74. package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js.map +1 -1
  75. package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.js +373 -0
  76. package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.js.map +1 -0
  77. package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js +288 -161
  78. package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js.map +1 -1
  79. package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js +180 -181
  80. package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js.map +1 -1
  81. package/dist/kadaikodi/pages/common/HelpSupportPage.js.map +1 -1
  82. package/dist/kadaikodi/pages/common/SettingsPage.js.map +1 -1
  83. package/dist/kadaikodi/pages/customer/BookingDetailsPage.js +17 -34
  84. package/dist/kadaikodi/pages/customer/BookingDetailsPage.js.map +1 -1
  85. package/dist/kadaikodi/pages/customer/CartDetailPage.js +405 -248
  86. package/dist/kadaikodi/pages/customer/CartDetailPage.js.map +1 -1
  87. package/dist/kadaikodi/pages/customer/CartDiscoveryPage.js +258 -232
  88. package/dist/kadaikodi/pages/customer/CartDiscoveryPage.js.map +1 -1
  89. package/dist/kadaikodi/pages/customer/CheckoutPage.js +254 -205
  90. package/dist/kadaikodi/pages/customer/CheckoutPage.js.map +1 -1
  91. package/dist/kadaikodi/pages/customer/DirectServiceBookingPage.js +270 -230
  92. package/dist/kadaikodi/pages/customer/DirectServiceBookingPage.js.map +1 -1
  93. package/dist/kadaikodi/pages/customer/FavoritesPage.js +149 -412
  94. package/dist/kadaikodi/pages/customer/FavoritesPage.js.map +1 -1
  95. package/dist/kadaikodi/pages/customer/OrderHistoryPage.js +173 -134
  96. package/dist/kadaikodi/pages/customer/OrderHistoryPage.js.map +1 -1
  97. package/dist/kadaikodi/pages/customer/OrderTrackingPage.js +176 -175
  98. package/dist/kadaikodi/pages/customer/OrderTrackingPage.js.map +1 -1
  99. package/dist/kadaikodi/pages/customer/ReviewsPage.js +295 -438
  100. package/dist/kadaikodi/pages/customer/ReviewsPage.js.map +1 -1
  101. package/dist/kadaikodi/pages/customer/shared.js +181 -0
  102. package/dist/kadaikodi/pages/customer/shared.js.map +1 -0
  103. package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js +438 -311
  104. package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js.map +1 -1
  105. package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js +320 -293
  106. package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
  107. package/dist/kadaikodi/pages/investor/InvestorAnalyticsPage.js +159 -91
  108. package/dist/kadaikodi/pages/investor/InvestorAnalyticsPage.js.map +1 -1
  109. package/dist/kadaikodi/pages/investor/InvestorDashboard.js +137 -107
  110. package/dist/kadaikodi/pages/investor/InvestorDashboard.js.map +1 -1
  111. package/dist/kadaikodi/pages/investor/InvestorInvestmentsPage.js +157 -135
  112. package/dist/kadaikodi/pages/investor/InvestorInvestmentsPage.js.map +1 -1
  113. package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.js +280 -206
  114. package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.js.map +1 -1
  115. package/dist/kadaikodi/pages/investor/InvestorProfilePage.js.map +1 -1
  116. package/dist/kadaikodi/pages/worker/TaskDetailsPage.js +424 -448
  117. package/dist/kadaikodi/pages/worker/TaskDetailsPage.js.map +1 -1
  118. package/dist/kadaikodi/pages/worker/WorkerDashboard.js +328 -445
  119. package/dist/kadaikodi/pages/worker/WorkerDashboard.js.map +1 -1
  120. package/dist/kadaikodi/pages/worker/WorkerOrdersPage.js +141 -164
  121. package/dist/kadaikodi/pages/worker/WorkerOrdersPage.js.map +1 -1
  122. package/dist/kadaikodi/pages/worker/WorkerProfilePage.js +300 -296
  123. package/dist/kadaikodi/pages/worker/WorkerProfilePage.js.map +1 -1
  124. package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js +284 -309
  125. package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js.map +1 -1
  126. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"InvestmentOpportunitiesPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/investor/InvestmentOpportunitiesPage.tsx"],"sourcesContent":["/**\n * Investment Opportunities Page\n * @module kadaikodi/pages/investor\n *\n * Platform stats, 6 filters (search/category/risk/amount/location/sort),\n * invest modal with amount calculator, opportunity cards with images/funding\n * progress/business metrics/documents.\n */\n\nimport { useState, useCallback, useMemo } from 'react';\nimport { Search, FileText, IndianRupee, Eye, X, Briefcase, CheckCircle2 } from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useInvestmentOpportunities, useInvest } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\nimport type { InvestmentOpportunity as DomainInvestmentOpportunity } from '../../types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface InvestmentOpportunity {\n id: string;\n cartId: string;\n cartName: string;\n cartOwner: string;\n description: string;\n targetAmount: number;\n raisedAmount: number;\n minimumInvestment: number;\n maximumInvestment: number;\n investorCount: number;\n daysLeft: number;\n expectedReturns: number;\n paybackPeriod: number;\n riskLevel: 'low' | 'medium' | 'high';\n category: string;\n location: string;\n image: string;\n businessMetrics: {\n monthlyRevenue: number;\n monthlyOrders: number;\n averageOrderValue: number;\n rating: number;\n reviewCount: number;\n operatingSince: string;\n };\n financials: {\n grossMargin: number;\n netMargin: number;\n monthlyExpenses: number;\n breakEvenAchieved: boolean;\n };\n investmentTerms: {\n equityOffered: number;\n dividendFrequency: 'monthly' | 'quarterly' | 'yearly';\n lockInPeriod: number;\n exitOptions: string[];\n };\n documents: Array<{ name: string; type: string; uploadedOn: string }>;\n status: 'active' | 'funded' | 'closed';\n}\n\ninterface OpportunityFilters {\n searchQuery: string;\n category: string;\n riskLevel: string;\n minimumAmount: string;\n sortBy: string;\n}\n\n// ---------------------------------------------------------------------------\n// Mapping (real domain opportunity → this page's richer view-model)\n// ---------------------------------------------------------------------------\n\nfunction mapOpportunity(o: DomainInvestmentOpportunity): InvestmentOpportunity {\n const daysLeft = o.expiresAt\n ? Math.max(0, Math.ceil((new Date(o.expiresAt).getTime() - Date.now()) / 86400000))\n : 0;\n return {\n id: o.id,\n cartId: o.cartId,\n cartName: o.cartName,\n cartOwner: o.businessOwner || '',\n description: o.description,\n targetAmount: o.targetAmount,\n raisedAmount: o.raisedAmount,\n minimumInvestment: o.minimumInvestment,\n // The backend exposes a single target, not a per-investor cap; use the\n // remaining amount to target as the sensible maximum (never fabricated).\n maximumInvestment: Math.max(o.minimumInvestment, o.targetAmount - o.raisedAmount),\n investorCount: o.investors.length,\n daysLeft,\n expectedReturns: o.expectedReturns,\n paybackPeriod: o.investmentPeriod,\n riskLevel: o.riskLevel,\n category: '',\n location: '',\n image: '',\n businessMetrics: {\n monthlyRevenue: 0,\n monthlyOrders: 0,\n averageOrderValue: 0,\n rating: 0,\n reviewCount: 0,\n operatingSince: '',\n },\n financials: { grossMargin: 0, netMargin: 0, monthlyExpenses: 0, breakEvenAchieved: false },\n investmentTerms: {\n equityOffered: 0,\n dividendFrequency: 'quarterly',\n lockInPeriod: o.investmentPeriod,\n exitOptions: [],\n },\n documents: o.documents.map((d) => ({ name: d, type: 'PDF', uploadedOn: '' })),\n status: o.status,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getRiskBadgeColor(risk: string): string {\n switch (risk) {\n case 'low':\n return 'bg-status-success-bg-subtle text-status-success-text';\n case 'medium':\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n case 'high':\n return 'bg-status-error-bg-subtle text-status-error-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\nfunction getRiskLabel(risk: string): string {\n switch (risk) {\n case 'low':\n return 'Low Risk';\n case 'medium':\n return 'Medium Risk';\n case 'high':\n return 'High Risk';\n default:\n return risk;\n }\n}\n\nfunction getFundingPercentage(raised: number, target: number): number {\n return target > 0 ? Math.min((raised / target) * 100, 100) : 0;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function InvestmentOpportunitiesPage() {\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: rawOpportunities = [], isLoading } = useInvestmentOpportunities();\n const invest = useInvest();\n const [selectedOpportunity, setSelectedOpportunity] = useState<InvestmentOpportunity | null>(\n null\n );\n const [showInvestModal, setShowInvestModal] = useState(false);\n const [investmentAmount, setInvestmentAmount] = useState('');\n const [investStatus, setInvestStatus] = useState<'idle' | 'success' | 'error'>('idle');\n\n const [filters, setFilters] = useState<OpportunityFilters>({\n searchQuery: '',\n category: '',\n riskLevel: '',\n minimumAmount: '',\n sortBy: 'featured',\n });\n\n const opportunities = useMemo(() => rawOpportunities.map(mapOpportunity), [rawOpportunities]);\n\n const platformStats = useMemo(() => {\n const count = opportunities.length;\n return {\n totalOpportunities: count,\n averageReturns: count\n ? Number((opportunities.reduce((s, o) => s + o.expectedReturns, 0) / count).toFixed(1))\n : 0,\n totalFunded: opportunities.reduce((s, o) => s + o.raisedAmount, 0),\n totalTarget: opportunities.reduce((s, o) => s + o.targetAmount, 0),\n };\n }, [opportunities]);\n\n const handleFilterChange = useCallback((key: keyof OpportunityFilters, value: string) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n const filteredOpportunities = useMemo(() => {\n let filtered = [...opportunities];\n\n // Search\n if (filters.searchQuery.trim()) {\n const query = filters.searchQuery.toLowerCase();\n filtered = filtered.filter(\n (opp) =>\n opp.cartName.toLowerCase().includes(query) ||\n opp.description.toLowerCase().includes(query) ||\n opp.category.toLowerCase().includes(query)\n );\n }\n\n // Category\n if (filters.category) {\n filtered = filtered.filter((opp) => opp.category === filters.category);\n }\n\n // Risk level\n if (filters.riskLevel) {\n filtered = filtered.filter((opp) => opp.riskLevel === filters.riskLevel);\n }\n\n // Minimum amount\n if (filters.minimumAmount) {\n const minAmount = parseInt(filters.minimumAmount, 10);\n filtered = filtered.filter((opp) => opp.minimumInvestment <= minAmount);\n }\n\n // Sort\n switch (filters.sortBy) {\n case 'returns':\n filtered.sort((a, b) => b.expectedReturns - a.expectedReturns);\n break;\n case 'funding':\n filtered.sort((a, b) => b.raisedAmount / b.targetAmount - a.raisedAmount / a.targetAmount);\n break;\n case 'closing':\n filtered.sort((a, b) => a.daysLeft - b.daysLeft);\n break;\n case 'minimum':\n filtered.sort((a, b) => a.minimumInvestment - b.minimumInvestment);\n break;\n default:\n break;\n }\n\n return filtered;\n }, [filters, opportunities]);\n\n const handleInvest = useCallback(() => {\n if (!selectedOpportunity || !investmentAmount) return;\n\n const amount = parseInt(investmentAmount, 10);\n if (isNaN(amount)) return;\n\n if (amount < selectedOpportunity.minimumInvestment) {\n setInvestStatus('error');\n return;\n }\n if (amount > selectedOpportunity.maximumInvestment) {\n setInvestStatus('error');\n return;\n }\n\n invest.mutate(\n {\n amount,\n investorId: currentUser?.id ?? '',\n opportunityId: selectedOpportunity.id,\n },\n {\n onSuccess: () => {\n setInvestStatus('success');\n setTimeout(() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n navigateTo('/investor/portfolio');\n }, 1500);\n },\n onError: () => setInvestStatus('error'),\n }\n );\n }, [selectedOpportunity, investmentAmount, navigateTo, invest, currentUser?.id]);\n\n const openInvestModal = useCallback((opportunity: InvestmentOpportunity) => {\n setSelectedOpportunity(opportunity);\n setInvestmentAmount('');\n setInvestStatus('idle');\n setShowInvestModal(true);\n }, []);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Investment Opportunities\" description=\"Loading opportunities...\">\n <div className=\"flex items-center justify-center py-20\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-primary\" />\n <p className=\"text-sm text-text-secondary\">Loading opportunities...</p>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Investment Opportunities\"\n description=\"Discover and invest in growing local businesses\"\n actions={\n <span className=\"text-sm text-text-secondary\">\n {filteredOpportunities.length} opportunities\n </span>\n }\n >\n <PagePurpose className=\"mb-6\">\n The deal flow — local carts and businesses looking for funding right now. Use this page to\n discover opportunities that fit your strategy, compare projected returns and equity on\n offer, filter by category or funding stage, and back the ones you believe in. The stats up\n top show how the platform is performing so you can gauge typical returns and success rates\n before you commit.\n </PagePurpose>\n\n {/* Platform Stats — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">Platform Performance</h2>\n <div className=\"grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Active Opportunities</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {platformStats.totalOpportunities}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Average Returns</p>\n <p className=\"text-lg font-bold text-status-success-text sm:text-2xl\">\n {platformStats.averageReturns}%\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Total Funded</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {formatCurrency(platformStats.totalFunded, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Total Target</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {formatCurrency(platformStats.totalTarget, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Filters */}\n <div className=\"mt-6 rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n {/* Search */}\n <div className=\"relative sm:col-span-2 lg:col-span-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search opportunities...\"\n value={filters.searchQuery}\n onChange={(e) => handleFilterChange('searchQuery', e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Category */}\n <select\n value={filters.category}\n onChange={(e) => handleFilterChange('category', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Categories</option>\n <option value=\"Healthy Food\">Healthy Food</option>\n <option value=\"Fast Food\">Fast Food</option>\n <option value=\"Fusion Food\">Fusion Food</option>\n <option value=\"Beverages\">Beverages</option>\n <option value=\"Street Food\">Street Food</option>\n </select>\n\n {/* Risk Level */}\n <select\n value={filters.riskLevel}\n onChange={(e) => handleFilterChange('riskLevel', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Risk Levels</option>\n <option value=\"low\">Low Risk</option>\n <option value=\"medium\">Medium Risk</option>\n <option value=\"high\">High Risk</option>\n </select>\n\n {/* Minimum Amount */}\n <select\n value={filters.minimumAmount}\n onChange={(e) => handleFilterChange('minimumAmount', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any Amount</option>\n <option value=\"15000\">Under &#x20B9;15,000</option>\n <option value=\"25000\">Under &#x20B9;25,000</option>\n <option value=\"50000\">Under &#x20B9;50,000</option>\n <option value=\"100000\">Under &#x20B9;1,00,000</option>\n </select>\n\n {/* Sort By */}\n <select\n value={filters.sortBy}\n onChange={(e) => handleFilterChange('sortBy', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"featured\">Featured</option>\n <option value=\"returns\">Highest Returns</option>\n <option value=\"funding\">Most Funded</option>\n <option value=\"closing\">Closing Soon</option>\n <option value=\"minimum\">Lowest Minimum</option>\n </select>\n </div>\n </div>\n\n {/* Opportunities Grid */}\n {filteredOpportunities.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n icon={<Search className=\"h-10 w-10\" />}\n title=\"No opportunities found\"\n description=\"Try adjusting your filters to find more opportunities\"\n action={\n <button\n type=\"button\"\n onClick={() =>\n setFilters({\n searchQuery: '',\n category: '',\n riskLevel: '',\n minimumAmount: '',\n sortBy: 'featured',\n })\n }\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Clear Filters\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 lg:grid-cols-2\">\n {filteredOpportunities.map((opportunity) => {\n const fundingPct = getFundingPercentage(\n opportunity.raisedAmount,\n opportunity.targetAmount\n );\n\n return (\n <div\n key={opportunity.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm hover:shadow-md transition-shadow\"\n >\n {/* Banner (providers have no image asset — show a neutral placeholder) */}\n <div className=\"relative\">\n <div className=\"flex h-40 w-full items-center justify-center overflow-hidden rounded-t-lg bg-bg-sunken sm:h-48\">\n <Briefcase className=\"h-10 w-10 text-text-secondary\" />\n </div>\n {opportunity.status === 'funded' && (\n <div className=\"absolute right-2 top-2 rounded-full bg-status-success-text px-2 py-1 text-xs font-medium text-white sm:right-4 sm:top-4 sm:px-3\">\n Fully Funded\n </div>\n )}\n {opportunity.daysLeft > 0 && opportunity.daysLeft <= 7 && (\n <div className=\"absolute left-2 top-2 rounded-full bg-status-error-text px-2 py-1 text-xs font-medium text-white sm:left-4 sm:top-4 sm:px-3\">\n {opportunity.daysLeft} days left\n </div>\n )}\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {/* Header */}\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary sm:text-xl\">\n {opportunity.cartName}\n </h3>\n <p className=\"text-xs text-text-secondary sm:text-sm\">\n by {opportunity.cartOwner}\n </p>\n <div className=\"mt-2 flex flex-wrap items-center gap-2\">\n <span className=\"text-xs text-text-secondary sm:text-sm\">\n {opportunity.category}\n </span>\n <span\n className={cn(\n 'rounded-full px-2 py-0.5 text-xs font-medium',\n getRiskBadgeColor(opportunity.riskLevel)\n )}\n >\n {getRiskLabel(opportunity.riskLevel)}\n </span>\n </div>\n </div>\n <div className=\"text-left sm:text-right\">\n <p className=\"text-xl font-bold text-status-success-text sm:text-2xl\">\n {opportunity.expectedReturns}%\n </p>\n <p className=\"text-xs text-text-secondary\">Expected Returns</p>\n </div>\n </div>\n\n {/* Description */}\n <p className=\"mb-4 text-sm leading-relaxed text-text-secondary line-clamp-2\">\n {opportunity.description}\n </p>\n\n {/* Funding Progress */}\n <div className=\"mb-4\">\n <div className=\"mb-1 flex justify-between text-xs sm:text-sm\">\n <span className=\"font-medium text-text-primary\">\n {formatCurrency(opportunity.raisedAmount, { minimumFractionDigits: 0 })}{' '}\n raised\n </span>\n <span className=\"text-text-secondary\">\n of {formatCurrency(opportunity.targetAmount, { minimumFractionDigits: 0 })}\n </span>\n </div>\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg transition-all duration-300\"\n style={{ width: `${fundingPct}%` }}\n />\n </div>\n <div className=\"mt-1 flex justify-between text-xs text-text-secondary\">\n <span>{opportunity.investorCount} investors</span>\n <span>{fundingPct.toFixed(0)}% funded</span>\n </div>\n </div>\n\n {/* Key Metrics */}\n <div className=\"mb-4 grid grid-cols-2 gap-2 text-center sm:gap-4\">\n <div>\n <p className=\"text-sm font-bold text-text-primary sm:text-lg\">\n {formatCurrency(opportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n })}\n </p>\n <p className=\"text-xs text-text-secondary\">Min Investment</p>\n </div>\n <div>\n <p className=\"text-sm font-bold text-text-primary sm:text-lg\">\n {opportunity.paybackPeriod} mo\n </p>\n <p className=\"text-xs text-text-secondary\">Lock-in Period</p>\n </div>\n </div>\n\n {/* Documents */}\n {opportunity.documents.length > 0 && (\n <div className=\"mb-4 border-t border-border-subtle pt-3\">\n <div className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <FileText className=\"h-3.5 w-3.5\" />\n <span>{opportunity.documents.length} documents available</span>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col gap-2 sm:flex-row sm:gap-3\">\n <button\n type=\"button\"\n onClick={() => openInvestModal(opportunity)}\n disabled={opportunity.status === 'funded'}\n className={cn(\n 'flex-1 rounded-lg px-4 py-2.5 text-sm font-medium transition-colors',\n opportunity.status === 'funded'\n ? 'cursor-not-allowed bg-bg-sunken text-text-secondary'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90'\n )}\n >\n {opportunity.status === 'funded' ? 'Fully Funded' : 'Invest Now'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/opportunity/${opportunity.id}`)}\n className=\"flex flex-1 items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-4 w-4\" />\n View Details\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Investment Modal */}\n {showInvestModal && selectedOpportunity && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"relative max-h-[90vh] w-full max-w-md overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n {/* Close button */}\n <button\n type=\"button\"\n onClick={() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n }}\n className=\"absolute right-3 top-3 rounded-lg p-1 text-text-secondary hover:bg-accent transition-colors\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n\n <h3 className=\"mb-4 text-base font-semibold text-text-primary sm:text-lg\">\n Invest in {selectedOpportunity.cartName}\n </h3>\n\n {investStatus === 'success' ? (\n <div className=\"py-8 text-center\">\n <CheckCircle2 className=\"mx-auto mb-3 h-12 w-12 text-status-success-text\" />\n <p className=\"text-sm font-medium text-text-primary\">Investment successful!</p>\n <p className=\"mt-1 text-xs text-text-secondary\">Redirecting to your portfolio...</p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Investment Range */}\n <div>\n <p className=\"mb-1 text-xs text-text-secondary sm:text-sm\">Investment Range</p>\n <p className=\"text-sm font-medium text-text-primary sm:text-base\">\n {formatCurrency(selectedOpportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n })}{' '}\n -{' '}\n {formatCurrency(selectedOpportunity.maximumInvestment, {\n minimumFractionDigits: 0,\n })}\n </p>\n </div>\n\n {/* Amount Input */}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-text-primary sm:text-sm\">\n Investment Amount\n </label>\n <div className=\"relative\">\n <IndianRupee className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"number\"\n value={investmentAmount}\n onChange={(e) => {\n setInvestmentAmount(e.target.value);\n setInvestStatus('idle');\n }}\n placeholder=\"Enter amount\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n {investStatus === 'error' && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n Amount must be between{' '}\n {formatCurrency(selectedOpportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n })}{' '}\n and{' '}\n {formatCurrency(selectedOpportunity.maximumInvestment, {\n minimumFractionDigits: 0,\n })}\n </p>\n )}\n </div>\n\n {/* Returns Calculator */}\n {investmentAmount &&\n !isNaN(parseInt(investmentAmount, 10)) &&\n parseInt(investmentAmount, 10) > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/50 p-3 sm:p-4\">\n <p className=\"mb-2 text-xs text-text-secondary sm:text-sm\">\n Expected Returns\n </p>\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between\">\n <span className=\"text-xs text-text-primary sm:text-sm\">Investment:</span>\n <span className=\"text-xs font-medium text-text-primary sm:text-sm\">\n {formatCurrency(parseInt(investmentAmount, 10), {\n minimumFractionDigits: 0,\n })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-xs text-text-primary sm:text-sm\">\n Expected Return ({selectedOpportunity.expectedReturns}%):\n </span>\n <span className=\"text-xs font-medium text-status-success-text sm:text-sm\">\n +\n {formatCurrency(\n (parseInt(investmentAmount, 10) *\n selectedOpportunity.expectedReturns) /\n 100,\n { minimumFractionDigits: 0 }\n )}\n </span>\n </div>\n <div className=\"flex justify-between border-t border-border-subtle pt-2\">\n <span className=\"text-xs font-medium text-text-primary sm:text-sm\">\n Total Value:\n </span>\n <span className=\"text-xs font-bold text-text-primary sm:text-sm\">\n {formatCurrency(\n parseInt(investmentAmount, 10) *\n (1 + selectedOpportunity.expectedReturns / 100),\n { minimumFractionDigits: 0 }\n )}\n </span>\n </div>\n </div>\n </div>\n )}\n\n {/* Investment Terms */}\n <div className=\"space-y-1 text-xs text-text-secondary sm:text-sm\">\n <p>\n &bull; Minimum lock-in period:{' '}\n {selectedOpportunity.investmentTerms.lockInPeriod} months\n </p>\n <p>\n &bull; Expected returns: {selectedOpportunity.expectedReturns}% over the lock-in\n period\n </p>\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-2 pt-2 sm:flex-row sm:gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n }}\n className=\"flex-1 rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleInvest}\n disabled={\n !investmentAmount ||\n isNaN(parseInt(investmentAmount, 10)) ||\n parseInt(investmentAmount, 10) < selectedOpportunity.minimumInvestment\n }\n className={cn(\n 'flex-1 rounded-lg px-4 py-2.5 text-sm font-medium transition-colors',\n !investmentAmount ||\n isNaN(parseInt(investmentAmount, 10)) ||\n parseInt(investmentAmount, 10) < selectedOpportunity.minimumInvestment\n ? 'cursor-not-allowed bg-bg-sunken text-text-secondary'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90'\n )}\n >\n Confirm Investment\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvestmentOpportunitiesPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/investor/InvestmentOpportunitiesPage.tsx"],"sourcesContent":["/**\n * Investment Opportunities Page\n * @module kadaikodi/pages/investor\n *\n * Platform stats, 6 filters (search/category/risk/amount/location/sort),\n * invest modal with amount calculator, opportunity cards with images/funding\n * progress/business metrics/documents.\n */\n\nimport { useState, useCallback, useMemo } from 'react';\nimport {\n Search,\n FileText,\n IndianRupee,\n Eye,\n Briefcase,\n CheckCircle2,\n X,\n Target,\n Lock,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { AllocationBar } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useInvestmentOpportunities, useInvest } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\nimport type { InvestmentOpportunity as DomainInvestmentOpportunity } from '../../types';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface InvestmentOpportunity {\n id: string;\n cartId: string;\n cartName: string;\n cartOwner: string;\n description: string;\n targetAmount: number;\n raisedAmount: number;\n minimumInvestment: number;\n maximumInvestment: number;\n investorCount: number;\n daysLeft: number;\n expectedReturns: number;\n paybackPeriod: number;\n riskLevel: 'low' | 'medium' | 'high';\n category: string;\n location: string;\n image: string;\n businessMetrics: {\n monthlyRevenue: number;\n monthlyOrders: number;\n averageOrderValue: number;\n rating: number;\n reviewCount: number;\n operatingSince: string;\n };\n financials: {\n grossMargin: number;\n netMargin: number;\n monthlyExpenses: number;\n breakEvenAchieved: boolean;\n };\n investmentTerms: {\n equityOffered: number;\n dividendFrequency: 'monthly' | 'quarterly' | 'yearly';\n lockInPeriod: number;\n exitOptions: string[];\n };\n documents: Array<{ name: string; type: string; uploadedOn: string }>;\n status: 'active' | 'funded' | 'closed';\n}\n\ninterface OpportunityFilters {\n searchQuery: string;\n category: string;\n riskLevel: string;\n minimumAmount: string;\n sortBy: string;\n}\n\n// ---------------------------------------------------------------------------\n// Mapping (real domain opportunity → this page's richer view-model)\n// ---------------------------------------------------------------------------\n\nfunction mapOpportunity(o: DomainInvestmentOpportunity): InvestmentOpportunity {\n const daysLeft = o.expiresAt\n ? Math.max(0, Math.ceil((new Date(o.expiresAt).getTime() - Date.now()) / 86400000))\n : 0;\n return {\n id: o.id,\n cartId: o.cartId,\n cartName: o.cartName,\n cartOwner: o.businessOwner || '',\n description: o.description,\n targetAmount: o.targetAmount,\n raisedAmount: o.raisedAmount,\n minimumInvestment: o.minimumInvestment,\n // The backend exposes a single target, not a per-investor cap; use the\n // remaining amount to target as the sensible maximum (never fabricated).\n maximumInvestment: Math.max(o.minimumInvestment, o.targetAmount - o.raisedAmount),\n investorCount: o.investors.length,\n daysLeft,\n expectedReturns: o.expectedReturns,\n paybackPeriod: o.investmentPeriod,\n riskLevel: o.riskLevel,\n category: '',\n location: '',\n image: '',\n businessMetrics: {\n monthlyRevenue: 0,\n monthlyOrders: 0,\n averageOrderValue: 0,\n rating: 0,\n reviewCount: 0,\n operatingSince: '',\n },\n financials: { grossMargin: 0, netMargin: 0, monthlyExpenses: 0, breakEvenAchieved: false },\n investmentTerms: {\n equityOffered: 0,\n dividendFrequency: 'quarterly',\n lockInPeriod: o.investmentPeriod,\n exitOptions: [],\n },\n documents: o.documents.map((d) => ({ name: d, type: 'PDF', uploadedOn: '' })),\n status: o.status,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction getRiskBadgeColor(risk: string): string {\n switch (risk) {\n case 'low':\n return 'bg-status-success-bg-subtle text-status-success-text';\n case 'medium':\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n case 'high':\n return 'bg-status-error-bg-subtle text-status-error-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\nfunction getRiskLabel(risk: string): string {\n switch (risk) {\n case 'low':\n return 'Low Risk';\n case 'medium':\n return 'Medium Risk';\n case 'high':\n return 'High Risk';\n default:\n return risk;\n }\n}\n\nfunction getFundingPercentage(raised: number, target: number): number {\n return target > 0 ? Math.min((raised / target) * 100, 100) : 0;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function InvestmentOpportunitiesPage() {\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: rawOpportunities = [], isLoading } = useInvestmentOpportunities();\n const invest = useInvest();\n const [selectedOpportunity, setSelectedOpportunity] = useState<InvestmentOpportunity | null>(\n null\n );\n const [showInvestModal, setShowInvestModal] = useState(false);\n const [investmentAmount, setInvestmentAmount] = useState('');\n const [investStatus, setInvestStatus] = useState<'idle' | 'success' | 'error'>('idle');\n\n const [filters, setFilters] = useState<OpportunityFilters>({\n searchQuery: '',\n category: '',\n riskLevel: '',\n minimumAmount: '',\n sortBy: 'featured',\n });\n\n const opportunities = useMemo(() => rawOpportunities.map(mapOpportunity), [rawOpportunities]);\n\n const platformStats = useMemo(() => {\n const count = opportunities.length;\n return {\n totalOpportunities: count,\n averageReturns: count\n ? Number((opportunities.reduce((s, o) => s + o.expectedReturns, 0) / count).toFixed(1))\n : 0,\n totalFunded: opportunities.reduce((s, o) => s + o.raisedAmount, 0),\n totalTarget: opportunities.reduce((s, o) => s + o.targetAmount, 0),\n };\n }, [opportunities]);\n\n const handleFilterChange = useCallback((key: keyof OpportunityFilters, value: string) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n }, []);\n\n const filteredOpportunities = useMemo(() => {\n let filtered = [...opportunities];\n\n // Search\n if (filters.searchQuery.trim()) {\n const query = filters.searchQuery.toLowerCase();\n filtered = filtered.filter(\n (opp) =>\n opp.cartName.toLowerCase().includes(query) ||\n opp.description.toLowerCase().includes(query) ||\n opp.category.toLowerCase().includes(query)\n );\n }\n\n // Category\n if (filters.category) {\n filtered = filtered.filter((opp) => opp.category === filters.category);\n }\n\n // Risk level\n if (filters.riskLevel) {\n filtered = filtered.filter((opp) => opp.riskLevel === filters.riskLevel);\n }\n\n // Minimum amount\n if (filters.minimumAmount) {\n const minAmount = parseInt(filters.minimumAmount, 10);\n filtered = filtered.filter((opp) => opp.minimumInvestment <= minAmount);\n }\n\n // Sort\n switch (filters.sortBy) {\n case 'returns':\n filtered.sort((a, b) => b.expectedReturns - a.expectedReturns);\n break;\n case 'funding':\n filtered.sort((a, b) => b.raisedAmount / b.targetAmount - a.raisedAmount / a.targetAmount);\n break;\n case 'closing':\n filtered.sort((a, b) => a.daysLeft - b.daysLeft);\n break;\n case 'minimum':\n filtered.sort((a, b) => a.minimumInvestment - b.minimumInvestment);\n break;\n default:\n break;\n }\n\n return filtered;\n }, [filters, opportunities]);\n\n const handleInvest = useCallback(() => {\n if (!selectedOpportunity || !investmentAmount) return;\n\n const amount = parseInt(investmentAmount, 10);\n if (isNaN(amount)) return;\n\n if (amount < selectedOpportunity.minimumInvestment) {\n setInvestStatus('error');\n return;\n }\n if (amount > selectedOpportunity.maximumInvestment) {\n setInvestStatus('error');\n return;\n }\n\n invest.mutate(\n {\n amount,\n investorId: currentUser?.id ?? '',\n opportunityId: selectedOpportunity.id,\n },\n {\n onSuccess: () => {\n setInvestStatus('success');\n setTimeout(() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n navigateTo('/investor/portfolio');\n }, 1500);\n },\n onError: () => setInvestStatus('error'),\n }\n );\n }, [selectedOpportunity, investmentAmount, navigateTo, invest, currentUser?.id]);\n\n const openInvestModal = useCallback((opportunity: InvestmentOpportunity) => {\n setSelectedOpportunity(opportunity);\n setInvestmentAmount('');\n setInvestStatus('idle');\n setShowInvestModal(true);\n }, []);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Investment Opportunities\" description=\"Loading opportunities...\">\n <div className=\"flex items-center justify-center py-20\">\n <div className=\"text-center\">\n <div className=\"mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-primary\" />\n <p className=\"text-sm text-text-secondary\">Loading opportunities...</p>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Investment Opportunities\"\n description=\"Discover and invest in growing local businesses\"\n actions={\n <span className=\"text-sm text-text-secondary\">\n {filteredOpportunities.length} opportunities\n </span>\n }\n >\n <PagePurpose className=\"mb-6\">\n The deal flow — local carts and businesses looking for funding right now. Use this page to\n discover opportunities that fit your strategy, compare projected returns and equity on\n offer, filter by category or funding stage, and back the ones you believe in. The stats up\n top show how the platform is performing so you can gauge typical returns and success rates\n before you commit.\n </PagePurpose>\n\n {/* Platform Stats — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">Platform Performance</h2>\n <div className=\"grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Active Opportunities</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {platformStats.totalOpportunities}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Average Returns</p>\n <p className=\"text-lg font-bold text-status-success-text sm:text-2xl\">\n {platformStats.averageReturns}%\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Total Funded</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {formatCurrency(platformStats.totalFunded, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-3 sm:p-4\">\n <p className=\"text-xs text-text-secondary\">Total Target</p>\n <p className=\"text-lg font-bold text-text-primary sm:text-2xl\">\n {formatCurrency(platformStats.totalTarget, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n </div>\n </EmphasisPanel>\n\n {/* Filters */}\n <div className=\"mt-6 rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5\">\n {/* Search */}\n <div className=\"relative sm:col-span-2 lg:col-span-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search opportunities...\"\n value={filters.searchQuery}\n onChange={(e) => handleFilterChange('searchQuery', e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Category */}\n <select\n value={filters.category}\n onChange={(e) => handleFilterChange('category', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Categories</option>\n <option value=\"Healthy Food\">Healthy Food</option>\n <option value=\"Fast Food\">Fast Food</option>\n <option value=\"Fusion Food\">Fusion Food</option>\n <option value=\"Beverages\">Beverages</option>\n <option value=\"Street Food\">Street Food</option>\n </select>\n\n {/* Risk Level */}\n <select\n value={filters.riskLevel}\n onChange={(e) => handleFilterChange('riskLevel', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Risk Levels</option>\n <option value=\"low\">Low Risk</option>\n <option value=\"medium\">Medium Risk</option>\n <option value=\"high\">High Risk</option>\n </select>\n\n {/* Minimum Amount */}\n <select\n value={filters.minimumAmount}\n onChange={(e) => handleFilterChange('minimumAmount', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any Amount</option>\n <option value=\"15000\">Under &#x20B9;15,000</option>\n <option value=\"25000\">Under &#x20B9;25,000</option>\n <option value=\"50000\">Under &#x20B9;50,000</option>\n <option value=\"100000\">Under &#x20B9;1,00,000</option>\n </select>\n\n {/* Sort By */}\n <select\n value={filters.sortBy}\n onChange={(e) => handleFilterChange('sortBy', e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"featured\">Featured</option>\n <option value=\"returns\">Highest Returns</option>\n <option value=\"funding\">Most Funded</option>\n <option value=\"closing\">Closing Soon</option>\n <option value=\"minimum\">Lowest Minimum</option>\n </select>\n </div>\n </div>\n\n {/* Opportunities Grid */}\n {filteredOpportunities.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n icon={<Search className=\"h-10 w-10\" />}\n title=\"No opportunities found\"\n description=\"Try adjusting your filters to find more opportunities\"\n action={\n <button\n type=\"button\"\n onClick={() =>\n setFilters({\n searchQuery: '',\n category: '',\n riskLevel: '',\n minimumAmount: '',\n sortBy: 'featured',\n })\n }\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Clear Filters\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {filteredOpportunities.map((opportunity) => {\n const fundingPct = getFundingPercentage(\n opportunity.raisedAmount,\n opportunity.targetAmount\n );\n const remaining = Math.max(0, opportunity.targetAmount - opportunity.raisedAmount);\n const isFunded = opportunity.status === 'funded';\n\n return (\n <GlassCard\n key={opportunity.id}\n treatment=\"plain\"\n className=\"flex flex-col p-5\"\n >\n {/* Header — avatar, name, owner, risk badge */}\n <div className=\"flex items-start gap-3\">\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Briefcase className=\"h-5 w-5 text-text-primary\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-xs text-text-secondary\">\n {opportunity.cartOwner || 'Local business'}\n </p>\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {opportunity.cartName}\n </h3>\n </div>\n <span\n className={cn(\n 'shrink-0 rounded-full px-2.5 py-1 text-xs font-semibold',\n getRiskBadgeColor(opportunity.riskLevel)\n )}\n >\n {getRiskLabel(opportunity.riskLevel)}\n </span>\n </div>\n\n {/* Description */}\n {opportunity.description && (\n <p className=\"mt-3 line-clamp-2 text-sm leading-relaxed text-text-secondary\">\n {opportunity.description}\n </p>\n )}\n\n {/* Metric strip — Target / Min / Returns / Lock-in */}\n <div className=\"mt-4 grid grid-cols-4 gap-2 rounded-lg border border-border-subtle bg-bg-sunken/40 p-3 text-center\">\n <div>\n <div className=\"flex items-center justify-center gap-1 text-text-secondary\">\n <Target className=\"h-3.5 w-3.5\" />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">Target</p>\n <p className=\"text-sm font-bold text-text-primary\">\n {formatCurrency(opportunity.targetAmount, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n <div>\n <div className=\"flex items-center justify-center gap-1 text-text-secondary\">\n <IndianRupee className=\"h-3.5 w-3.5\" />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">Min</p>\n <p className=\"text-sm font-bold text-text-primary\">\n {formatCurrency(opportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </p>\n </div>\n <div>\n <div className=\"flex items-center justify-center gap-1 text-text-secondary\">\n <TrendingUp className=\"h-3.5 w-3.5\" />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">Returns</p>\n <p className=\"text-sm font-bold text-status-success-text\">\n {opportunity.expectedReturns}%\n </p>\n </div>\n <div>\n <div className=\"flex items-center justify-center gap-1 text-text-secondary\">\n <Lock className=\"h-3.5 w-3.5\" />\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">Lock-in</p>\n <p className=\"text-sm font-bold text-text-primary\">\n {opportunity.paybackPeriod}mo\n </p>\n </div>\n </div>\n\n {/* Funding — raised vs remaining allocation */}\n <div className=\"mt-4\">\n <AllocationBar\n height={10}\n showLegend={false}\n label={\n <span className=\"text-text-primary\">\n {formatCurrency(opportunity.raisedAmount, { minimumFractionDigits: 0 })}{' '}\n raised\n </span>\n }\n rightLabel={`${fundingPct.toFixed(0)}%`}\n segments={[\n { label: 'Raised', value: opportunity.raisedAmount },\n {\n label: 'Remaining',\n value: remaining,\n color: 'var(--color-bg-sunken)',\n },\n ]}\n />\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n {opportunity.investorCount} investors ·{' '}\n {formatCurrency(opportunity.targetAmount, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}{' '}\n goal\n </p>\n </div>\n\n {/* Documents */}\n {opportunity.documents.length > 0 && (\n <div className=\"mt-3 flex items-center gap-1.5 text-xs text-text-secondary\">\n <FileText className=\"h-3.5 w-3.5\" />\n <span>{opportunity.documents.length} documents available</span>\n </div>\n )}\n\n {/* Actions — pinned to the bottom for even card heights */}\n <div className=\"mt-auto flex flex-col gap-2 pt-4 sm:flex-row\">\n <button\n type=\"button\"\n onClick={() => openInvestModal(opportunity)}\n disabled={isFunded}\n className={cn(\n 'flex-1 rounded-lg px-4 py-2.5 text-sm font-semibold transition-colors',\n isFunded\n ? 'cursor-not-allowed bg-bg-sunken text-text-secondary'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover'\n )}\n >\n {isFunded ? 'Fully Funded' : 'Invest'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/opportunity/${opportunity.id}`)}\n className=\"flex flex-1 items-center justify-center gap-2 rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-4 w-4\" />\n Details\n </button>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n\n {/* Investment Modal */}\n {showInvestModal && selectedOpportunity && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"relative max-h-[90vh] w-full max-w-md overflow-y-auto rounded-lg border border-border-subtle bg-bg-surface p-4 sm:p-6\">\n {/* Close button */}\n <button\n type=\"button\"\n onClick={() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n }}\n className=\"absolute right-3 top-3 rounded-lg p-1 text-text-secondary hover:bg-accent transition-colors\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n\n <h3 className=\"mb-4 text-base font-semibold text-text-primary sm:text-lg\">\n Invest in {selectedOpportunity.cartName}\n </h3>\n\n {investStatus === 'success' ? (\n <div className=\"py-8 text-center\">\n <CheckCircle2 className=\"mx-auto mb-3 h-12 w-12 text-status-success-text\" />\n <p className=\"text-sm font-medium text-text-primary\">Investment successful!</p>\n <p className=\"mt-1 text-xs text-text-secondary\">Redirecting to your portfolio...</p>\n </div>\n ) : (\n <div className=\"space-y-4\">\n {/* Investment Range */}\n <div>\n <p className=\"mb-1 text-xs text-text-secondary sm:text-sm\">Investment Range</p>\n <p className=\"text-sm font-medium text-text-primary sm:text-base\">\n {formatCurrency(selectedOpportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n })}{' '}\n -{' '}\n {formatCurrency(selectedOpportunity.maximumInvestment, {\n minimumFractionDigits: 0,\n })}\n </p>\n </div>\n\n {/* Amount Input */}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-text-primary sm:text-sm\">\n Investment Amount\n </label>\n <div className=\"relative\">\n <IndianRupee className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"number\"\n value={investmentAmount}\n onChange={(e) => {\n setInvestmentAmount(e.target.value);\n setInvestStatus('idle');\n }}\n placeholder=\"Enter amount\"\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n {investStatus === 'error' && (\n <p className=\"mt-1 text-xs text-status-error-text\">\n Amount must be between{' '}\n {formatCurrency(selectedOpportunity.minimumInvestment, {\n minimumFractionDigits: 0,\n })}{' '}\n and{' '}\n {formatCurrency(selectedOpportunity.maximumInvestment, {\n minimumFractionDigits: 0,\n })}\n </p>\n )}\n </div>\n\n {/* Returns Calculator */}\n {investmentAmount &&\n !isNaN(parseInt(investmentAmount, 10)) &&\n parseInt(investmentAmount, 10) > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/50 p-3 sm:p-4\">\n <p className=\"mb-2 text-xs text-text-secondary sm:text-sm\">\n Expected Returns\n </p>\n <div className=\"space-y-1.5\">\n <div className=\"flex justify-between\">\n <span className=\"text-xs text-text-primary sm:text-sm\">Investment:</span>\n <span className=\"text-xs font-medium text-text-primary sm:text-sm\">\n {formatCurrency(parseInt(investmentAmount, 10), {\n minimumFractionDigits: 0,\n })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-xs text-text-primary sm:text-sm\">\n Expected Return ({selectedOpportunity.expectedReturns}%):\n </span>\n <span className=\"text-xs font-medium text-status-success-text sm:text-sm\">\n +\n {formatCurrency(\n (parseInt(investmentAmount, 10) *\n selectedOpportunity.expectedReturns) /\n 100,\n { minimumFractionDigits: 0 }\n )}\n </span>\n </div>\n <div className=\"flex justify-between border-t border-border-subtle pt-2\">\n <span className=\"text-xs font-medium text-text-primary sm:text-sm\">\n Total Value:\n </span>\n <span className=\"text-xs font-bold text-text-primary sm:text-sm\">\n {formatCurrency(\n parseInt(investmentAmount, 10) *\n (1 + selectedOpportunity.expectedReturns / 100),\n { minimumFractionDigits: 0 }\n )}\n </span>\n </div>\n </div>\n </div>\n )}\n\n {/* Investment Terms */}\n <div className=\"space-y-1 text-xs text-text-secondary sm:text-sm\">\n <p>\n &bull; Minimum lock-in period:{' '}\n {selectedOpportunity.investmentTerms.lockInPeriod} months\n </p>\n <p>\n &bull; Expected returns: {selectedOpportunity.expectedReturns}% over the lock-in\n period\n </p>\n </div>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-2 pt-2 sm:flex-row sm:gap-3\">\n <button\n type=\"button\"\n onClick={() => {\n setShowInvestModal(false);\n setInvestmentAmount('');\n setInvestStatus('idle');\n }}\n className=\"flex-1 rounded-lg border border-border-subtle px-4 py-2.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleInvest}\n disabled={\n !investmentAmount ||\n isNaN(parseInt(investmentAmount, 10)) ||\n parseInt(investmentAmount, 10) < selectedOpportunity.minimumInvestment\n }\n className={cn(\n 'flex-1 rounded-lg px-4 py-2.5 text-sm font-medium transition-colors',\n !investmentAmount ||\n isNaN(parseInt(investmentAmount, 10)) ||\n parseInt(investmentAmount, 10) < selectedOpportunity.minimumInvestment\n ? 'cursor-not-allowed bg-bg-sunken text-text-secondary'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90'\n )}\n >\n Confirm Investment\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,18 +1,28 @@
1
1
  import { useKadaikodi as e } from "../../context/KadaikodiContext.js";
2
2
  import { cn as t } from "../../utils/cn.js";
3
3
  import { PageLayout as n } from "../../components/PageLayout.js";
4
- import { MetricsCard as r } from "../../components/MetricsCard.js";
5
- import { EmptyState as i } from "../../components/EmptyState.js";
6
- import { useInvestments as a } from "../../hooks/useKadaikodiApi.js";
7
- import { formatCurrency as o } from "../../utils/formatters.js";
8
- import { useMemo as s } from "react";
9
- import { ArrowDown as c, ArrowUp as l, BarChart3 as u, IndianRupee as d, PieChart as f, Target as p, TrendingUp as m, Wallet as h } from "lucide-react";
10
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
- import { EmphasisPanel as v, PagePurpose as y } from "@burdenoff/fe-libs/ui";
4
+ import { EmptyState as r } from "../../components/EmptyState.js";
5
+ import { useInvestments as i } from "../../hooks/useKadaikodiApi.js";
6
+ import { formatCurrency as a, formatDate as o } from "../../utils/formatters.js";
7
+ import { MetricsCard as s } from "../../components/MetricsCard.js";
8
+ import { AreaChart as c } from "../../components/viz/AreaChart.js";
9
+ import { BarChart as l } from "../../components/viz/BarChart.js";
10
+ import { DonutChart as u } from "../../components/viz/DonutChart.js";
11
+ import "../../components/viz/index.js";
12
+ import { useMemo as d } from "react";
13
+ import { ArrowDown as f, ArrowUp as p, BarChart3 as m, IndianRupee as h, PieChart as g, Target as _, TrendingUp as v, Wallet as y } from "lucide-react";
14
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
15
+ import { EmphasisPanel as S, GlassCard as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
12
16
  //#region src/kadaikodi/pages/investor/InvestorAnalyticsPage.tsx
13
- function b() {
14
- let { navigateTo: b, currentUser: x } = e(), { data: S = [], isLoading: C } = a(x?.id || ""), w = s(() => {
15
- let e = S.reduce((e, t) => e + t.investedAmount, 0), t = S.reduce((e, t) => e + t.currentValue, 0), n = t - e, r = e > 0 ? n / e * 100 : 0, i = S.map((e) => {
17
+ function T(e) {
18
+ return a(e, {
19
+ minimumFractionDigits: 0,
20
+ notation: "compact"
21
+ });
22
+ }
23
+ function E() {
24
+ let { navigateTo: E, currentUser: D } = e(), { data: O = [], isLoading: k } = i(D?.id || ""), A = d(() => {
25
+ let e = O.reduce((e, t) => e + t.investedAmount, 0), t = O.reduce((e, t) => e + t.currentValue, 0), n = t - e, r = e > 0 ? n / e * 100 : 0, i = O.map((e) => {
16
26
  let t = e.currentValue - e.investedAmount, n = e.investedAmount > 0 ? t / e.investedAmount * 100 : 0;
17
27
  return {
18
28
  id: e.id,
@@ -22,143 +32,201 @@ function b() {
22
32
  returns: t,
23
33
  roi: n
24
34
  };
25
- }), a = i.length ? i.reduce((e, t) => e + t.roi, 0) / i.length : 0, o = [...i].sort((e, t) => t.roi - e.roi);
35
+ }), a = i.length ? i.reduce((e, t) => e + t.roi, 0) / i.length : 0, s = [...i].sort((e, t) => t.roi - e.roi), c = [...i].sort((e, t) => t.currentValue - e.currentValue).map((e) => ({
36
+ label: e.cartName,
37
+ value: e.currentValue
38
+ })), l = [...O].sort((e, t) => new Date(e.investmentDate).getTime() - new Date(t.investmentDate).getTime()), u = 0, d = l.map((e) => (u += e.currentValue, {
39
+ label: o(e.investmentDate, {
40
+ day: void 0,
41
+ hour: void 0,
42
+ minute: void 0
43
+ }),
44
+ value: u
45
+ }));
26
46
  return {
27
47
  totalInvested: e,
28
48
  totalValue: t,
29
49
  totalReturns: n,
30
50
  portfolioGrowth: r,
31
51
  averageRoi: a,
32
- performanceByCart: o,
33
- bestPerforming: o[0]?.cartName ?? "",
34
- worstPerforming: o[o.length - 1]?.cartName ?? ""
52
+ performanceByCart: s,
53
+ valueByCart: c,
54
+ valueTrend: d.length === 1 ? [{
55
+ label: "Start",
56
+ value: 0
57
+ }, ...d] : d,
58
+ bestPerforming: s[0]?.cartName ?? "",
59
+ worstPerforming: s[s.length - 1]?.cartName ?? ""
35
60
  };
36
- }, [S]);
37
- return C ? /* @__PURE__ */ g(n, {
61
+ }, [O]);
62
+ return k ? /* @__PURE__ */ b(n, {
38
63
  title: "Investment Analytics",
39
64
  description: "Loading analytics...",
40
- children: /* @__PURE__ */ g("div", {
41
- className: "flex items-center justify-center py-20",
42
- children: /* @__PURE__ */ _("div", {
43
- className: "text-center",
44
- children: [/* @__PURE__ */ g("div", { className: "mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-primary" }), /* @__PURE__ */ g("p", {
45
- className: "text-sm text-text-secondary",
46
- children: "Loading analytics data..."
47
- })]
48
- })
65
+ children: /* @__PURE__ */ x("div", {
66
+ className: "space-y-4",
67
+ children: [/* @__PURE__ */ b("div", {
68
+ className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
69
+ children: Array.from({ length: 4 }).map((e, t) => /* @__PURE__ */ b("div", { className: "h-28 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken" }, t))
70
+ }), /* @__PURE__ */ b("div", { className: "h-72 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken" })]
49
71
  })
50
- }) : S.length === 0 ? /* @__PURE__ */ g(n, {
72
+ }) : O.length === 0 ? /* @__PURE__ */ b(n, {
51
73
  title: "Investment Analytics",
52
74
  description: "Comprehensive analysis of your portfolio performance",
53
- children: /* @__PURE__ */ g(i, {
75
+ children: /* @__PURE__ */ b(r, {
54
76
  illustration: "empty-data",
55
77
  title: "No analytics yet",
56
78
  description: "Once you back a cart, your portfolio performance and per-cart analytics will appear here.",
57
- action: /* @__PURE__ */ g("button", {
79
+ action: /* @__PURE__ */ b("button", {
58
80
  type: "button",
59
- onClick: () => b("/investor/opportunities"),
60
- className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
81
+ onClick: () => E("/investor/opportunities"),
82
+ className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg-hover transition-colors",
61
83
  children: "Browse Opportunities"
62
84
  })
63
85
  })
64
- }) : /* @__PURE__ */ _(n, {
86
+ }) : /* @__PURE__ */ x(n, {
65
87
  title: "Investment Analytics",
66
88
  description: "Comprehensive analysis of your portfolio performance",
67
89
  children: [
68
- /* @__PURE__ */ g(y, {
90
+ /* @__PURE__ */ b(w, {
69
91
  className: "mb-6",
70
- children: "The analytical lens on your whole portfolio. Use this page to judge how well your money is working — overall value and returns, average ROI — then see your best and worst performers so you can rebalance, double down, or trim exposure."
92
+ children: "The analytical lens on your whole portfolio. Judge how well your money is working — overall value and returns, average ROI, how value has grown, and where it's concentrated — then see your best and worst performers so you can rebalance, double down, or trim exposure."
71
93
  }),
72
- /* @__PURE__ */ _(v, { children: [/* @__PURE__ */ g("h2", {
94
+ /* @__PURE__ */ x(S, { children: [/* @__PURE__ */ b("h2", {
73
95
  className: "mb-4 text-base font-semibold text-text-primary",
74
96
  children: "Portfolio Performance"
75
- }), /* @__PURE__ */ _("div", {
97
+ }), /* @__PURE__ */ x("div", {
76
98
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4",
77
99
  children: [
78
- /* @__PURE__ */ g(r, {
100
+ /* @__PURE__ */ b(s, {
79
101
  featured: !0,
80
- icon: /* @__PURE__ */ g(h, { className: "h-5 w-5" }),
81
- value: o(w.totalValue, { minimumFractionDigits: 0 }),
102
+ icon: /* @__PURE__ */ b(y, { className: "h-5 w-5" }),
103
+ value: a(A.totalValue, { minimumFractionDigits: 0 }),
82
104
  label: "Portfolio Value",
83
- trend: w.portfolioGrowth >= 0 ? "up" : "down",
84
- trendValue: `${w.portfolioGrowth >= 0 ? "+" : ""}${w.portfolioGrowth.toFixed(1)}%`
105
+ trend: A.portfolioGrowth >= 0 ? "up" : "down",
106
+ trendValue: `${A.portfolioGrowth >= 0 ? "+" : ""}${A.portfolioGrowth.toFixed(1)}%`
85
107
  }),
86
- /* @__PURE__ */ g(r, {
87
- icon: /* @__PURE__ */ g(d, { className: "h-5 w-5" }),
88
- value: o(w.totalInvested, { minimumFractionDigits: 0 }),
108
+ /* @__PURE__ */ b(s, {
109
+ icon: /* @__PURE__ */ b(h, { className: "h-5 w-5" }),
110
+ value: a(A.totalInvested, { minimumFractionDigits: 0 }),
89
111
  label: "Total Invested"
90
112
  }),
91
- /* @__PURE__ */ g(r, {
92
- icon: /* @__PURE__ */ g(m, { className: "h-5 w-5" }),
93
- value: o(w.totalReturns, { minimumFractionDigits: 0 }),
113
+ /* @__PURE__ */ b(s, {
114
+ icon: /* @__PURE__ */ b(v, { className: "h-5 w-5" }),
115
+ value: a(A.totalReturns, { minimumFractionDigits: 0 }),
94
116
  label: "Total Returns",
95
- trend: w.totalReturns >= 0 ? "up" : "down"
117
+ trend: A.totalReturns >= 0 ? "up" : "down"
96
118
  }),
97
- /* @__PURE__ */ g(r, {
98
- icon: /* @__PURE__ */ g(p, { className: "h-5 w-5" }),
99
- value: `${w.averageRoi.toFixed(1)}%`,
119
+ /* @__PURE__ */ b(s, {
120
+ icon: /* @__PURE__ */ b(_, { className: "h-5 w-5" }),
121
+ value: `${A.averageRoi.toFixed(1)}%`,
100
122
  label: "Average ROI"
101
123
  })
102
124
  ]
103
125
  })] }),
104
- w.performanceByCart.length > 1 && /* @__PURE__ */ _("div", {
105
- className: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2",
106
- children: [/* @__PURE__ */ _("div", {
126
+ /* @__PURE__ */ x("div", {
127
+ className: "mt-6 grid gap-6 lg:grid-cols-3",
128
+ children: [/* @__PURE__ */ x(C, {
129
+ treatment: "plain",
130
+ className: "p-5 sm:p-6 lg:col-span-2",
131
+ children: [/* @__PURE__ */ b("h2", {
132
+ className: "mb-4 text-lg font-semibold text-text-primary",
133
+ children: "Portfolio Value Trend"
134
+ }), /* @__PURE__ */ b(c, {
135
+ series: A.valueTrend,
136
+ height: 240,
137
+ valueFormatter: T,
138
+ title: "Cumulative portfolio value by holding date"
139
+ })]
140
+ }), /* @__PURE__ */ x(C, {
141
+ treatment: "plain",
142
+ className: "p-5 sm:p-6",
143
+ children: [/* @__PURE__ */ b("h2", {
144
+ className: "mb-4 text-lg font-semibold text-text-primary",
145
+ children: "Allocation"
146
+ }), /* @__PURE__ */ b("div", {
147
+ className: "flex justify-center",
148
+ children: /* @__PURE__ */ b(u, {
149
+ segments: A.valueByCart,
150
+ size: 180,
151
+ centerLabel: T(A.totalValue),
152
+ centerCaption: "total value",
153
+ valueFormatter: T
154
+ })
155
+ })]
156
+ })]
157
+ }),
158
+ A.valueByCart.length > 1 && /* @__PURE__ */ x(C, {
159
+ treatment: "plain",
160
+ className: "mt-6 p-5 sm:p-6",
161
+ children: [/* @__PURE__ */ b("h2", {
162
+ className: "mb-4 text-lg font-semibold text-text-primary",
163
+ children: "Value by Cart"
164
+ }), /* @__PURE__ */ b(l, {
165
+ series: A.valueByCart,
166
+ height: 240,
167
+ colorful: !0,
168
+ valueFormatter: T,
169
+ title: "Current value held per cart"
170
+ })]
171
+ }),
172
+ A.performanceByCart.length > 1 && /* @__PURE__ */ x("div", {
173
+ className: "mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2",
174
+ children: [/* @__PURE__ */ x("div", {
107
175
  className: "flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4",
108
- children: [/* @__PURE__ */ g("div", {
176
+ children: [/* @__PURE__ */ b("div", {
109
177
  className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-status-success-bg-subtle",
110
- children: /* @__PURE__ */ g(l, { className: "h-5 w-5 text-status-success-text" })
111
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
178
+ children: /* @__PURE__ */ b(p, { className: "h-5 w-5 text-status-success-text" })
179
+ }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
112
180
  className: "text-xs text-text-secondary",
113
181
  children: "Best Performing"
114
- }), /* @__PURE__ */ g("p", {
182
+ }), /* @__PURE__ */ b("p", {
115
183
  className: "text-sm font-semibold text-status-success-text",
116
- children: w.bestPerforming
184
+ children: A.bestPerforming
117
185
  })] })]
118
- }), /* @__PURE__ */ _("div", {
186
+ }), /* @__PURE__ */ x("div", {
119
187
  className: "flex items-center gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4",
120
- children: [/* @__PURE__ */ g("div", {
188
+ children: [/* @__PURE__ */ b("div", {
121
189
  className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-status-error-bg-subtle",
122
- children: /* @__PURE__ */ g(c, { className: "h-5 w-5 text-status-error-text" })
123
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
190
+ children: /* @__PURE__ */ b(f, { className: "h-5 w-5 text-status-error-text" })
191
+ }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
124
192
  className: "text-xs text-text-secondary",
125
193
  children: "Worst Performing"
126
- }), /* @__PURE__ */ g("p", {
194
+ }), /* @__PURE__ */ b("p", {
127
195
  className: "text-sm font-semibold text-text-primary",
128
- children: w.worstPerforming
196
+ children: A.worstPerforming
129
197
  })] })]
130
198
  })]
131
199
  }),
132
- /* @__PURE__ */ _("section", {
200
+ /* @__PURE__ */ x("section", {
133
201
  className: "mt-6",
134
- children: [/* @__PURE__ */ _("div", {
202
+ children: [/* @__PURE__ */ x("div", {
135
203
  className: "mb-4 flex items-center gap-2",
136
- children: [/* @__PURE__ */ g(u, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ g("h2", {
204
+ children: [/* @__PURE__ */ b(m, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ b("h2", {
137
205
  className: "text-lg font-semibold text-text-primary",
138
- children: "Performance by Cart"
206
+ children: "Performance Ranking"
139
207
  })]
140
- }), /* @__PURE__ */ g("div", {
208
+ }), /* @__PURE__ */ b("div", {
141
209
  className: "space-y-3",
142
- children: w.performanceByCart.map((e, n) => /* @__PURE__ */ _("div", {
210
+ children: A.performanceByCart.map((e, n) => /* @__PURE__ */ x("div", {
143
211
  className: "flex items-center justify-between rounded-lg border border-border-subtle bg-bg-surface p-3",
144
- children: [/* @__PURE__ */ _("div", {
212
+ children: [/* @__PURE__ */ x("div", {
145
213
  className: "flex items-center gap-3",
146
- children: [/* @__PURE__ */ g("div", {
214
+ children: [/* @__PURE__ */ b("div", {
147
215
  className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-xs font-bold text-action-primary-text",
148
216
  children: n + 1
149
- }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
217
+ }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("p", {
150
218
  className: "text-sm font-medium text-text-primary",
151
219
  children: e.cartName
152
- }), /* @__PURE__ */ _("p", {
220
+ }), /* @__PURE__ */ x("p", {
153
221
  className: "text-xs text-text-secondary",
154
- children: [o(e.investedAmount, { minimumFractionDigits: 0 }), " invested"]
222
+ children: [a(e.investedAmount, { minimumFractionDigits: 0 }), " invested"]
155
223
  })] })]
156
- }), /* @__PURE__ */ _("div", {
224
+ }), /* @__PURE__ */ x("div", {
157
225
  className: "text-right",
158
- children: [/* @__PURE__ */ _("p", {
226
+ children: [/* @__PURE__ */ x("p", {
159
227
  className: t("text-sm font-semibold", e.returns >= 0 ? "text-status-success-text" : "text-status-error-text"),
160
- children: [e.returns >= 0 ? "+" : "", o(e.returns, { minimumFractionDigits: 0 })]
161
- }), /* @__PURE__ */ _("p", {
228
+ children: [e.returns >= 0 ? "+" : "", a(e.returns, { minimumFractionDigits: 0 })]
229
+ }), /* @__PURE__ */ x("p", {
162
230
  className: t("text-xs", e.roi >= 0 ? "text-status-success-text" : "text-status-error-text"),
163
231
  children: [
164
232
  e.roi >= 0 ? "+" : "",
@@ -170,26 +238,26 @@ function b() {
170
238
  }, e.id))
171
239
  })]
172
240
  }),
173
- /* @__PURE__ */ _("div", {
241
+ /* @__PURE__ */ x("div", {
174
242
  className: "mt-8 flex flex-wrap gap-3",
175
243
  children: [
176
- /* @__PURE__ */ _("button", {
244
+ /* @__PURE__ */ x("button", {
177
245
  type: "button",
178
- onClick: () => b("/investor/portfolio"),
246
+ onClick: () => E("/investor/portfolio"),
179
247
  className: "flex items-center gap-2 rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors",
180
- children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), "View Portfolio"]
248
+ children: [/* @__PURE__ */ b(g, { className: "h-4 w-4" }), "View Portfolio"]
181
249
  }),
182
- /* @__PURE__ */ _("button", {
250
+ /* @__PURE__ */ x("button", {
183
251
  type: "button",
184
- onClick: () => b("/investor/opportunities"),
252
+ onClick: () => E("/investor/opportunities"),
185
253
  className: "flex items-center gap-2 rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors",
186
- children: [/* @__PURE__ */ g(p, { className: "h-4 w-4" }), "Browse Opportunities"]
254
+ children: [/* @__PURE__ */ b(_, { className: "h-4 w-4" }), "Browse Opportunities"]
187
255
  }),
188
- /* @__PURE__ */ _("button", {
256
+ /* @__PURE__ */ x("button", {
189
257
  type: "button",
190
- onClick: () => b("/investor/investments"),
258
+ onClick: () => E("/investor/investments"),
191
259
  className: "flex items-center gap-2 rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors",
192
- children: [/* @__PURE__ */ g(h, { className: "h-4 w-4" }), "My Investments"]
260
+ children: [/* @__PURE__ */ b(y, { className: "h-4 w-4" }), "My Investments"]
193
261
  })
194
262
  ]
195
263
  })
@@ -197,6 +265,6 @@ function b() {
197
265
  });
198
266
  }
199
267
  //#endregion
200
- export { b as InvestorAnalyticsPage };
268
+ export { E as InvestorAnalyticsPage };
201
269
 
202
270
  //# sourceMappingURL=InvestorAnalyticsPage.js.map