@burdenoff/microfe-kadaikodi 2026.710.1 → 2026.716.1

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 (60) hide show
  1. package/dist/kadaikodi/pages/HomePage.d.ts.map +1 -1
  2. package/dist/kadaikodi/pages/HomePage.js.map +1 -1
  3. package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.d.ts.map +1 -1
  4. package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js.map +1 -1
  5. package/dist/kadaikodi/pages/admin/AdminDashboard.d.ts.map +1 -1
  6. package/dist/kadaikodi/pages/admin/AdminDashboard.js.map +1 -1
  7. package/dist/kadaikodi/pages/admin/AdminFinancialPage.d.ts.map +1 -1
  8. package/dist/kadaikodi/pages/admin/AdminFinancialPage.js.map +1 -1
  9. package/dist/kadaikodi/pages/admin/AdminOrdersPage.d.ts.map +1 -1
  10. package/dist/kadaikodi/pages/admin/AdminOrdersPage.js.map +1 -1
  11. package/dist/kadaikodi/pages/admin/CartManagementPage.d.ts.map +1 -1
  12. package/dist/kadaikodi/pages/admin/CartManagementPage.js.map +1 -1
  13. package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.d.ts.map +1 -1
  14. package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
  15. package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.d.ts.map +1 -1
  16. package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js.map +1 -1
  17. package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js.map +1 -1
  18. package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.d.ts.map +1 -1
  19. package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.js.map +1 -1
  20. package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.d.ts.map +1 -1
  21. package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js.map +1 -1
  22. package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.d.ts.map +1 -1
  23. package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js.map +1 -1
  24. package/dist/kadaikodi/pages/customer/BookingDetailsPage.d.ts.map +1 -1
  25. package/dist/kadaikodi/pages/customer/BookingDetailsPage.js.map +1 -1
  26. package/dist/kadaikodi/pages/customer/CartDetailPage.d.ts.map +1 -1
  27. package/dist/kadaikodi/pages/customer/CartDetailPage.js.map +1 -1
  28. package/dist/kadaikodi/pages/customer/CheckoutPage.js.map +1 -1
  29. package/dist/kadaikodi/pages/customer/FavoritesPage.d.ts.map +1 -1
  30. package/dist/kadaikodi/pages/customer/FavoritesPage.js.map +1 -1
  31. package/dist/kadaikodi/pages/customer/OrderTrackingPage.d.ts.map +1 -1
  32. package/dist/kadaikodi/pages/customer/OrderTrackingPage.js.map +1 -1
  33. package/dist/kadaikodi/pages/customer/ReviewsPage.d.ts.map +1 -1
  34. package/dist/kadaikodi/pages/customer/ReviewsPage.js.map +1 -1
  35. package/dist/kadaikodi/pages/customer/shared.d.ts.map +1 -1
  36. package/dist/kadaikodi/pages/customer/shared.js.map +1 -1
  37. package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.d.ts.map +1 -1
  38. package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js +3 -3
  39. package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js.map +1 -1
  40. package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.d.ts.map +1 -1
  41. package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
  42. package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.d.ts.map +1 -1
  43. package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.js.map +1 -1
  44. package/dist/kadaikodi/pages/worker/TaskDetailsPage.d.ts.map +1 -1
  45. package/dist/kadaikodi/pages/worker/TaskDetailsPage.js.map +1 -1
  46. package/dist/kadaikodi/pages/worker/WorkerDashboard.d.ts.map +1 -1
  47. package/dist/kadaikodi/pages/worker/WorkerDashboard.js.map +1 -1
  48. package/dist/kadaikodi/pages/worker/WorkerSchedulePage.d.ts.map +1 -1
  49. package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js.map +1 -1
  50. package/dist/services/createSandboxAssistantTransport.d.ts +8 -27
  51. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -1
  52. package/dist/services/createSandboxAssistantTransport.js +7 -10
  53. package/dist/services/createSandboxAssistantTransport.js.map +1 -1
  54. package/dist/stories/EmptyState.stories.d.ts +10 -0
  55. package/dist/stories/EmptyState.stories.d.ts.map +1 -0
  56. package/dist/stories/MetricsCard.stories.d.ts +10 -0
  57. package/dist/stories/MetricsCard.stories.d.ts.map +1 -0
  58. package/dist/stories/StatusBadge.stories.d.ts +12 -0
  59. package/dist/stories/StatusBadge.stories.d.ts.map +1 -0
  60. package/package.json +15 -3
@@ -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 {\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":";;;;;;;;;;;;;AA0FA,SAAS,EAAe,GAAuD;CAC7E,IAAM,IAAW,EAAE,YACf,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,KAAK,KAAK,IAAI,MAAS,CAAC,GACjF;AACJ,QAAO;EACL,IAAI,EAAE;EACN,QAAQ,EAAE;EACV,UAAU,EAAE;EACZ,WAAW,EAAE,iBAAiB;EAC9B,aAAa,EAAE;EACf,cAAc,EAAE;EAChB,cAAc,EAAE;EAChB,mBAAmB,EAAE;EAGrB,mBAAmB,KAAK,IAAI,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa;EACjF,eAAe,EAAE,UAAU;EAC3B;EACA,iBAAiB,EAAE;EACnB,eAAe,EAAE;EACjB,WAAW,EAAE;EACb,UAAU;EACV,UAAU;EACV,OAAO;EACP,iBAAiB;GACf,gBAAgB;GAChB,eAAe;GACf,mBAAmB;GACnB,QAAQ;GACR,aAAa;GACb,gBAAgB;GACjB;EACD,YAAY;GAAE,aAAa;GAAG,WAAW;GAAG,iBAAiB;GAAG,mBAAmB;GAAO;EAC1F,iBAAiB;GACf,eAAe;GACf,mBAAmB;GACnB,cAAc,EAAE;GAChB,aAAa,EAAE;GAChB;EACD,WAAW,EAAE,UAAU,KAAK,OAAO;GAAE,MAAM;GAAG,MAAM;GAAO,YAAY;GAAI,EAAE;EAC7E,QAAQ,EAAE;EACX;;AAOH,SAAS,EAAkB,GAAsB;AAC/C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAa,GAAsB;AAC1C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAqB,GAAgB,GAAwB;AACpE,QAAO,IAAS,IAAI,KAAK,IAAK,IAAS,IAAU,KAAK,IAAI,GAAG;;AAO/D,SAAgB,IAA8B;CAC5C,IAAM,EAAE,eAAY,mBAAgB,GAAc,EAC5C,EAAE,MAAM,IAAmB,EAAE,EAAE,iBAAc,GAA4B,EACzE,IAAS,GAAW,EACpB,CAAC,GAAqB,KAA0B,EACpD,KACD,EACK,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAkB,KAAuB,EAAS,GAAG,EACtD,CAAC,GAAc,KAAmB,EAAuC,OAAO,EAEhF,CAAC,GAAS,KAAc,EAA6B;EACzD,aAAa;EACb,UAAU;EACV,WAAW;EACX,eAAe;EACf,QAAQ;EACT,CAAC,EAEI,IAAgB,QAAc,EAAiB,IAAI,EAAe,EAAE,CAAC,EAAiB,CAAC,EAEvF,IAAgB,QAAc;EAClC,IAAM,IAAQ,EAAc;AAC5B,SAAO;GACL,oBAAoB;GACpB,gBAAgB,IACZ,QAAQ,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,iBAAiB,EAAE,GAAG,GAAO,QAAQ,EAAE,CAAC,GACrF;GACJ,aAAa,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE;GAClE,aAAa,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE;GACnE;IACA,CAAC,EAAc,CAAC,EAEb,IAAqB,GAAa,GAA+B,MAAkB;AACvF,KAAY,OAAU;GAAE,GAAG;IAAO,IAAM;GAAO,EAAE;IAChD,EAAE,CAAC,EAEA,IAAwB,QAAc;EAC1C,IAAI,IAAW,CAAC,GAAG,EAAc;AAGjC,MAAI,EAAQ,YAAY,MAAM,EAAE;GAC9B,IAAM,IAAQ,EAAQ,YAAY,aAAa;AAC/C,OAAW,EAAS,QACjB,MACC,EAAI,SAAS,aAAa,CAAC,SAAS,EAAM,IAC1C,EAAI,YAAY,aAAa,CAAC,SAAS,EAAM,IAC7C,EAAI,SAAS,aAAa,CAAC,SAAS,EAAM,CAC7C;;AAcH,MAVI,EAAQ,aACV,IAAW,EAAS,QAAQ,MAAQ,EAAI,aAAa,EAAQ,SAAS,GAIpE,EAAQ,cACV,IAAW,EAAS,QAAQ,MAAQ,EAAI,cAAc,EAAQ,UAAU,GAItE,EAAQ,eAAe;GACzB,IAAM,IAAY,SAAS,EAAQ,eAAe,GAAG;AACrD,OAAW,EAAS,QAAQ,MAAQ,EAAI,qBAAqB,EAAU;;AAIzE,UAAQ,EAAQ,QAAhB;GACE,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,gBAAgB;AAC9D;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa;AAC1F;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;AAChD;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,oBAAoB,EAAE,kBAAkB;AAClE;GACF,QACE;;AAGJ,SAAO;IACN,CAAC,GAAS,EAAc,CAAC,EAEtB,IAAe,QAAkB;AACrC,MAAI,CAAC,KAAuB,CAAC,EAAkB;EAE/C,IAAM,IAAS,SAAS,GAAkB,GAAG;AACzC,aAAM,EAAO,EAEjB;OAAI,IAAS,EAAoB,mBAAmB;AAClD,MAAgB,QAAQ;AACxB;;AAEF,OAAI,IAAS,EAAoB,mBAAmB;AAClD,MAAgB,QAAQ;AACxB;;AAGF,KAAO,OACL;IACE;IACA,YAAY,GAAa,MAAM;IAC/B,eAAe,EAAoB;IACpC,EACD;IACE,iBAAiB;AAEf,KADA,EAAgB,UAAU,EAC1B,iBAAiB;AAIf,MAHA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO,EACvB,EAAW,sBAAsB;QAChC,KAAK;;IAEV,eAAe,EAAgB,QAAQ;IACxC,CACF;;IACA;EAAC;EAAqB;EAAkB;EAAY;EAAQ,GAAa;EAAG,CAAC,EAE1E,IAAkB,GAAa,MAAuC;AAI1E,EAHA,EAAuB,EAAY,EACnC,EAAoB,GAAG,EACvB,EAAgB,OAAO,EACvB,EAAmB,GAAK;IACvB,EAAE,CAAC;AAeN,QAbI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAA2B,aAAY;YACvD,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAK,WAAU,8EAA+E,CAAA,EAC9F,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAA4B,CAAA,CACnE;;GACF,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACG,EAAsB,QAAO,iBACzB;;YANX;GASE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAMhB,CAAA;GAGd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAiD;IAAyB,CAAA,EACxF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAwB,CAAA,EACnE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAc;OACb,CAAA,CACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAmB,CAAA,EAC9D,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAc,gBAAe,IAC5B;SACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAgB,CAAA,EAC3D,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAc,aAAa;QACzC,uBAAuB;QACvB,UAAU;QACX,CAAC;OACA,CAAA,CACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAgB,CAAA,EAC3D,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAc,aAAa;QACzC,uBAAuB;QACvB,UAAU;QACX,CAAC;OACA,CAAA,CACA;;KACF;MACQ,EAAA,CAAA;GAGhB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,SAAD;QACE,MAAK;QACL,aAAY;QACZ,OAAO,EAAQ;QACf,WAAW,MAAM,EAAmB,eAAe,EAAE,OAAO,MAAM;QAClE,WAAU;QACV,CAAA,CACE;;MAGN,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,YAAY,EAAE,OAAO,MAAM;OAC/D,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAuB,CAAA;QACxC,kBAAC,UAAD;SAAQ,OAAM;mBAAe;SAAqB,CAAA;QAClD,kBAAC,UAAD;SAAQ,OAAM;mBAAY;SAAkB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAc;SAAoB,CAAA;QAChD,kBAAC,UAAD;SAAQ,OAAM;mBAAY;SAAkB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAc;SAAoB,CAAA;QACzC;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,aAAa,EAAE,OAAO,MAAM;OAChE,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAwB,CAAA;QACzC,kBAAC,UAAD;SAAQ,OAAM;mBAAM;SAAiB,CAAA;QACrC,kBAAC,UAAD;SAAQ,OAAM;mBAAS;SAAoB,CAAA;QAC3C,kBAAC,UAAD;SAAQ,OAAM;mBAAO;SAAkB,CAAA;QAChC;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,iBAAiB,EAAE,OAAO,MAAM;OACpE,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAmB,CAAA;QACpC,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAS;SAA+B,CAAA;QAC/C;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,UAAU,EAAE,OAAO,MAAM;OAC7D,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAW;SAAiB,CAAA;QAC1C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAwB,CAAA;QAChD,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAoB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAqB,CAAA;QAC7C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAuB,CAAA;QACxC;;MACL;;IACF,CAAA;GAGL,EAAsB,WAAW,IAChC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;KACtC,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,UAAD;MACE,MAAK;MACL,eACE,EAAW;OACT,aAAa;OACb,UAAU;OACV,WAAW;OACX,eAAe;OACf,QAAQ;OACT,CAAC;MAEJ,WAAU;gBACX;MAEQ,CAAA;KAEX,CAAA;IACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAsB,KAAK,MAAgB;KAC1C,IAAM,IAAa,EACjB,EAAY,cACZ,EAAY,aACb,EACK,IAAY,KAAK,IAAI,GAAG,EAAY,eAAe,EAAY,aAAa,EAC5E,IAAW,EAAY,WAAW;AAExC,YACE,kBAAC,GAAD;MAEE,WAAU;MACV,WAAU;gBAHZ;OAME,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAW,WAAU,6BAA8B,CAAA;UAC/C,CAAA;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAY,aAAa;WACxB,CAAA,EACJ,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAY;WACV,CAAA,CACD;;SACN,kBAAC,QAAD;UACE,WAAW,EACT,2DACA,EAAkB,EAAY,UAAU,CACzC;oBAEA,EAAa,EAAY,UAAU;UAC/B,CAAA;SACH;;OAGL,EAAY,eACX,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAY;QACX,CAAA;OAIN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;WAC9B,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAU,CAAA;UAC1D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAe,EAAY,cAAc;YACxC,uBAAuB;YACvB,UAAU;YACX,CAAC;WACA,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA;WACnC,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAO,CAAA;UACvD,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAe,EAAY,mBAAmB;YAC7C,uBAAuB;YACvB,UAAU;YACX,CAAC;WACA,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;WAClC,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAW,CAAA;UAC3D,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACG,EAAY,iBAAgB,IAC3B;;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA;WAC5B,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAW,CAAA;UAC3D,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACG,EAAY,eAAc,KACzB;;UACA,EAAA,CAAA;SACF;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SACE,QAAQ;SACR,YAAY;SACZ,OACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAe,EAAY,cAAc,EAAE,uBAAuB,GAAG,CAAC;WAAE;WAAI;WAExE;;SAET,YAAY,GAAG,EAAW,QAAQ,EAAE,CAAC;SACrC,UAAU,CACR;UAAE,OAAO;UAAU,OAAO,EAAY;UAAc,EACpD;UACE,OAAO;UACP,OAAO;UACP,OAAO;UACR,CACF;SACD,CAAA,EACF,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAY;UAAc;UAAa;UACvC,EAAe,EAAY,cAAc;WACxC,uBAAuB;WACvB,UAAU;WACX,CAAC;UAAE;UAAI;UAEN;WACA;;OAGL,EAAY,UAAU,SAAS,KAC9B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACpC,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAY,UAAU,QAAO,uBAA2B,EAAA,CAAA,CAC3D;;OAIR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAgB,EAAY;SAC3C,UAAU;SACV,WAAW,EACT,yEACA,IACI,wDACA,iFACL;mBAEA,IAAW,iBAAiB;SACtB,CAAA,EACT,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,yBAAyB,EAAY,KAAK;SACpE,WAAU;mBAHZ,CAKE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAAA,UAEpB;WACL;;OACI;QA/IL,EAAY,GA+IP;MAEd;IACE,CAAA;GAIP,KAAmB,KAClB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe;AAGb,QAFA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO;;OAEzB,WAAU;iBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;OAClB,CAAA;MAET,kBAAC,MAAD;OAAI,WAAU;iBAAd,CAA0E,cAC7D,EAAoB,SAC5B;;MAEJ,MAAiB,YAChB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAc,WAAU,mDAAoD,CAAA;QAC5E,kBAAC,KAAD;SAAG,WAAU;mBAAwC;SAA0B,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAmC;SAAoC,CAAA;QAChF;WAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8C;SAAoB,CAAA,EAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;UAAE;UAAI;UACN;UACD,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;UACA;WACA,EAAA,CAAA;QAGN,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,WAAU;oBAA8D;UAEvE,CAAA;SACR,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wEAAyE,CAAA,EAChG,kBAAC,SAAD;WACE,MAAK;WACL,OAAO;WACP,WAAW,MAAM;AAEf,YADA,EAAoB,EAAE,OAAO,MAAM,EACnC,EAAgB,OAAO;;WAEzB,aAAY;WACZ,WAAU;WACV,CAAA,CACE;;SACL,MAAiB,WAChB,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAmD;WAC1B;WACtB,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;WAAE;WAAI;WACJ;WACH,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;WACA;;SAEF,EAAA,CAAA;QAGL,KACC,CAAC,MAAM,SAAS,GAAkB,GAAG,CAAC,IACtC,SAAS,GAAkB,GAAG,GAAG,KAC/B,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAA8C;UAEvD,CAAA,EACJ,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAuC;aAAkB,CAAA,EACzE,kBAAC,QAAD;aAAM,WAAU;uBACb,EAAe,SAAS,GAAkB,GAAG,EAAE,EAC9C,uBAAuB,GACxB,CAAC;aACG,CAAA,CACH;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAhB;cAAuD;cACnC,EAAoB;cAAgB;cACjD;gBACP,kBAAC,QAAD;aAAM,WAAU;uBAAhB,CAA0E,KAEvE,EACE,SAAS,GAAkB,GAAG,GAC7B,EAAoB,kBACpB,KACF,EAAE,uBAAuB,GAAG,CAC7B,CACI;eACH;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAmD;aAE5D,CAAA,EACP,kBAAC,QAAD;aAAM,WAAU;uBACb,EACC,SAAS,GAAkB,GAAG,IAC3B,IAAI,EAAoB,kBAAkB,MAC7C,EAAE,uBAAuB,GAAG,CAC7B;aACI,CAAA,CACH;;WACF;YACF;;QAIV,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD,EAAA,UAAA;UAAG;UAC8B;UAC9B,EAAoB,gBAAgB;UAAa;UAChD,EAAA,CAAA,EACJ,kBAAC,KAAD,EAAA,UAAA;UAAG;UACyB,EAAoB;UAAgB;UAE5D,EAAA,CAAA,CACA;;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe;AAGb,WAFA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO;;UAEzB,WAAU;oBACX;UAEQ,CAAA,EACT,kBAAC,UAAD;UACE,MAAK;UACL,SAAS;UACT,UACE,CAAC,KACD,MAAM,SAAS,GAAkB,GAAG,CAAC,IACrC,SAAS,GAAkB,GAAG,GAAG,EAAoB;UAEvD,WAAW,EACT,uEACA,CAAC,KACC,MAAM,SAAS,GAAkB,GAAG,CAAC,IACrC,SAAS,GAAkB,GAAG,GAAG,EAAoB,oBACnD,wDACA,8EACL;oBACF;UAEQ,CAAA,CACL;;QACF;;MAEJ;;IACF,CAAA;GAEG"}
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 key={opportunity.id} treatment=\"plain\" className=\"flex flex-col p-5\">\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":";;;;;;;;;;;;;AA0FA,SAAS,EAAe,GAAuD;CAC7E,IAAM,IAAW,EAAE,YACf,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,KAAK,KAAK,IAAI,MAAS,CAAC,GACjF;AACJ,QAAO;EACL,IAAI,EAAE;EACN,QAAQ,EAAE;EACV,UAAU,EAAE;EACZ,WAAW,EAAE,iBAAiB;EAC9B,aAAa,EAAE;EACf,cAAc,EAAE;EAChB,cAAc,EAAE;EAChB,mBAAmB,EAAE;EAGrB,mBAAmB,KAAK,IAAI,EAAE,mBAAmB,EAAE,eAAe,EAAE,aAAa;EACjF,eAAe,EAAE,UAAU;EAC3B;EACA,iBAAiB,EAAE;EACnB,eAAe,EAAE;EACjB,WAAW,EAAE;EACb,UAAU;EACV,UAAU;EACV,OAAO;EACP,iBAAiB;GACf,gBAAgB;GAChB,eAAe;GACf,mBAAmB;GACnB,QAAQ;GACR,aAAa;GACb,gBAAgB;GACjB;EACD,YAAY;GAAE,aAAa;GAAG,WAAW;GAAG,iBAAiB;GAAG,mBAAmB;GAAO;EAC1F,iBAAiB;GACf,eAAe;GACf,mBAAmB;GACnB,cAAc,EAAE;GAChB,aAAa,EAAE;GAChB;EACD,WAAW,EAAE,UAAU,KAAK,OAAO;GAAE,MAAM;GAAG,MAAM;GAAO,YAAY;GAAI,EAAE;EAC7E,QAAQ,EAAE;EACX;;AAOH,SAAS,EAAkB,GAAsB;AAC/C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAa,GAAsB;AAC1C,SAAQ,GAAR;EACE,KAAK,MACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,EAAqB,GAAgB,GAAwB;AACpE,QAAO,IAAS,IAAI,KAAK,IAAK,IAAS,IAAU,KAAK,IAAI,GAAG;;AAO/D,SAAgB,IAA8B;CAC5C,IAAM,EAAE,eAAY,mBAAgB,GAAc,EAC5C,EAAE,MAAM,IAAmB,EAAE,EAAE,iBAAc,GAA4B,EACzE,IAAS,GAAW,EACpB,CAAC,GAAqB,KAA0B,EACpD,KACD,EACK,CAAC,GAAiB,KAAsB,EAAS,GAAM,EACvD,CAAC,GAAkB,KAAuB,EAAS,GAAG,EACtD,CAAC,GAAc,KAAmB,EAAuC,OAAO,EAEhF,CAAC,GAAS,KAAc,EAA6B;EACzD,aAAa;EACb,UAAU;EACV,WAAW;EACX,eAAe;EACf,QAAQ;EACT,CAAC,EAEI,IAAgB,QAAc,EAAiB,IAAI,EAAe,EAAE,CAAC,EAAiB,CAAC,EAEvF,IAAgB,QAAc;EAClC,IAAM,IAAQ,EAAc;AAC5B,SAAO;GACL,oBAAoB;GACpB,gBAAgB,IACZ,QAAQ,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,iBAAiB,EAAE,GAAG,GAAO,QAAQ,EAAE,CAAC,GACrF;GACJ,aAAa,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE;GAClE,aAAa,EAAc,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE;GACnE;IACA,CAAC,EAAc,CAAC,EAEb,IAAqB,GAAa,GAA+B,MAAkB;AACvF,KAAY,OAAU;GAAE,GAAG;IAAO,IAAM;GAAO,EAAE;IAChD,EAAE,CAAC,EAEA,IAAwB,QAAc;EAC1C,IAAI,IAAW,CAAC,GAAG,EAAc;AAGjC,MAAI,EAAQ,YAAY,MAAM,EAAE;GAC9B,IAAM,IAAQ,EAAQ,YAAY,aAAa;AAC/C,OAAW,EAAS,QACjB,MACC,EAAI,SAAS,aAAa,CAAC,SAAS,EAAM,IAC1C,EAAI,YAAY,aAAa,CAAC,SAAS,EAAM,IAC7C,EAAI,SAAS,aAAa,CAAC,SAAS,EAAM,CAC7C;;AAcH,MAVI,EAAQ,aACV,IAAW,EAAS,QAAQ,MAAQ,EAAI,aAAa,EAAQ,SAAS,GAIpE,EAAQ,cACV,IAAW,EAAS,QAAQ,MAAQ,EAAI,cAAc,EAAQ,UAAU,GAItE,EAAQ,eAAe;GACzB,IAAM,IAAY,SAAS,EAAQ,eAAe,GAAG;AACrD,OAAW,EAAS,QAAQ,MAAQ,EAAI,qBAAqB,EAAU;;AAIzE,UAAQ,EAAQ,QAAhB;GACE,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,gBAAgB;AAC9D;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa;AAC1F;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS;AAChD;GACF,KAAK;AACH,MAAS,MAAM,GAAG,MAAM,EAAE,oBAAoB,EAAE,kBAAkB;AAClE;GACF,QACE;;AAGJ,SAAO;IACN,CAAC,GAAS,EAAc,CAAC,EAEtB,IAAe,QAAkB;AACrC,MAAI,CAAC,KAAuB,CAAC,EAAkB;EAE/C,IAAM,IAAS,SAAS,GAAkB,GAAG;AACzC,aAAM,EAAO,EAEjB;OAAI,IAAS,EAAoB,mBAAmB;AAClD,MAAgB,QAAQ;AACxB;;AAEF,OAAI,IAAS,EAAoB,mBAAmB;AAClD,MAAgB,QAAQ;AACxB;;AAGF,KAAO,OACL;IACE;IACA,YAAY,GAAa,MAAM;IAC/B,eAAe,EAAoB;IACpC,EACD;IACE,iBAAiB;AAEf,KADA,EAAgB,UAAU,EAC1B,iBAAiB;AAIf,MAHA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO,EACvB,EAAW,sBAAsB;QAChC,KAAK;;IAEV,eAAe,EAAgB,QAAQ;IACxC,CACF;;IACA;EAAC;EAAqB;EAAkB;EAAY;EAAQ,GAAa;EAAG,CAAC,EAE1E,IAAkB,GAAa,MAAuC;AAI1E,EAHA,EAAuB,EAAY,EACnC,EAAoB,GAAG,EACvB,EAAgB,OAAO,EACvB,EAAmB,GAAK;IACvB,EAAE,CAAC;AAeN,QAbI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAA2B,aAAY;YACvD,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAK,WAAU,8EAA+E,CAAA,EAC9F,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAA4B,CAAA,CACnE;;GACF,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACG,EAAsB,QAAO,iBACzB;;YANX;GASE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAMhB,CAAA;GAGd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAAiD;IAAyB,CAAA,EACxF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAwB,CAAA,EACnE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAc;OACb,CAAA,CACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAmB,CAAA,EAC9D,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAc,gBAAe,IAC5B;SACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAgB,CAAA,EAC3D,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAc,aAAa;QACzC,uBAAuB;QACvB,UAAU;QACX,CAAC;OACA,CAAA,CACA;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAgB,CAAA,EAC3D,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAe,EAAc,aAAa;QACzC,uBAAuB;QACvB,UAAU;QACX,CAAC;OACA,CAAA,CACA;;KACF;MACQ,EAAA,CAAA;GAGhB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,SAAD;QACE,MAAK;QACL,aAAY;QACZ,OAAO,EAAQ;QACf,WAAW,MAAM,EAAmB,eAAe,EAAE,OAAO,MAAM;QAClE,WAAU;QACV,CAAA,CACE;;MAGN,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,YAAY,EAAE,OAAO,MAAM;OAC/D,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAuB,CAAA;QACxC,kBAAC,UAAD;SAAQ,OAAM;mBAAe;SAAqB,CAAA;QAClD,kBAAC,UAAD;SAAQ,OAAM;mBAAY;SAAkB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAc;SAAoB,CAAA;QAChD,kBAAC,UAAD;SAAQ,OAAM;mBAAY;SAAkB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAc;SAAoB,CAAA;QACzC;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,aAAa,EAAE,OAAO,MAAM;OAChE,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAwB,CAAA;QACzC,kBAAC,UAAD;SAAQ,OAAM;mBAAM;SAAiB,CAAA;QACrC,kBAAC,UAAD;SAAQ,OAAM;mBAAS;SAAoB,CAAA;QAC3C,kBAAC,UAAD;SAAQ,OAAM;mBAAO;SAAkB,CAAA;QAChC;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,iBAAiB,EAAE,OAAO,MAAM;OACpE,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAmB,CAAA;QACpC,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAQ;SAA6B,CAAA;QACnD,kBAAC,UAAD;SAAQ,OAAM;mBAAS;SAA+B,CAAA;QAC/C;;MAGT,kBAAC,UAAD;OACE,OAAO,EAAQ;OACf,WAAW,MAAM,EAAmB,UAAU,EAAE,OAAO,MAAM;OAC7D,WAAU;iBAHZ;QAKE,kBAAC,UAAD;SAAQ,OAAM;mBAAW;SAAiB,CAAA;QAC1C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAwB,CAAA;QAChD,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAoB,CAAA;QAC5C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAqB,CAAA;QAC7C,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAuB,CAAA;QACxC;;MACL;;IACF,CAAA;GAGL,EAAsB,WAAW,IAChC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAQ,WAAU,aAAc,CAAA;KACtC,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,UAAD;MACE,MAAK;MACL,eACE,EAAW;OACT,aAAa;OACb,UAAU;OACV,WAAW;OACX,eAAe;OACf,QAAQ;OACT,CAAC;MAEJ,WAAU;gBACX;MAEQ,CAAA;KAEX,CAAA;IACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAsB,KAAK,MAAgB;KAC1C,IAAM,IAAa,EACjB,EAAY,cACZ,EAAY,aACb,EACK,IAAY,KAAK,IAAI,GAAG,EAAY,eAAe,EAAY,aAAa,EAC5E,IAAW,EAAY,WAAW;AAExC,YACE,kBAAC,GAAD;MAAgC,WAAU;MAAQ,WAAU;gBAA5D;OAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAW,WAAU,6BAA8B,CAAA;UAC/C,CAAA;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAY,aAAa;WACxB,CAAA,EACJ,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAY;WACV,CAAA,CACD;;SACN,kBAAC,QAAD;UACE,WAAW,EACT,2DACA,EAAkB,EAAY,UAAU,CACzC;oBAEA,EAAa,EAAY,UAAU;UAC/B,CAAA;SACH;;OAGL,EAAY,eACX,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAY;QACX,CAAA;OAIN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;WAC9B,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAU,CAAA;UAC1D,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAe,EAAY,cAAc;YACxC,uBAAuB;YACvB,UAAU;YACX,CAAC;WACA,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAa,WAAU,eAAgB,CAAA;WACnC,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAO,CAAA;UACvD,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAe,EAAY,mBAAmB;YAC7C,uBAAuB;YACvB,UAAU;YACX,CAAC;WACA,CAAA;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAY,WAAU,eAAgB,CAAA;WAClC,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAW,CAAA;UAC3D,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACG,EAAY,iBAAgB,IAC3B;;UACA,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD,EAAM,WAAU,eAAgB,CAAA;WAC5B,CAAA;UACN,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAAW,CAAA;UAC3D,kBAAC,KAAD;WAAG,WAAU;qBAAb,CACG,EAAY,eAAc,KACzB;;UACA,EAAA,CAAA;SACF;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SACE,QAAQ;SACR,YAAY;SACZ,OACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACG,EAAe,EAAY,cAAc,EAAE,uBAAuB,GAAG,CAAC;WAAE;WAAI;WAExE;;SAET,YAAY,GAAG,EAAW,QAAQ,EAAE,CAAC;SACrC,UAAU,CACR;UAAE,OAAO;UAAU,OAAO,EAAY;UAAc,EACpD;UACE,OAAO;UACP,OAAO;UACP,OAAO;UACR,CACF;SACD,CAAA,EACF,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAY;UAAc;UAAa;UACvC,EAAe,EAAY,cAAc;WACxC,uBAAuB;WACvB,UAAU;WACX,CAAC;UAAE;UAAI;UAEN;WACA;;OAGL,EAAY,UAAU,SAAS,KAC9B,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA,EACpC,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAY,UAAU,QAAO,uBAA2B,EAAA,CAAA,CAC3D;;OAIR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAgB,EAAY;SAC3C,UAAU;SACV,WAAW,EACT,yEACA,IACI,wDACA,iFACL;mBAEA,IAAW,iBAAiB;SACtB,CAAA,EACT,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAW,yBAAyB,EAAY,KAAK;SACpE,WAAU;mBAHZ,CAKE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAAA,UAEpB;WACL;;OACI;QA5II,EAAY,GA4IhB;MAEd;IACE,CAAA;GAIP,KAAmB,KAClB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEE,kBAAC,UAAD;OACE,MAAK;OACL,eAAe;AAGb,QAFA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO;;OAEzB,WAAU;iBAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;OAClB,CAAA;MAET,kBAAC,MAAD;OAAI,WAAU;iBAAd,CAA0E,cAC7D,EAAoB,SAC5B;;MAEJ,MAAiB,YAChB,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD,EAAc,WAAU,mDAAoD,CAAA;QAC5E,kBAAC,KAAD;SAAG,WAAU;mBAAwC;SAA0B,CAAA;QAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAmC;SAAoC,CAAA;QAChF;WAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA8C;SAAoB,CAAA,EAC/E,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;UAAE;UAAI;UACN;UACD,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;UACA;WACA,EAAA,CAAA;QAGN,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,SAAD;UAAO,WAAU;oBAA8D;UAEvE,CAAA;SACR,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,wEAAyE,CAAA,EAChG,kBAAC,SAAD;WACE,MAAK;WACL,OAAO;WACP,WAAW,MAAM;AAEf,YADA,EAAoB,EAAE,OAAO,MAAM,EACnC,EAAgB,OAAO;;WAEzB,aAAY;WACZ,WAAU;WACV,CAAA,CACE;;SACL,MAAiB,WAChB,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAmD;WAC1B;WACtB,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;WAAE;WAAI;WACJ;WACH,EAAe,EAAoB,mBAAmB,EACrD,uBAAuB,GACxB,CAAC;WACA;;SAEF,EAAA,CAAA;QAGL,KACC,CAAC,MAAM,SAAS,GAAkB,GAAG,CAAC,IACtC,SAAS,GAAkB,GAAG,GAAG,KAC/B,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAA8C;UAEvD,CAAA,EACJ,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAuC;aAAkB,CAAA,EACzE,kBAAC,QAAD;aAAM,WAAU;uBACb,EAAe,SAAS,GAAkB,GAAG,EAAE,EAC9C,uBAAuB,GACxB,CAAC;aACG,CAAA,CACH;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAhB;cAAuD;cACnC,EAAoB;cAAgB;cACjD;gBACP,kBAAC,QAAD;aAAM,WAAU;uBAAhB,CAA0E,KAEvE,EACE,SAAS,GAAkB,GAAG,GAC7B,EAAoB,kBACpB,KACF,EAAE,uBAAuB,GAAG,CAC7B,CACI;eACH;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,QAAD;aAAM,WAAU;uBAAmD;aAE5D,CAAA,EACP,kBAAC,QAAD;aAAM,WAAU;uBACb,EACC,SAAS,GAAkB,GAAG,IAC3B,IAAI,EAAoB,kBAAkB,MAC7C,EAAE,uBAAuB,GAAG,CAC7B;aACI,CAAA,CACH;;WACF;YACF;;QAIV,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD,EAAA,UAAA;UAAG;UAC8B;UAC9B,EAAoB,gBAAgB;UAAa;UAChD,EAAA,CAAA,EACJ,kBAAC,KAAD,EAAA,UAAA;UAAG;UACyB,EAAoB;UAAgB;UAE5D,EAAA,CAAA,CACA;;QAGN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe;AAGb,WAFA,EAAmB,GAAM,EACzB,EAAoB,GAAG,EACvB,EAAgB,OAAO;;UAEzB,WAAU;oBACX;UAEQ,CAAA,EACT,kBAAC,UAAD;UACE,MAAK;UACL,SAAS;UACT,UACE,CAAC,KACD,MAAM,SAAS,GAAkB,GAAG,CAAC,IACrC,SAAS,GAAkB,GAAG,GAAG,EAAoB;UAEvD,WAAW,EACT,uEACA,CAAC,KACC,MAAM,SAAS,GAAkB,GAAG,CAAC,IACrC,SAAS,GAAkB,GAAG,GAAG,EAAoB,oBACnD,wDACA,8EACL;oBACF;UAEQ,CAAA,CACL;;QACF;;MAEJ;;IACF,CAAA;GAEG"}
@@ -1 +1 @@
1
- {"version":3,"file":"InvestorPortfolioPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/investor/InvestorPortfolioPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuCH,wBAAgB,qBAAqB,gCAoUpC"}
1
+ {"version":3,"file":"InvestorPortfolioPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/investor/InvestorPortfolioPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuCH,wBAAgB,qBAAqB,gCAiUpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"InvestorPortfolioPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/investor/InvestorPortfolioPage.tsx"],"sourcesContent":["/**\n * Investor Portfolio Page\n * @module kadaikodi/pages/investor\n *\n * A portfolio dashboard: headline KPI cards, a portfolio-value trend chart, an\n * allocation donut, the full holdings table, and a recent-activity feed. Every\n * figure — including the value trend and the activity feed — is derived from the\n * investor's real holdings (useInvestments). No fabricated series or ledger.\n */\n\nimport { useMemo } from 'react';\nimport {\n Wallet,\n TrendingUp,\n Briefcase,\n IndianRupee,\n Eye,\n Download,\n CheckCircle2,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { EmptyState } from '../../components/EmptyState';\nimport { AreaChart, DonutChart } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useInvestments } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtPct(value: number) {\n return `${value > 0 ? '+' : ''}${value.toFixed(1)}%`;\n}\n\n/** Compact rupee formatter for chart axes. */\nfunction formatRupeeCompact(value: number): string {\n return formatCurrency(value, { minimumFractionDigits: 0, notation: 'compact' });\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function InvestorPortfolioPage() {\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: investments = [], isLoading } = useInvestments(currentUser?.id || '');\n\n const totalInvested = investments.reduce((s, i) => s + i.investedAmount, 0);\n const currentValue = investments.reduce((s, i) => s + i.currentValue, 0);\n const totalReturns = currentValue - totalInvested;\n const returnsPercentage = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n const activeCount = investments.filter((i) => i.status === 'active').length;\n\n // Allocation by cart — derived purely from the real holdings' current value.\n const allocation = useMemo(() => {\n const map = investments.reduce<Record<string, number>>((acc, inv) => {\n acc[inv.cartName] = (acc[inv.cartName] || 0) + inv.currentValue;\n return acc;\n }, {});\n return Object.entries(map)\n .map(([label, value]) => ({ label, value }))\n .sort((a, b) => b.value - a.value);\n }, [investments]);\n\n // Portfolio value trend — the running total of holding value as each stake was\n // added, in date order. Real, derived from the holdings' own dates/values.\n const valueTrend = useMemo(() => {\n const sorted = [...investments].sort(\n (a, b) => new Date(a.investmentDate).getTime() - new Date(b.investmentDate).getTime()\n );\n let running = 0;\n const points = sorted.map((inv) => {\n running += inv.currentValue;\n return {\n label: formatDate(inv.investmentDate, {\n day: undefined,\n hour: undefined,\n minute: undefined,\n }),\n value: running,\n };\n });\n return points.length === 1 ? [{ label: 'Start', value: 0 }, ...points] : points;\n }, [investments]);\n\n // Recent activity — each holding is a real \"Buy\" event with its date/amount.\n const activity = useMemo(\n () =>\n [...investments]\n .sort(\n (a, b) => new Date(b.investmentDate).getTime() - new Date(a.investmentDate).getTime()\n )\n .slice(0, 6),\n [investments]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"My Portfolio\" description=\"Loading your portfolio...\">\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-28 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken\"\n />\n ))}\n </div>\n <div className=\"h-72 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken\" />\n </div>\n </PageLayout>\n );\n }\n\n const isEmpty = investments.length === 0;\n\n return (\n <PageLayout\n title=\"My Portfolio\"\n description=\"View and manage your investments\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/investor/opportunities')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n Add Funds\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n The complete picture of your money on Kadaikodi. See how your portfolio is performing\n overall, how its value has grown as you&apos;ve backed more carts, how it&apos;s split across\n holdings, and the detail of every position.\n </PagePurpose>\n\n {/* Summary Cards — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">Portfolio at a Glance</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Wallet className=\"h-5 w-5\" />}\n value={formatCurrency(totalInvested, { minimumFractionDigits: 0 })}\n label=\"Total Invested\"\n trend=\"neutral\"\n trendValue={`${activeCount} active`}\n />\n <MetricsCard\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={formatCurrency(currentValue, { minimumFractionDigits: 0 })}\n label=\"Current Value\"\n trend={returnsPercentage >= 0 ? 'up' : 'down'}\n trendValue={fmtPct(returnsPercentage)}\n />\n <MetricsCard\n icon={<TrendingUp className=\"h-5 w-5\" />}\n value={fmtPct(returnsPercentage)}\n label=\"Overall Returns\"\n trend={totalReturns >= 0 ? 'up' : 'down'}\n trendValue={formatCurrency(Math.abs(totalReturns), { minimumFractionDigits: 0 })}\n />\n <MetricsCard\n icon={<Briefcase className=\"h-5 w-5\" />}\n value={String(activeCount)}\n label=\"Active Investments\"\n />\n </div>\n </EmphasisPanel>\n\n {isEmpty ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No holdings yet\"\n description=\"Once you back a cart, your portfolio value, allocation and holdings will appear here.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/investor/opportunities')}\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-hover transition-colors\"\n >\n Browse Opportunities\n </button>\n }\n />\n </div>\n ) : (\n <>\n {/* Trend + Allocation */}\n <div className=\"mt-6 grid gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6 lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Portfolio Value Trend</h2>\n <span className=\"text-xs text-text-secondary\">by holding date</span>\n </div>\n <AreaChart\n series={valueTrend}\n height={240}\n valueFormatter={formatRupeeCompact}\n title=\"Cumulative portfolio value by holding date\"\n />\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Allocation</h2>\n <div className=\"flex flex-col items-center gap-4\">\n <DonutChart\n segments={allocation}\n size={180}\n centerLabel={formatRupeeCompact(currentValue)}\n centerCaption=\"total value\"\n valueFormatter={formatRupeeCompact}\n />\n </div>\n </GlassCard>\n </div>\n\n {/* Holdings + Recent Activity */}\n <div className=\"mt-6 grid gap-6 lg:grid-cols-3\">\n {/* Holdings table (desktop) / cards (mobile) */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6 lg:col-span-2\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Your Holdings</h2>\n\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs text-text-secondary\">\n <th className=\"pb-3 pr-4 font-medium\">Merchant</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Invested</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Current Value</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Returns</th>\n <th className=\"pb-3 pr-4 font-medium\">Status</th>\n <th className=\"pb-3 font-medium\" aria-label=\"actions\" />\n </tr>\n </thead>\n <tbody>\n {investments.map((inv) => {\n const pl = inv.currentValue - inv.investedAmount;\n const plPct = inv.investedAmount > 0 ? (pl / inv.investedAmount) * 100 : 0;\n return (\n <tr\n key={inv.id}\n className=\"border-b border-border-subtle/60 last:border-0\"\n >\n <td className=\"py-3 pr-4 font-medium text-text-primary\">{inv.cartName}</td>\n <td className=\"py-3 pr-4 text-right tabular-nums text-text-primary\">\n {formatCurrency(inv.investedAmount, { minimumFractionDigits: 0 })}\n </td>\n <td className=\"py-3 pr-4 text-right tabular-nums text-text-primary\">\n {formatCurrency(inv.currentValue, { minimumFractionDigits: 0 })}\n </td>\n <td\n className={cn(\n 'py-3 pr-4 text-right font-medium tabular-nums',\n pl >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {fmtPct(plPct)}\n </td>\n <td className=\"py-3 pr-4\">\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs font-medium capitalize text-status-success-text\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-status-success-text\" />\n {inv.status}\n </span>\n </td>\n <td className=\"py-3 text-right\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/investments/${inv.id}`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-2.5 py-1.5 text-xs font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n View\n </button>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"space-y-3 md:hidden\">\n {investments.map((inv) => {\n const pl = inv.currentValue - inv.investedAmount;\n const plPct = inv.investedAmount > 0 ? (pl / inv.investedAmount) * 100 : 0;\n return (\n <button\n key={inv.id}\n type=\"button\"\n onClick={() => navigateTo(`/investor/investments/${inv.id}`)}\n className=\"w-full rounded-lg border border-border-subtle p-4 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"font-medium text-text-primary\">{inv.cartName}</span>\n <span\n className={cn(\n 'text-sm font-semibold tabular-nums',\n pl >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {fmtPct(plPct)}\n </span>\n </div>\n <div className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <p className=\"text-xs text-text-secondary\">Invested</p>\n <p className=\"font-medium text-text-primary\">\n {formatCurrency(inv.investedAmount, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Current Value</p>\n <p className=\"font-medium text-text-primary\">\n {formatCurrency(inv.currentValue, { minimumFractionDigits: 0 })}\n </p>\n </div>\n </div>\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n {/* Recent activity — derived from real investment events */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Recent Activity</h2>\n <ul className=\"space-y-4\">\n {activity.map((inv) => (\n <li key={inv.id} className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Download className=\"h-4 w-4 text-text-primary\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n Invested · {inv.cartName}\n </p>\n <span className=\"shrink-0 text-sm font-semibold tabular-nums text-text-primary\">\n {formatCurrency(inv.investedAmount, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </span>\n </div>\n <div className=\"mt-0.5 flex items-center gap-2 text-xs text-text-secondary\">\n <span>\n {formatDate(inv.investmentDate, { hour: undefined, minute: undefined })}\n </span>\n <span className=\"inline-flex items-center gap-1 capitalize text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n {inv.status}\n </span>\n </div>\n </div>\n </li>\n ))}\n </ul>\n </GlassCard>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"InvestorPortfolioPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/investor/InvestorPortfolioPage.tsx"],"sourcesContent":["/**\n * Investor Portfolio Page\n * @module kadaikodi/pages/investor\n *\n * A portfolio dashboard: headline KPI cards, a portfolio-value trend chart, an\n * allocation donut, the full holdings table, and a recent-activity feed. Every\n * figure — including the value trend and the activity feed — is derived from the\n * investor's real holdings (useInvestments). No fabricated series or ledger.\n */\n\nimport { useMemo } from 'react';\nimport {\n Wallet,\n TrendingUp,\n Briefcase,\n IndianRupee,\n Eye,\n Download,\n CheckCircle2,\n} from 'lucide-react';\nimport { EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { EmptyState } from '../../components/EmptyState';\nimport { AreaChart, DonutChart } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useInvestments } from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtPct(value: number) {\n return `${value > 0 ? '+' : ''}${value.toFixed(1)}%`;\n}\n\n/** Compact rupee formatter for chart axes. */\nfunction formatRupeeCompact(value: number): string {\n return formatCurrency(value, { minimumFractionDigits: 0, notation: 'compact' });\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport function InvestorPortfolioPage() {\n const { navigateTo, currentUser } = useKadaikodi();\n const { data: investments = [], isLoading } = useInvestments(currentUser?.id || '');\n\n const totalInvested = investments.reduce((s, i) => s + i.investedAmount, 0);\n const currentValue = investments.reduce((s, i) => s + i.currentValue, 0);\n const totalReturns = currentValue - totalInvested;\n const returnsPercentage = totalInvested > 0 ? (totalReturns / totalInvested) * 100 : 0;\n const activeCount = investments.filter((i) => i.status === 'active').length;\n\n // Allocation by cart — derived purely from the real holdings' current value.\n const allocation = useMemo(() => {\n const map = investments.reduce<Record<string, number>>((acc, inv) => {\n acc[inv.cartName] = (acc[inv.cartName] || 0) + inv.currentValue;\n return acc;\n }, {});\n return Object.entries(map)\n .map(([label, value]) => ({ label, value }))\n .sort((a, b) => b.value - a.value);\n }, [investments]);\n\n // Portfolio value trend — the running total of holding value as each stake was\n // added, in date order. Real, derived from the holdings' own dates/values.\n const valueTrend = useMemo(() => {\n const sorted = [...investments].sort(\n (a, b) => new Date(a.investmentDate).getTime() - new Date(b.investmentDate).getTime()\n );\n let running = 0;\n const points = sorted.map((inv) => {\n running += inv.currentValue;\n return {\n label: formatDate(inv.investmentDate, {\n day: undefined,\n hour: undefined,\n minute: undefined,\n }),\n value: running,\n };\n });\n return points.length === 1 ? [{ label: 'Start', value: 0 }, ...points] : points;\n }, [investments]);\n\n // Recent activity — each holding is a real \"Buy\" event with its date/amount.\n const activity = useMemo(\n () =>\n [...investments]\n .sort((a, b) => new Date(b.investmentDate).getTime() - new Date(a.investmentDate).getTime())\n .slice(0, 6),\n [investments]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"My Portfolio\" description=\"Loading your portfolio...\">\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-28 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken\"\n />\n ))}\n </div>\n <div className=\"h-72 animate-pulse rounded-[var(--radius-card)] bg-bg-sunken\" />\n </div>\n </PageLayout>\n );\n }\n\n const isEmpty = investments.length === 0;\n\n return (\n <PageLayout\n title=\"My Portfolio\"\n description=\"View and manage your investments\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/investor/opportunities')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg-hover transition-colors\"\n >\n Add Funds\n </button>\n }\n >\n <PagePurpose className=\"mb-6\">\n The complete picture of your money on Kadaikodi. See how your portfolio is performing\n overall, how its value has grown as you&apos;ve backed more carts, how it&apos;s split\n across holdings, and the detail of every position.\n </PagePurpose>\n\n {/* Summary Cards — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"mb-4 text-base font-semibold text-text-primary\">Portfolio at a Glance</h2>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <MetricsCard\n featured\n icon={<Wallet className=\"h-5 w-5\" />}\n value={formatCurrency(totalInvested, { minimumFractionDigits: 0 })}\n label=\"Total Invested\"\n trend=\"neutral\"\n trendValue={`${activeCount} active`}\n />\n <MetricsCard\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={formatCurrency(currentValue, { minimumFractionDigits: 0 })}\n label=\"Current Value\"\n trend={returnsPercentage >= 0 ? 'up' : 'down'}\n trendValue={fmtPct(returnsPercentage)}\n />\n <MetricsCard\n icon={<TrendingUp className=\"h-5 w-5\" />}\n value={fmtPct(returnsPercentage)}\n label=\"Overall Returns\"\n trend={totalReturns >= 0 ? 'up' : 'down'}\n trendValue={formatCurrency(Math.abs(totalReturns), { minimumFractionDigits: 0 })}\n />\n <MetricsCard\n icon={<Briefcase className=\"h-5 w-5\" />}\n value={String(activeCount)}\n label=\"Active Investments\"\n />\n </div>\n </EmphasisPanel>\n\n {isEmpty ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No holdings yet\"\n description=\"Once you back a cart, your portfolio value, allocation and holdings will appear here.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/investor/opportunities')}\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-hover transition-colors\"\n >\n Browse Opportunities\n </button>\n }\n />\n </div>\n ) : (\n <>\n {/* Trend + Allocation */}\n <div className=\"mt-6 grid gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6 lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Portfolio Value Trend</h2>\n <span className=\"text-xs text-text-secondary\">by holding date</span>\n </div>\n <AreaChart\n series={valueTrend}\n height={240}\n valueFormatter={formatRupeeCompact}\n title=\"Cumulative portfolio value by holding date\"\n />\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Allocation</h2>\n <div className=\"flex flex-col items-center gap-4\">\n <DonutChart\n segments={allocation}\n size={180}\n centerLabel={formatRupeeCompact(currentValue)}\n centerCaption=\"total value\"\n valueFormatter={formatRupeeCompact}\n />\n </div>\n </GlassCard>\n </div>\n\n {/* Holdings + Recent Activity */}\n <div className=\"mt-6 grid gap-6 lg:grid-cols-3\">\n {/* Holdings table (desktop) / cards (mobile) */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6 lg:col-span-2\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Your Holdings</h2>\n\n {/* Desktop table */}\n <div className=\"hidden overflow-x-auto md:block\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs text-text-secondary\">\n <th className=\"pb-3 pr-4 font-medium\">Merchant</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Invested</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Current Value</th>\n <th className=\"pb-3 pr-4 text-right font-medium\">Returns</th>\n <th className=\"pb-3 pr-4 font-medium\">Status</th>\n <th className=\"pb-3 font-medium\" aria-label=\"actions\" />\n </tr>\n </thead>\n <tbody>\n {investments.map((inv) => {\n const pl = inv.currentValue - inv.investedAmount;\n const plPct = inv.investedAmount > 0 ? (pl / inv.investedAmount) * 100 : 0;\n return (\n <tr key={inv.id} className=\"border-b border-border-subtle/60 last:border-0\">\n <td className=\"py-3 pr-4 font-medium text-text-primary\">\n {inv.cartName}\n </td>\n <td className=\"py-3 pr-4 text-right tabular-nums text-text-primary\">\n {formatCurrency(inv.investedAmount, { minimumFractionDigits: 0 })}\n </td>\n <td className=\"py-3 pr-4 text-right tabular-nums text-text-primary\">\n {formatCurrency(inv.currentValue, { minimumFractionDigits: 0 })}\n </td>\n <td\n className={cn(\n 'py-3 pr-4 text-right font-medium tabular-nums',\n pl >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {fmtPct(plPct)}\n </td>\n <td className=\"py-3 pr-4\">\n <span className=\"inline-flex items-center gap-1.5 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs font-medium capitalize text-status-success-text\">\n <span className=\"h-1.5 w-1.5 rounded-full bg-status-success-text\" />\n {inv.status}\n </span>\n </td>\n <td className=\"py-3 text-right\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/investor/investments/${inv.id}`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-2.5 py-1.5 text-xs font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n View\n </button>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile cards */}\n <div className=\"space-y-3 md:hidden\">\n {investments.map((inv) => {\n const pl = inv.currentValue - inv.investedAmount;\n const plPct = inv.investedAmount > 0 ? (pl / inv.investedAmount) * 100 : 0;\n return (\n <button\n key={inv.id}\n type=\"button\"\n onClick={() => navigateTo(`/investor/investments/${inv.id}`)}\n className=\"w-full rounded-lg border border-border-subtle p-4 text-left transition-colors hover:bg-accent/50\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"font-medium text-text-primary\">{inv.cartName}</span>\n <span\n className={cn(\n 'text-sm font-semibold tabular-nums',\n pl >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {fmtPct(plPct)}\n </span>\n </div>\n <div className=\"mt-3 grid grid-cols-2 gap-3 text-sm\">\n <div>\n <p className=\"text-xs text-text-secondary\">Invested</p>\n <p className=\"font-medium text-text-primary\">\n {formatCurrency(inv.investedAmount, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Current Value</p>\n <p className=\"font-medium text-text-primary\">\n {formatCurrency(inv.currentValue, { minimumFractionDigits: 0 })}\n </p>\n </div>\n </div>\n </button>\n );\n })}\n </div>\n </GlassCard>\n\n {/* Recent activity — derived from real investment events */}\n <GlassCard treatment=\"plain\" className=\"p-5 sm:p-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Recent Activity</h2>\n <ul className=\"space-y-4\">\n {activity.map((inv) => (\n <li key={inv.id} className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[var(--color-accent-soft)]\">\n <Download className=\"h-4 w-4 text-text-primary\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n Invested · {inv.cartName}\n </p>\n <span className=\"shrink-0 text-sm font-semibold tabular-nums text-text-primary\">\n {formatCurrency(inv.investedAmount, {\n minimumFractionDigits: 0,\n notation: 'compact',\n })}\n </span>\n </div>\n <div className=\"mt-0.5 flex items-center gap-2 text-xs text-text-secondary\">\n <span>\n {formatDate(inv.investmentDate, { hour: undefined, minute: undefined })}\n </span>\n <span className=\"inline-flex items-center gap-1 capitalize text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n {inv.status}\n </span>\n </div>\n </div>\n </li>\n ))}\n </ul>\n </GlassCard>\n </div>\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TaskDetailsPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/worker/TaskDetailsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0OH,wBAAgB,eAAe,gCA0Y9B"}
1
+ {"version":3,"file":"TaskDetailsPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/worker/TaskDetailsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA2OH,wBAAgB,eAAe,gCAgZ9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"TaskDetailsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/worker/TaskDetailsPage.tsx"],"sourcesContent":["/**\n * Task Details Page\n *\n * A delivery-job detail screen, elevated to the kk-worker-02-task-details\n * concept: a stylised (honest, not-to-scale) pickup → drop route panel, a\n * StepProgress tracker for the delivery flow, separate Pickup and Drop cards\n * with Call / Navigate actions, an earnings summary, a delivery-note field and\n * a single primary CTA that advances the task (Mark Picked Up → Mark Delivered).\n *\n * All data is real (useWorkerTasks). Status transitions use\n * useUpdateWorkerTaskStatus; the delivery note uses useUpdateWorkerTask. Nothing\n * is fabricated — fields the worker-task backend does not carry (per-item order\n * breakdown, payment badge) are simply omitted.\n *\n * @module kadaikodi/pages/worker\n */\n\nimport { useState, useEffect, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Package,\n Phone,\n MapPin,\n Clock,\n Navigation,\n CheckCircle2,\n Loader2,\n Truck,\n ShoppingBag,\n User,\n AlertTriangle,\n FileText,\n StickyNote,\n IndianRupee,\n ShoppingCart,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { StepProgress, Timeline, type TimelineItem } from '../../components/viz';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useWorkerTasks,\n useUpdateWorkerTask,\n useUpdateWorkerTaskStatus,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\nimport type { TaskStatus, WorkerTask } from '../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface TaskCustomerInfo {\n name: string;\n phone: string;\n address: string;\n}\n\ninterface TaskDetails {\n id: string;\n orderId: string;\n cartId: string;\n cartName: string;\n type: WorkerTask['type'];\n status: TaskStatus;\n priority: WorkerTask['priority'];\n title: string;\n description: string;\n estimatedTime: number;\n assignedBy: string;\n assignedTime: string;\n dueTime: string;\n location?: string;\n earnings: number;\n distance?: number;\n customerInfo: TaskCustomerInfo;\n specialInstructions?: string;\n notes?: string;\n completedAt?: string;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst typeLabels: Record<string, string> = {\n delivery: 'Delivery',\n pickup: 'Pickup',\n service: 'Service',\n};\n\nfunction getTaskTypeIcon(type: string) {\n switch (type) {\n case 'delivery':\n return Truck;\n case 'pickup':\n return ShoppingBag;\n case 'service':\n return Package;\n default:\n return FileText;\n }\n}\n\nfunction getPriorityStyle(priority: string): string {\n switch (priority) {\n case 'urgent':\n case 'high':\n return 'bg-status-error-bg-subtle text-status-error-text';\n case 'medium':\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n case 'low':\n return 'bg-status-success-bg-subtle text-status-success-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\nfunction formatTime(dateString: string): string {\n const d = new Date(dateString);\n if (Number.isNaN(d.getTime())) return dateString;\n return d.toLocaleTimeString('en-IN', { hour: 'numeric', minute: '2-digit', hour12: true });\n}\n\n/** Map a real worker task into the page's detail view-model (no fabricated fields). */\nfunction mapWorkerTaskToDetails(t: WorkerTask): TaskDetails {\n const address = t.deliveryAddress\n ? [t.deliveryAddress.street, t.deliveryAddress.area, t.deliveryAddress.city, t.deliveryAddress.pincode]\n .filter(Boolean)\n .join(', ')\n : '';\n return {\n id: t.id,\n orderId: t.orderId,\n cartId: t.cartId,\n cartName: t.cartName,\n type: t.type,\n status: t.status,\n priority: t.priority,\n title: t.title ?? t.description ?? 'Task',\n description: t.description,\n estimatedTime: t.estimatedTime ?? 0,\n assignedBy: t.assignedBy ?? '',\n assignedTime: t.assignedAt || '',\n dueTime: t.dueAt,\n location: t.location,\n earnings: t.earnings,\n distance: t.distance,\n customerInfo: { name: t.customerName, phone: t.customerPhone, address },\n specialInstructions: t.notes ?? undefined,\n notes: t.notes ?? '',\n completedAt: t.completedAt,\n };\n}\n\nconst DELIVERY_STEPS = [\n { label: 'Assigned' },\n { label: 'Picked Up' },\n { label: 'Delivered' },\n];\n\nfunction currentStep(status: TaskStatus): number {\n switch (status) {\n case 'pending':\n return 1;\n case 'in_progress':\n return 2;\n case 'completed':\n return 3;\n default:\n return 0;\n }\n}\n\n/**\n * Honest, illustrative pickup → drop panel. This is a stylised schematic (clearly\n * labelled \"not to scale\"), NOT a real map — the worker-task backend does not\n * expose geo-coordinates, so we never imply a real position.\n */\nfunction RouteSchematic({\n pickupLabel,\n dropLabel,\n distance,\n}: {\n pickupLabel: string;\n dropLabel: string;\n distance?: number;\n}) {\n return (\n <div className=\"relative overflow-hidden rounded-[var(--radius-card)] border border-border-subtle bg-bg-sunken\">\n <svg\n viewBox=\"0 0 400 190\"\n className=\"block w-full\"\n role=\"img\"\n aria-label={`Illustrative route from ${pickupLabel} to ${dropLabel}`}\n >\n {/* faint street grid */}\n <g stroke=\"var(--color-border-subtle)\" strokeWidth=\"1\" opacity=\"0.7\">\n <path d=\"M0 45 H400 M0 95 H400 M0 145 H400\" />\n <path d=\"M70 0 V190 M160 0 V190 M250 0 V190 M330 0 V190\" />\n </g>\n {/* route */}\n <path\n d=\"M70 55 H150 Q165 55 165 70 V120 Q165 135 180 135 H300\"\n fill=\"none\"\n stroke=\"var(--color-accent-primary)\"\n strokeWidth=\"3.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray=\"1 0\"\n />\n {/* pickup node */}\n <circle cx=\"70\" cy=\"55\" r=\"7\" fill=\"var(--color-accent-primary)\" />\n <circle cx=\"70\" cy=\"55\" r=\"12\" fill=\"var(--color-accent-primary)\" opacity=\"0.18\" />\n {/* drop node */}\n <circle cx=\"300\" cy=\"135\" r=\"7\" fill=\"var(--color-status-success-text)\" />\n <circle cx=\"300\" cy=\"135\" r=\"12\" fill=\"var(--color-status-success-text)\" opacity=\"0.18\" />\n </svg>\n <div className=\"pointer-events-none absolute left-3 top-3 flex items-center gap-1.5 rounded-md bg-bg-surface/85 px-2 py-1 backdrop-blur\">\n <Package className=\"h-3.5 w-3.5 text-accent-primary\" />\n <span className=\"max-w-[9rem] truncate text-[11px] font-medium text-text-primary\">\n {pickupLabel}\n </span>\n </div>\n <div className=\"pointer-events-none absolute bottom-3 right-3 flex items-center gap-1.5 rounded-md bg-bg-surface/85 px-2 py-1 backdrop-blur\">\n <MapPin className=\"h-3.5 w-3.5 text-status-success-text\" />\n <span className=\"max-w-[9rem] truncate text-[11px] font-medium text-text-primary\">\n {dropLabel}\n </span>\n </div>\n <div className=\"flex items-center justify-between border-t border-border-subtle px-3 py-2\">\n <span className=\"text-[11px] text-text-muted\">Illustrative route · not to scale</span>\n {distance != null && (\n <span className=\"text-[11px] font-medium text-text-secondary tabular-nums\">\n {distance} km\n </span>\n )}\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function TaskDetailsPage() {\n const { taskId } = useParams<{ taskId: string }>();\n const { navigateTo, currentUser } = useKadaikodi();\n const workerId = currentUser?.id ?? '';\n const { data: tasks = [], isLoading: loading } = useWorkerTasks(workerId);\n const updateStatus = useUpdateWorkerTaskStatus();\n const updateNotes = useUpdateWorkerTask();\n const [task, setTask] = useState<TaskDetails | null>(null);\n const [notes, setNotes] = useState('');\n const [notesSaved, setNotesSaved] = useState(false);\n\n useEffect(() => {\n const found = tasks.find((t) => t.id === taskId);\n if (found) {\n const mapped = mapWorkerTaskToDetails(found);\n setTask(mapped);\n setNotes(mapped.notes ?? '');\n } else {\n setTask(null);\n }\n }, [tasks, taskId]);\n\n const handleAdvanceStatus = () => {\n if (!task) return;\n if (task.status === 'pending') {\n setTask({ ...task, status: 'in_progress' });\n updateStatus.mutate({ taskId: task.id, status: 'IN_PROGRESS' });\n } else if (task.status === 'in_progress') {\n setTask({ ...task, status: 'completed' });\n updateStatus.mutate({ taskId: task.id, status: 'COMPLETED' });\n }\n };\n\n const handleCallCustomer = () => {\n if (task?.customerInfo?.phone) {\n window.location.href = `tel:${task.customerInfo.phone.replace(/\\s/g, '')}`;\n }\n };\n\n const handleNavigate = (query: string) => {\n if (query) {\n window.open(`https://maps.google.com?q=${encodeURIComponent(query)}`, '_blank');\n }\n };\n\n const handleSaveNotes = () => {\n if (!task) return;\n setNotesSaved(false);\n updateNotes.mutate(\n { taskId: task.id, notes },\n {\n onSuccess: () => {\n setTask((t) => (t ? { ...t, notes } : t));\n setNotesSaved(true);\n },\n }\n );\n };\n\n const timeline = useMemo<TimelineItem[]>(() => {\n if (!task) return [];\n return [\n {\n title: 'Task assigned',\n time: task.assignedTime ? formatTime(task.assignedTime) : undefined,\n meta: task.assignedBy || undefined,\n tone: 'success',\n },\n {\n title: 'Picked up',\n time:\n task.status === 'in_progress' || task.status === 'completed' ? 'Done' : undefined,\n tone: task.status === 'in_progress' || task.status === 'completed' ? 'success' : 'muted',\n },\n {\n title: 'Delivered',\n time: task.status === 'completed' ? (task.completedAt ? formatTime(task.completedAt) : 'Done') : undefined,\n tone: task.status === 'completed' ? 'success' : 'muted',\n },\n ];\n }, [task]);\n\n if (loading) {\n return (\n <PageLayout title=\"Task Details\">\n <div className=\"flex items-center justify-center py-24\">\n <div className=\"text-center\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">Loading task details...</p>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n if (!task) {\n return (\n <PageLayout title=\"Task Not Found\">\n <EmptyState\n icon={<FileText className=\"h-12 w-12\" />}\n title=\"Task not found\"\n description={`No task found with ID \"${taskId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/worker/orders')}\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-hover transition-colors\"\n >\n View All Orders\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const TaskTypeIcon = getTaskTypeIcon(task.type);\n const typeLabel = typeLabels[task.type] ?? 'Task';\n const pickupLabel = task.cartName || 'Pickup';\n const dropLabel = task.customerInfo.address || task.customerInfo.name || 'Drop-off';\n const ctaLabel =\n task.status === 'pending'\n ? 'Mark Picked Up'\n : task.status === 'in_progress'\n ? 'Mark Delivered'\n : 'Delivered';\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/worker/orders')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors shrink-0\"\n aria-label=\"Back to orders\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span className=\"truncate\">\n {typeLabel}\n {task.orderId ? <span className=\"text-text-secondary\"> · #{task.orderId}</span> : null}\n </span>\n </div>\n }\n description=\"Everything you need to complete this delivery\"\n >\n <PagePurpose className=\"mb-6\">\n One delivery, start to finish. Reach the customer, follow the route, jot a note, and advance\n the job — mark it picked up, then delivered — so nothing gets missed and your completion\n stats stay strong.\n </PagePurpose>\n\n <div className=\"space-y-6\">\n {/* Delivery step tracker */}\n <GlassCard treatment=\"metric\" className=\"p-5\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-accent-primary/12 text-accent-primary\">\n <TaskTypeIcon className=\"h-4.5 w-4.5\" />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">{task.title}</h2>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium uppercase',\n getPriorityStyle(task.priority)\n )}\n >\n {task.priority}\n </span>\n </div>\n <StepProgress steps={DELIVERY_STEPS} current={currentStep(task.status)} />\n </GlassCard>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-6\">\n <RouteSchematic\n pickupLabel={pickupLabel}\n dropLabel={dropLabel}\n distance={task.distance}\n />\n\n {/* Pickup + Drop cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Pickup */}\n <GlassCard className=\"p-5\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">Pickup</p>\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{task.cartName}</p>\n {task.location && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.location}</p>\n )}\n <div className=\"mt-4\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(task.location || task.cartName)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Navigate\n </button>\n </div>\n </GlassCard>\n\n {/* Drop */}\n <GlassCard className=\"p-5\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">Drop</p>\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">\n {task.customerInfo.name || 'Customer'}\n </p>\n {task.customerInfo.address && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.customerInfo.address}</p>\n )}\n {task.customerInfo.phone && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.customerInfo.phone}</p>\n )}\n <div className=\"mt-4 flex gap-2\">\n {task.customerInfo.phone && (\n <button\n type=\"button\"\n onClick={handleCallCustomer}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Phone className=\"h-3.5 w-3.5\" />\n Call\n </button>\n )}\n {task.customerInfo.address && (\n <button\n type=\"button\"\n onClick={() => handleNavigate(task.customerInfo.address)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Navigate\n </button>\n )}\n </div>\n </GlassCard>\n </div>\n\n {/* Earnings summary */}\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <IndianRupee className=\"h-4.5 w-4.5\" />\n </span>\n <div>\n <p className=\"text-xs text-text-secondary\">You earn</p>\n <p className=\"text-lg font-bold text-text-primary\">\n {formatCurrency(task.earnings, { minimumFractionDigits: 0 })}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-4 text-sm\">\n <span className=\"flex items-center gap-1.5 text-text-secondary\">\n <ShoppingCart className=\"h-4 w-4\" />\n {task.cartName}\n </span>\n {task.dueTime && (\n <span className=\"flex items-center gap-1.5 text-text-secondary\">\n <Clock className=\"h-4 w-4\" />\n Due {formatTime(task.dueTime)}\n </span>\n )}\n </div>\n </div>\n </GlassCard>\n\n {/* Special Instructions */}\n {task.specialInstructions && (\n <div className=\"rounded-[var(--radius-card)] border border-status-warning-text/20 bg-status-warning-bg-subtle p-4\">\n <p className=\"text-sm text-status-warning-text flex items-start gap-2\">\n <AlertTriangle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n {task.specialInstructions}\n </p>\n </div>\n )}\n\n {/* Delivery note */}\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <StickyNote className=\"h-4 w-4 text-text-secondary\" />\n Delivery note\n </h3>\n <textarea\n value={notes}\n onChange={(e) => {\n setNotes(e.target.value);\n setNotesSaved(false);\n }}\n placeholder=\"Add a delivery note...\"\n rows={3}\n className=\"w-full resize-none rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-border-focus/40\"\n />\n <div className=\"mt-3 flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleSaveNotes}\n disabled={updateNotes.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {updateNotes.isPending ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <StickyNote className=\"h-3.5 w-3.5\" />\n )}\n {updateNotes.isPending ? 'Saving...' : 'Save note'}\n </button>\n {notesSaved && !updateNotes.isPending && (\n <span className=\"text-xs text-status-success-text\">Note saved</span>\n )}\n {updateNotes.isError && (\n <span className=\"text-xs text-status-error-text\">Couldn&apos;t save — retry</span>\n )}\n </div>\n </GlassCard>\n\n {/* Primary CTA */}\n <button\n type=\"button\"\n onClick={handleAdvanceStatus}\n disabled={task.status === 'completed' || updateStatus.isPending}\n className={cn(\n 'w-full inline-flex items-center justify-center gap-2 rounded-[var(--radius-card)] px-4 py-3.5 text-base font-semibold transition-colors',\n task.status === 'completed'\n ? 'bg-status-success-bg-subtle text-status-success-text cursor-default'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-60'\n )}\n >\n {updateStatus.isPending ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-5 w-5\" />\n )}\n {ctaLabel}\n </button>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Delivery timeline</h3>\n <Timeline items={timeline} />\n </GlassCard>\n\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Job details</h3>\n <dl className=\"space-y-3 text-sm\">\n {task.orderId && (\n <div className=\"flex items-center gap-2\">\n <FileText className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Order ID</dt>\n <dd className=\"font-mono font-medium text-text-primary\">{task.orderId}</dd>\n </div>\n </div>\n )}\n {task.customerInfo.name && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Customer</dt>\n <dd className=\"font-medium text-text-primary\">{task.customerInfo.name}</dd>\n </div>\n </div>\n )}\n {task.distance != null && (\n <div className=\"flex items-center gap-2\">\n <Navigation className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Distance</dt>\n <dd className=\"font-medium text-text-primary\">{task.distance} km</dd>\n </div>\n </div>\n )}\n {task.assignedTime && (\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Assigned</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDate(task.assignedTime)}\n </dd>\n </div>\n </div>\n )}\n </dl>\n </GlassCard>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TaskDetailsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/worker/TaskDetailsPage.tsx"],"sourcesContent":["/**\n * Task Details Page\n *\n * A delivery-job detail screen, elevated to the kk-worker-02-task-details\n * concept: a stylised (honest, not-to-scale) pickup → drop route panel, a\n * StepProgress tracker for the delivery flow, separate Pickup and Drop cards\n * with Call / Navigate actions, an earnings summary, a delivery-note field and\n * a single primary CTA that advances the task (Mark Picked Up → Mark Delivered).\n *\n * All data is real (useWorkerTasks). Status transitions use\n * useUpdateWorkerTaskStatus; the delivery note uses useUpdateWorkerTask. Nothing\n * is fabricated — fields the worker-task backend does not carry (per-item order\n * breakdown, payment badge) are simply omitted.\n *\n * @module kadaikodi/pages/worker\n */\n\nimport { useState, useEffect, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Package,\n Phone,\n MapPin,\n Clock,\n Navigation,\n CheckCircle2,\n Loader2,\n Truck,\n ShoppingBag,\n User,\n AlertTriangle,\n FileText,\n StickyNote,\n IndianRupee,\n ShoppingCart,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { StepProgress, Timeline, type TimelineItem } from '../../components/viz';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useWorkerTasks,\n useUpdateWorkerTask,\n useUpdateWorkerTaskStatus,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatDate } from '../../utils/formatters';\nimport type { TaskStatus, WorkerTask } from '../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface TaskCustomerInfo {\n name: string;\n phone: string;\n address: string;\n}\n\ninterface TaskDetails {\n id: string;\n orderId: string;\n cartId: string;\n cartName: string;\n type: WorkerTask['type'];\n status: TaskStatus;\n priority: WorkerTask['priority'];\n title: string;\n description: string;\n estimatedTime: number;\n assignedBy: string;\n assignedTime: string;\n dueTime: string;\n location?: string;\n earnings: number;\n distance?: number;\n customerInfo: TaskCustomerInfo;\n specialInstructions?: string;\n notes?: string;\n completedAt?: string;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst typeLabels: Record<string, string> = {\n delivery: 'Delivery',\n pickup: 'Pickup',\n service: 'Service',\n};\n\nfunction getTaskTypeIcon(type: string) {\n switch (type) {\n case 'delivery':\n return Truck;\n case 'pickup':\n return ShoppingBag;\n case 'service':\n return Package;\n default:\n return FileText;\n }\n}\n\nfunction getPriorityStyle(priority: string): string {\n switch (priority) {\n case 'urgent':\n case 'high':\n return 'bg-status-error-bg-subtle text-status-error-text';\n case 'medium':\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n case 'low':\n return 'bg-status-success-bg-subtle text-status-success-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\nfunction formatTime(dateString: string): string {\n const d = new Date(dateString);\n if (Number.isNaN(d.getTime())) return dateString;\n return d.toLocaleTimeString('en-IN', { hour: 'numeric', minute: '2-digit', hour12: true });\n}\n\n/** Map a real worker task into the page's detail view-model (no fabricated fields). */\nfunction mapWorkerTaskToDetails(t: WorkerTask): TaskDetails {\n const address = t.deliveryAddress\n ? [\n t.deliveryAddress.street,\n t.deliveryAddress.area,\n t.deliveryAddress.city,\n t.deliveryAddress.pincode,\n ]\n .filter(Boolean)\n .join(', ')\n : '';\n return {\n id: t.id,\n orderId: t.orderId,\n cartId: t.cartId,\n cartName: t.cartName,\n type: t.type,\n status: t.status,\n priority: t.priority,\n title: t.title ?? t.description ?? 'Task',\n description: t.description,\n estimatedTime: t.estimatedTime ?? 0,\n assignedBy: t.assignedBy ?? '',\n assignedTime: t.assignedAt || '',\n dueTime: t.dueAt,\n location: t.location,\n earnings: t.earnings,\n distance: t.distance,\n customerInfo: { name: t.customerName, phone: t.customerPhone, address },\n specialInstructions: t.notes ?? undefined,\n notes: t.notes ?? '',\n completedAt: t.completedAt,\n };\n}\n\nconst DELIVERY_STEPS = [{ label: 'Assigned' }, { label: 'Picked Up' }, { label: 'Delivered' }];\n\nfunction currentStep(status: TaskStatus): number {\n switch (status) {\n case 'pending':\n return 1;\n case 'in_progress':\n return 2;\n case 'completed':\n return 3;\n default:\n return 0;\n }\n}\n\n/**\n * Honest, illustrative pickup → drop panel. This is a stylised schematic (clearly\n * labelled \"not to scale\"), NOT a real map — the worker-task backend does not\n * expose geo-coordinates, so we never imply a real position.\n */\nfunction RouteSchematic({\n pickupLabel,\n dropLabel,\n distance,\n}: {\n pickupLabel: string;\n dropLabel: string;\n distance?: number;\n}) {\n return (\n <div className=\"relative overflow-hidden rounded-[var(--radius-card)] border border-border-subtle bg-bg-sunken\">\n <svg\n viewBox=\"0 0 400 190\"\n className=\"block w-full\"\n role=\"img\"\n aria-label={`Illustrative route from ${pickupLabel} to ${dropLabel}`}\n >\n {/* faint street grid */}\n <g stroke=\"var(--color-border-subtle)\" strokeWidth=\"1\" opacity=\"0.7\">\n <path d=\"M0 45 H400 M0 95 H400 M0 145 H400\" />\n <path d=\"M70 0 V190 M160 0 V190 M250 0 V190 M330 0 V190\" />\n </g>\n {/* route */}\n <path\n d=\"M70 55 H150 Q165 55 165 70 V120 Q165 135 180 135 H300\"\n fill=\"none\"\n stroke=\"var(--color-accent-primary)\"\n strokeWidth=\"3.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeDasharray=\"1 0\"\n />\n {/* pickup node */}\n <circle cx=\"70\" cy=\"55\" r=\"7\" fill=\"var(--color-accent-primary)\" />\n <circle cx=\"70\" cy=\"55\" r=\"12\" fill=\"var(--color-accent-primary)\" opacity=\"0.18\" />\n {/* drop node */}\n <circle cx=\"300\" cy=\"135\" r=\"7\" fill=\"var(--color-status-success-text)\" />\n <circle cx=\"300\" cy=\"135\" r=\"12\" fill=\"var(--color-status-success-text)\" opacity=\"0.18\" />\n </svg>\n <div className=\"pointer-events-none absolute left-3 top-3 flex items-center gap-1.5 rounded-md bg-bg-surface/85 px-2 py-1 backdrop-blur\">\n <Package className=\"h-3.5 w-3.5 text-accent-primary\" />\n <span className=\"max-w-[9rem] truncate text-[11px] font-medium text-text-primary\">\n {pickupLabel}\n </span>\n </div>\n <div className=\"pointer-events-none absolute bottom-3 right-3 flex items-center gap-1.5 rounded-md bg-bg-surface/85 px-2 py-1 backdrop-blur\">\n <MapPin className=\"h-3.5 w-3.5 text-status-success-text\" />\n <span className=\"max-w-[9rem] truncate text-[11px] font-medium text-text-primary\">\n {dropLabel}\n </span>\n </div>\n <div className=\"flex items-center justify-between border-t border-border-subtle px-3 py-2\">\n <span className=\"text-[11px] text-text-muted\">Illustrative route · not to scale</span>\n {distance != null && (\n <span className=\"text-[11px] font-medium text-text-secondary tabular-nums\">\n {distance} km\n </span>\n )}\n </div>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function TaskDetailsPage() {\n const { taskId } = useParams<{ taskId: string }>();\n const { navigateTo, currentUser } = useKadaikodi();\n const workerId = currentUser?.id ?? '';\n const { data: tasks = [], isLoading: loading } = useWorkerTasks(workerId);\n const updateStatus = useUpdateWorkerTaskStatus();\n const updateNotes = useUpdateWorkerTask();\n const [task, setTask] = useState<TaskDetails | null>(null);\n const [notes, setNotes] = useState('');\n const [notesSaved, setNotesSaved] = useState(false);\n\n useEffect(() => {\n const found = tasks.find((t) => t.id === taskId);\n if (found) {\n const mapped = mapWorkerTaskToDetails(found);\n setTask(mapped);\n setNotes(mapped.notes ?? '');\n } else {\n setTask(null);\n }\n }, [tasks, taskId]);\n\n const handleAdvanceStatus = () => {\n if (!task) return;\n if (task.status === 'pending') {\n setTask({ ...task, status: 'in_progress' });\n updateStatus.mutate({ taskId: task.id, status: 'IN_PROGRESS' });\n } else if (task.status === 'in_progress') {\n setTask({ ...task, status: 'completed' });\n updateStatus.mutate({ taskId: task.id, status: 'COMPLETED' });\n }\n };\n\n const handleCallCustomer = () => {\n if (task?.customerInfo?.phone) {\n window.location.href = `tel:${task.customerInfo.phone.replace(/\\s/g, '')}`;\n }\n };\n\n const handleNavigate = (query: string) => {\n if (query) {\n window.open(`https://maps.google.com?q=${encodeURIComponent(query)}`, '_blank');\n }\n };\n\n const handleSaveNotes = () => {\n if (!task) return;\n setNotesSaved(false);\n updateNotes.mutate(\n { taskId: task.id, notes },\n {\n onSuccess: () => {\n setTask((t) => (t ? { ...t, notes } : t));\n setNotesSaved(true);\n },\n }\n );\n };\n\n const timeline = useMemo<TimelineItem[]>(() => {\n if (!task) return [];\n return [\n {\n title: 'Task assigned',\n time: task.assignedTime ? formatTime(task.assignedTime) : undefined,\n meta: task.assignedBy || undefined,\n tone: 'success',\n },\n {\n title: 'Picked up',\n time: task.status === 'in_progress' || task.status === 'completed' ? 'Done' : undefined,\n tone: task.status === 'in_progress' || task.status === 'completed' ? 'success' : 'muted',\n },\n {\n title: 'Delivered',\n time:\n task.status === 'completed'\n ? task.completedAt\n ? formatTime(task.completedAt)\n : 'Done'\n : undefined,\n tone: task.status === 'completed' ? 'success' : 'muted',\n },\n ];\n }, [task]);\n\n if (loading) {\n return (\n <PageLayout title=\"Task Details\">\n <div className=\"flex items-center justify-center py-24\">\n <div className=\"text-center\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">Loading task details...</p>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n if (!task) {\n return (\n <PageLayout title=\"Task Not Found\">\n <EmptyState\n icon={<FileText className=\"h-12 w-12\" />}\n title=\"Task not found\"\n description={`No task found with ID \"${taskId}\".`}\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/worker/orders')}\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-hover transition-colors\"\n >\n View All Orders\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const TaskTypeIcon = getTaskTypeIcon(task.type);\n const typeLabel = typeLabels[task.type] ?? 'Task';\n const pickupLabel = task.cartName || 'Pickup';\n const dropLabel = task.customerInfo.address || task.customerInfo.name || 'Drop-off';\n const ctaLabel =\n task.status === 'pending'\n ? 'Mark Picked Up'\n : task.status === 'in_progress'\n ? 'Mark Delivered'\n : 'Delivered';\n\n return (\n <PageLayout\n title={\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/worker/orders')}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors shrink-0\"\n aria-label=\"Back to orders\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <span className=\"truncate\">\n {typeLabel}\n {task.orderId ? <span className=\"text-text-secondary\"> · #{task.orderId}</span> : null}\n </span>\n </div>\n }\n description=\"Everything you need to complete this delivery\"\n >\n <PagePurpose className=\"mb-6\">\n One delivery, start to finish. Reach the customer, follow the route, jot a note, and advance\n the job — mark it picked up, then delivered — so nothing gets missed and your completion\n stats stay strong.\n </PagePurpose>\n\n <div className=\"space-y-6\">\n {/* Delivery step tracker */}\n <GlassCard treatment=\"metric\" className=\"p-5\">\n <div className=\"mb-4 flex flex-wrap items-center gap-2\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-accent-primary/12 text-accent-primary\">\n <TaskTypeIcon className=\"h-4.5 w-4.5\" />\n </span>\n <h2 className=\"text-base font-semibold text-text-primary\">{task.title}</h2>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium uppercase',\n getPriorityStyle(task.priority)\n )}\n >\n {task.priority}\n </span>\n </div>\n <StepProgress steps={DELIVERY_STEPS} current={currentStep(task.status)} />\n </GlassCard>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main column */}\n <div className=\"lg:col-span-2 space-y-6\">\n <RouteSchematic\n pickupLabel={pickupLabel}\n dropLabel={dropLabel}\n distance={task.distance}\n />\n\n {/* Pickup + Drop cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n {/* Pickup */}\n <GlassCard className=\"p-5\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">\n Pickup\n </p>\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">{task.cartName}</p>\n {task.location && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.location}</p>\n )}\n <div className=\"mt-4\">\n <button\n type=\"button\"\n onClick={() => handleNavigate(task.location || task.cartName)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Navigate\n </button>\n </div>\n </GlassCard>\n\n {/* Drop */}\n <GlassCard className=\"p-5\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-muted\">Drop</p>\n <p className=\"mt-1 text-sm font-semibold text-text-primary\">\n {task.customerInfo.name || 'Customer'}\n </p>\n {task.customerInfo.address && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.customerInfo.address}</p>\n )}\n {task.customerInfo.phone && (\n <p className=\"mt-0.5 text-xs text-text-secondary\">{task.customerInfo.phone}</p>\n )}\n <div className=\"mt-4 flex gap-2\">\n {task.customerInfo.phone && (\n <button\n type=\"button\"\n onClick={handleCallCustomer}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Phone className=\"h-3.5 w-3.5\" />\n Call\n </button>\n )}\n {task.customerInfo.address && (\n <button\n type=\"button\"\n onClick={() => handleNavigate(task.customerInfo.address)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-2 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Navigation className=\"h-3.5 w-3.5\" />\n Navigate\n </button>\n )}\n </div>\n </GlassCard>\n </div>\n\n {/* Earnings summary */}\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-status-success-bg-subtle text-status-success-text\">\n <IndianRupee className=\"h-4.5 w-4.5\" />\n </span>\n <div>\n <p className=\"text-xs text-text-secondary\">You earn</p>\n <p className=\"text-lg font-bold text-text-primary\">\n {formatCurrency(task.earnings, { minimumFractionDigits: 0 })}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-4 text-sm\">\n <span className=\"flex items-center gap-1.5 text-text-secondary\">\n <ShoppingCart className=\"h-4 w-4\" />\n {task.cartName}\n </span>\n {task.dueTime && (\n <span className=\"flex items-center gap-1.5 text-text-secondary\">\n <Clock className=\"h-4 w-4\" />\n Due {formatTime(task.dueTime)}\n </span>\n )}\n </div>\n </div>\n </GlassCard>\n\n {/* Special Instructions */}\n {task.specialInstructions && (\n <div className=\"rounded-[var(--radius-card)] border border-status-warning-text/20 bg-status-warning-bg-subtle p-4\">\n <p className=\"text-sm text-status-warning-text flex items-start gap-2\">\n <AlertTriangle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n {task.specialInstructions}\n </p>\n </div>\n )}\n\n {/* Delivery note */}\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <StickyNote className=\"h-4 w-4 text-text-secondary\" />\n Delivery note\n </h3>\n <textarea\n value={notes}\n onChange={(e) => {\n setNotes(e.target.value);\n setNotesSaved(false);\n }}\n placeholder=\"Add a delivery note...\"\n rows={3}\n className=\"w-full resize-none rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-border-focus/40\"\n />\n <div className=\"mt-3 flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={handleSaveNotes}\n disabled={updateNotes.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-default bg-bg-surface px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {updateNotes.isPending ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <StickyNote className=\"h-3.5 w-3.5\" />\n )}\n {updateNotes.isPending ? 'Saving...' : 'Save note'}\n </button>\n {notesSaved && !updateNotes.isPending && (\n <span className=\"text-xs text-status-success-text\">Note saved</span>\n )}\n {updateNotes.isError && (\n <span className=\"text-xs text-status-error-text\">Couldn&apos;t save — retry</span>\n )}\n </div>\n </GlassCard>\n\n {/* Primary CTA */}\n <button\n type=\"button\"\n onClick={handleAdvanceStatus}\n disabled={task.status === 'completed' || updateStatus.isPending}\n className={cn(\n 'w-full inline-flex items-center justify-center gap-2 rounded-[var(--radius-card)] px-4 py-3.5 text-base font-semibold transition-colors',\n task.status === 'completed'\n ? 'bg-status-success-bg-subtle text-status-success-text cursor-default'\n : 'bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg-hover disabled:opacity-60'\n )}\n >\n {updateStatus.isPending ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <CheckCircle2 className=\"h-5 w-5\" />\n )}\n {ctaLabel}\n </button>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Delivery timeline</h3>\n <Timeline items={timeline} />\n </GlassCard>\n\n <GlassCard className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Job details</h3>\n <dl className=\"space-y-3 text-sm\">\n {task.orderId && (\n <div className=\"flex items-center gap-2\">\n <FileText className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Order ID</dt>\n <dd className=\"font-mono font-medium text-text-primary\">{task.orderId}</dd>\n </div>\n </div>\n )}\n {task.customerInfo.name && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Customer</dt>\n <dd className=\"font-medium text-text-primary\">{task.customerInfo.name}</dd>\n </div>\n </div>\n )}\n {task.distance != null && (\n <div className=\"flex items-center gap-2\">\n <Navigation className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Distance</dt>\n <dd className=\"font-medium text-text-primary\">{task.distance} km</dd>\n </div>\n </div>\n )}\n {task.assignedTime && (\n <div className=\"flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary shrink-0\" />\n <div>\n <dt className=\"text-xs text-text-secondary\">Assigned</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatDate(task.assignedTime)}\n </dd>\n </div>\n </div>\n )}\n </dl>\n </GlassCard>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerDashboard.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/worker/WorkerDashboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA+EH,eAAO,MAAM,eAAe,mCAuc3B,CAAC"}
1
+ {"version":3,"file":"WorkerDashboard.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/worker/WorkerDashboard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA+EH,eAAO,MAAM,eAAe,mCAsc3B,CAAC"}