@burdenoff/microfe-store 2026.731.1 → 2026.805.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 (35) hide show
  1. package/dist/StoreAdminRoutes.js +1 -1
  2. package/dist/StoreAdminRoutes.js.map +1 -1
  3. package/dist/StoreRoutes.js +1 -1
  4. package/dist/StoreRoutes.js.map +1 -1
  5. package/dist/components/InstallAppModal.js +211 -137
  6. package/dist/components/InstallAppModal.js.map +1 -1
  7. package/dist/generated/global-types.d.ts +6 -0
  8. package/dist/generated/global-types.js.map +1 -1
  9. package/dist/pages/AdminDashboardPage.js +1 -1
  10. package/dist/pages/AdminDashboardPage.js.map +1 -1
  11. package/dist/pages/AdminReviewModerationPage.js +1 -1
  12. package/dist/pages/AdminReviewModerationPage.js.map +1 -1
  13. package/dist/pages/AdminStoresPage.js +1 -1
  14. package/dist/pages/AdminStoresPage.js.map +1 -1
  15. package/dist/pages/AdminSubmissionsQueuePage.js +1 -1
  16. package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
  17. package/dist/pages/AppDetailPage.js +1 -1
  18. package/dist/pages/AppDetailPage.js.map +1 -1
  19. package/dist/pages/AppSettingsPage.js +1 -1
  20. package/dist/pages/AppSettingsPage.js.map +1 -1
  21. package/dist/pages/CartPage.js +1 -1
  22. package/dist/pages/CartPage.js.map +1 -1
  23. package/dist/pages/InstalledAppsPage.js +1 -1
  24. package/dist/pages/InstalledAppsPage.js.map +1 -1
  25. package/dist/pages/MarketplaceHomePage.js +1 -1
  26. package/dist/pages/MarketplaceHomePage.js.map +1 -1
  27. package/dist/pages/MyLicensesPage.js +1 -1
  28. package/dist/pages/MyLicensesPage.js.map +1 -1
  29. package/dist/pages/OrderConfirmationPage.js +1 -1
  30. package/dist/pages/OrderConfirmationPage.js.map +1 -1
  31. package/dist/pages/OrdersPage.js +1 -1
  32. package/dist/pages/OrdersPage.js.map +1 -1
  33. package/dist/pages/ProductDetailPage.js +1 -1
  34. package/dist/pages/ProductDetailPage.js.map +1 -1
  35. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"OrderConfirmationPage.js","names":[],"sources":["../../src/pages/OrderConfirmationPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport {\n CheckCircle2,\n Receipt,\n ArrowRight,\n Copy,\n Download,\n KeyRound,\n Truck,\n Package,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeCopyText, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { formatPrice as formatCurrency } from '../utils';\n\ntype LineItem = {\n productId: string;\n name: string;\n quantity: number;\n unitPrice: number;\n subtotal: number;\n itemType?: string;\n};\n\ntype OrderFulfillment = {\n status: string;\n trackingNumber: string | null;\n trackingUrl: string | null;\n carrier: string | null;\n estimatedDeliveryDate: string | null;\n};\n\ntype OrderDetails = {\n id: string;\n status: string;\n total: number;\n currency: string;\n purchasedAt?: string | null;\n lineItems: LineItem[];\n context?: Record<string, unknown> | null;\n fulfillment?: OrderFulfillment | null;\n};\n\ntype Entitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n expiresAt: string | null;\n features: string[];\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n};\n\nconst ORDER_QUERY = /* GraphQL */ `\n query OrderConfirmation($id: ID!) {\n getStoreOrder(id: $id) {\n id\n status\n total\n currency\n purchasedAt\n lineItems\n context\n fulfillment {\n status\n trackingNumber\n trackingUrl\n carrier\n estimatedDeliveryDate\n }\n }\n myEntitlements {\n id\n productId\n licenseKey\n expiresAt\n features\n product {\n id\n name\n slug\n icon\n manifest\n }\n }\n }\n`;\n\nexport const OrderConfirmationPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, authToken } = useStore();\n const { orderId } = useParams<{ orderId: string }>();\n const [order, setOrder] = useState<OrderDetails | null>(null);\n const [entitlements, setEntitlements] = useState<Entitlement[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n\n useEffect(() => {\n if (!orderId) return;\n let cancelled = false;\n const load = async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n getStoreOrder: OrderDetails | null;\n myEntitlements: Entitlement[];\n }>({\n gateway: 'global',\n query: ORDER_QUERY,\n variables: { id: orderId },\n authToken: authToken || undefined,\n });\n if (cancelled) return;\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load order');\n }\n setOrder(result.data?.getStoreOrder ?? null);\n // Filter entitlements to only those matching products in this order\n const productIds = new Set(\n (result.data?.getStoreOrder?.lineItems ?? []).map((item) => item.productId)\n );\n const orderEntitlements = (result.data?.myEntitlements ?? []).filter((e) =>\n productIds.has(e.productId)\n );\n setEntitlements(orderEntitlements);\n } catch (err) {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : 'Failed to load order');\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [orderId, authToken]);\n\n const copyKey = async (key: string) => {\n void nativeImpact('light');\n try {\n const ok = await nativeCopyText(key);\n if (ok) {\n setCopiedKey(key);\n void nativeNotify('success');\n setTimeout(() => setCopiedKey(null), 1500);\n } else {\n void nativeNotify('error');\n }\n } catch {\n void nativeNotify('error');\n }\n };\n\n const installInWorkspace = (productId: string, licenseKey: string | null) => {\n const params = new URLSearchParams();\n params.set('install', productId);\n if (licenseKey) params.set('licenseKey', licenseKey);\n // Navigate to the vibecontrols plugin install page via the top-level router.\n // Uses a plain anchor to avoid cross-MFE router imports.\n window.location.href = `/vibecontrols/agents?${params.toString()}`;\n };\n\n const formatPrice = (amount: number, currency: string) =>\n formatCurrency(amount, currency || 'USD');\n\n const lineItems = order?.lineItems ?? [];\n const hasPhysical = lineItems.some(\n (li) => String(li.itemType ?? '').toLowerCase() === 'physical'\n );\n const hasDigital = lineItems.some((li) => String(li.itemType ?? '').toLowerCase() !== 'physical');\n const formatFulfillmentStatus = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/^./, (c) => c.toUpperCase());\n\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <EmphasisPanel className=\"rounded-2xl p-6 text-center\">\n <CheckCircle2 className=\"mx-auto size-12 text-status-success-text\" />\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">Order placed successfully</h1>\n {order && (\n <p className=\"mt-1 text-sm tabular-nums text-text-secondary\">\n Total: {formatPrice(order.total, order.currency)}\n {order.purchasedAt && ` • ${new Date(order.purchasedAt).toLocaleString()}`}\n </p>\n )}\n {orderId && (\n <div className=\"mx-auto mt-4 max-w-sm rounded-lg border border-border-seam bg-surface-default px-4 py-2 text-left\">\n <p className=\"text-xs uppercase tracking-wide text-text-tertiary\">Order ID</p>\n <p className=\"mt-0.5 break-all font-mono text-xs text-text-primary\">{orderId}</p>\n </div>\n )}\n </EmphasisPanel>\n\n <p className=\"mt-3 text-center text-xs text-text-tertiary\">\n {hasPhysical\n ? \"You'll receive shipping and tracking updates as your order is prepared and dispatched.\"\n : 'This purchase is final and non-refundable. Digital app orders cannot be cancelled once completed.'}\n </p>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading ? (\n <output className=\"mt-6 block py-12 text-center text-text-secondary\">\n Loading your order…\n </output>\n ) : (\n <>\n {hasPhysical && (\n <section className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Truck className=\"size-4\" />\n Shipping\n </h2>\n <div className=\"rounded-lg border border-border-seam bg-surface-raised p-4\">\n <p className=\"text-sm text-text-secondary\">\n {order?.fulfillment?.status && order.fulfillment.status !== 'PENDING'\n ? `Status: ${formatFulfillmentStatus(order.fulfillment.status)}`\n : \"Your order is being prepared for shipment. We'll share tracking details once it ships.\"}\n </p>\n {order?.fulfillment?.estimatedDeliveryDate && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Estimated delivery:{' '}\n {new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString()}\n </p>\n )}\n {order?.fulfillment?.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-xs font-semibold text-text-primary hover:bg-surface-hover\"\n >\n <Package className=\"size-3.5\" />\n Track package\n {order.fulfillment.trackingNumber\n ? ` (${order.fulfillment.trackingNumber})`\n : ''}\n </a>\n )}\n <ul className=\"mt-4 space-y-2 border-t border-border-seam pt-3\">\n {lineItems\n .filter((li) => String(li.itemType ?? '').toLowerCase() === 'physical')\n .map((li, idx) => (\n <li\n key={li.productId ?? idx}\n className=\"flex items-center justify-between gap-3 text-sm\"\n >\n <span className=\"min-w-0 truncate text-text-primary\">\n {li.name} × {li.quantity}\n </span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatPrice(li.subtotal, order?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n </div>\n </section>\n )}\n {entitlements.length > 0 && (\n <div className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"size-4\" />\n Your licenses ({entitlements.length})\n </h2>\n <ul className=\"space-y-3\">\n {entitlements.map((ent) => (\n <li\n key={ent.id}\n className=\"rounded-lg border border-border-seam bg-surface-raised p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {ent.product?.name.slice(0, 2).toUpperCase() ?? '?'}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {ent.product?.name ?? 'Unknown product'}\n </h3>\n {ent.expiresAt && (\n <p className=\"text-xs text-text-secondary\">\n Expires: {new Date(ent.expiresAt).toLocaleDateString()}\n </p>\n )}\n {ent.licenseKey && (\n <div className=\"mt-2 flex items-center gap-2\">\n <code className=\"flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary\">\n {ent.licenseKey}\n </code>\n <button\n type=\"button\"\n onClick={() => void copyKey(ent.licenseKey!)}\n className=\"rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover\"\n title=\"Copy license key\"\n >\n <Copy className=\"size-3.5\" />\n </button>\n </div>\n )}\n {copiedKey === ent.licenseKey && (\n <p className=\"mt-1 text-xs text-status-success-text\">\n Copied to clipboard\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => installInWorkspace(ent.productId, ent.licenseKey)}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-semibold text-action-primary-text hover:opacity-90\"\n >\n <Download className=\"size-3.5\" />\n Install to workspace\n </button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n )}\n </>\n )}\n\n {!loading && (\n <NextSteps\n className=\"mt-6\"\n storageKey=\"store-order-confirmation\"\n title=\"What's next?\"\n steps={[\n ...(hasDigital || entitlements.length > 0\n ? [\n {\n id: 'install',\n label: 'Install what you bought',\n description: 'Add each purchased app to a workspace using its license.',\n icon: <Download className=\"size-4\" />,\n onClick: () => {\n const first = entitlements[0];\n if (first) installInWorkspace(first.productId, first.licenseKey);\n },\n done: entitlements.length === 0,\n },\n ]\n : []),\n ...(hasPhysical\n ? [\n {\n id: 'track',\n label: 'Track your order',\n description: 'Follow delivery status under My Orders.',\n icon: <Truck className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n ]\n : []),\n {\n id: 'orders',\n label: 'Keep this receipt',\n description: 'Find this order any time under My Orders.',\n icon: <Receipt className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n {\n id: 'shop',\n label: 'Discover more',\n description: 'Browse the marketplace for more apps and tools.',\n icon: <ArrowRight className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/marketplace`),\n },\n ]}\n />\n )}\n\n <div className=\"mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/orders`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Receipt className=\"size-4\" />\n View My Orders\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-semibold text-text-primary hover:bg-surface-hover\"\n >\n Continue Shopping\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"OrderConfirmationPage.js","names":[],"sources":["../../src/pages/OrderConfirmationPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { useNavigate, useParams } from 'react-router';\nimport {\n CheckCircle2,\n Receipt,\n ArrowRight,\n Copy,\n Download,\n KeyRound,\n Truck,\n Package,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { EmphasisPanel, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { useStore } from '../providers/StoreProvider';\nimport { nativeCopyText, nativeImpact, nativeNotify } from '../utils/nativeBridge';\nimport { formatPrice as formatCurrency } from '../utils';\n\ntype LineItem = {\n productId: string;\n name: string;\n quantity: number;\n unitPrice: number;\n subtotal: number;\n itemType?: string;\n};\n\ntype OrderFulfillment = {\n status: string;\n trackingNumber: string | null;\n trackingUrl: string | null;\n carrier: string | null;\n estimatedDeliveryDate: string | null;\n};\n\ntype OrderDetails = {\n id: string;\n status: string;\n total: number;\n currency: string;\n purchasedAt?: string | null;\n lineItems: LineItem[];\n context?: Record<string, unknown> | null;\n fulfillment?: OrderFulfillment | null;\n};\n\ntype Entitlement = {\n id: string;\n productId: string;\n licenseKey: string | null;\n expiresAt: string | null;\n features: string[];\n product: {\n id: string;\n name: string;\n slug: string;\n icon: string | null;\n manifest: Record<string, unknown> | null;\n } | null;\n};\n\nconst ORDER_QUERY = /* GraphQL */ `\n query OrderConfirmation($id: ID!) {\n getStoreOrder(id: $id) {\n id\n status\n total\n currency\n purchasedAt\n lineItems\n context\n fulfillment {\n status\n trackingNumber\n trackingUrl\n carrier\n estimatedDeliveryDate\n }\n }\n myEntitlements {\n id\n productId\n licenseKey\n expiresAt\n features\n product {\n id\n name\n slug\n icon\n manifest\n }\n }\n }\n`;\n\nexport const OrderConfirmationPage: FC = () => {\n const navigate = useNavigate();\n const { basePath, authToken } = useStore();\n const { orderId } = useParams<{ orderId: string }>();\n const [order, setOrder] = useState<OrderDetails | null>(null);\n const [entitlements, setEntitlements] = useState<Entitlement[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [copiedKey, setCopiedKey] = useState<string | null>(null);\n\n useEffect(() => {\n if (!orderId) return;\n let cancelled = false;\n const load = async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n getStoreOrder: OrderDetails | null;\n myEntitlements: Entitlement[];\n }>({\n gateway: 'global',\n query: ORDER_QUERY,\n variables: { id: orderId },\n authToken: authToken || undefined,\n });\n if (cancelled) return;\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load order');\n }\n setOrder(result.data?.getStoreOrder ?? null);\n // Filter entitlements to only those matching products in this order\n const productIds = new Set(\n (result.data?.getStoreOrder?.lineItems ?? []).map((item) => item.productId)\n );\n const orderEntitlements = (result.data?.myEntitlements ?? []).filter((e) =>\n productIds.has(e.productId)\n );\n setEntitlements(orderEntitlements);\n } catch (err) {\n if (cancelled) return;\n setError(err instanceof Error ? err.message : 'Failed to load order');\n } finally {\n if (!cancelled) setLoading(false);\n }\n };\n void load();\n return () => {\n cancelled = true;\n };\n }, [orderId, authToken]);\n\n const copyKey = async (key: string) => {\n void nativeImpact('light');\n try {\n const ok = await nativeCopyText(key);\n if (ok) {\n setCopiedKey(key);\n void nativeNotify('success');\n setTimeout(() => setCopiedKey(null), 1500);\n } else {\n void nativeNotify('error');\n }\n } catch {\n void nativeNotify('error');\n }\n };\n\n const installInWorkspace = (productId: string, licenseKey: string | null) => {\n const params = new URLSearchParams();\n params.set('install', productId);\n if (licenseKey) params.set('licenseKey', licenseKey);\n // Navigate to the vibecontrols plugin install page via the top-level router.\n // Uses a plain anchor to avoid cross-MFE router imports.\n window.location.href = `/vibecontrols/agents?${params.toString()}`;\n };\n\n const formatPrice = (amount: number, currency: string) =>\n formatCurrency(amount, currency || 'USD');\n\n const lineItems = order?.lineItems ?? [];\n const hasPhysical = lineItems.some(\n (li) => String(li.itemType ?? '').toLowerCase() === 'physical'\n );\n const hasDigital = lineItems.some((li) => String(li.itemType ?? '').toLowerCase() !== 'physical');\n const formatFulfillmentStatus = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/^./, (c) => c.toUpperCase());\n\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <EmphasisPanel className=\"rounded-2xl p-6 text-center\">\n <CheckCircle2 className=\"mx-auto size-12 text-status-success-text\" />\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">Order placed successfully</h1>\n {order && (\n <p className=\"mt-1 text-sm tabular-nums text-text-secondary\">\n Total: {formatPrice(order.total, order.currency)}\n {order.purchasedAt && ` • ${new Date(order.purchasedAt).toLocaleString()}`}\n </p>\n )}\n {orderId && (\n <div className=\"mx-auto mt-4 max-w-sm rounded-lg border border-border-seam bg-surface-default px-4 py-2 text-left\">\n <p className=\"text-xs uppercase tracking-wide text-text-tertiary\">Order ID</p>\n <p className=\"mt-0.5 break-all font-mono text-xs text-text-primary\">{orderId}</p>\n </div>\n )}\n </EmphasisPanel>\n\n <p className=\"mt-3 text-center text-xs text-text-tertiary\">\n {hasPhysical\n ? \"You'll receive shipping and tracking updates as your order is prepared and dispatched.\"\n : 'This purchase is final and non-refundable. Digital app orders cannot be cancelled once completed.'}\n </p>\n\n {error && (\n <div\n role=\"alert\"\n className=\"mt-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading ? (\n <output className=\"mt-6 block py-12 text-center text-text-secondary\">\n Loading your order…\n </output>\n ) : (\n <>\n {hasPhysical && (\n <section className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Truck className=\"size-4\" />\n Shipping\n </h2>\n <div className=\"rounded-lg border border-border-seam bg-surface-raised p-4\">\n <p className=\"text-sm text-text-secondary\">\n {order?.fulfillment?.status && order.fulfillment.status !== 'PENDING'\n ? `Status: ${formatFulfillmentStatus(order.fulfillment.status)}`\n : \"Your order is being prepared for shipment. We'll share tracking details once it ships.\"}\n </p>\n {order?.fulfillment?.estimatedDeliveryDate && (\n <p className=\"mt-1 text-xs text-text-tertiary\">\n Estimated delivery:{' '}\n {new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString()}\n </p>\n )}\n {order?.fulfillment?.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-3 inline-flex items-center gap-1.5 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-xs font-semibold text-text-primary hover:bg-surface-hover\"\n >\n <Package className=\"size-3.5\" />\n Track package\n {order.fulfillment.trackingNumber\n ? ` (${order.fulfillment.trackingNumber})`\n : ''}\n </a>\n )}\n <ul className=\"mt-4 space-y-2 border-t border-border-seam pt-3\">\n {lineItems\n .filter((li) => String(li.itemType ?? '').toLowerCase() === 'physical')\n .map((li, idx) => (\n <li\n key={li.productId ?? idx}\n className=\"flex items-center justify-between gap-3 text-sm\"\n >\n <span className=\"min-w-0 truncate text-text-primary\">\n {li.name} × {li.quantity}\n </span>\n <span className=\"tabular-nums text-text-secondary\">\n {formatPrice(li.subtotal, order?.currency ?? 'USD')}\n </span>\n </li>\n ))}\n </ul>\n </div>\n </section>\n )}\n {entitlements.length > 0 && (\n <div className=\"mt-6\">\n <h2 className=\"mb-3 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <KeyRound className=\"size-4\" />\n Your licenses ({entitlements.length})\n </h2>\n <ul className=\"space-y-3\">\n {entitlements.map((ent) => (\n <li\n key={ent.id}\n className=\"rounded-lg border border-border-seam bg-surface-raised p-4 transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-3\">\n {ent.product?.icon ? (\n <img\n src={ent.product.icon}\n alt=\"\"\n className=\"size-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {ent.product?.name.slice(0, 2).toUpperCase() ?? '?'}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {ent.product?.name ?? 'Unknown product'}\n </h3>\n {ent.expiresAt && (\n <p className=\"text-xs text-text-secondary\">\n Expires: {new Date(ent.expiresAt).toLocaleDateString()}\n </p>\n )}\n {ent.licenseKey && (\n <div className=\"mt-2 flex items-center gap-2\">\n <code className=\"flex-1 truncate rounded bg-surface-muted px-2 py-1 font-mono text-[11px] text-text-primary\">\n {ent.licenseKey}\n </code>\n <button\n type=\"button\"\n onClick={() => void copyKey(ent.licenseKey!)}\n className=\"rounded-md border border-border-default bg-surface-default p-1 text-text-primary hover:bg-surface-hover\"\n title=\"Copy license key\"\n >\n <Copy className=\"size-3.5\" />\n </button>\n </div>\n )}\n {copiedKey === ent.licenseKey && (\n <p className=\"mt-1 text-xs text-status-success-text\">\n Copied to clipboard\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => installInWorkspace(ent.productId, ent.licenseKey)}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1.5 text-xs font-semibold text-action-primary-text hover:opacity-90\"\n >\n <Download className=\"size-3.5\" />\n Install to workspace\n </button>\n </div>\n </li>\n ))}\n </ul>\n </div>\n )}\n </>\n )}\n\n {!loading && (\n <NextSteps\n className=\"mt-6\"\n storageKey=\"store-order-confirmation\"\n title=\"What's next?\"\n steps={[\n ...(hasDigital || entitlements.length > 0\n ? [\n {\n id: 'install',\n label: 'Install what you bought',\n description: 'Add each purchased app to a workspace using its license.',\n icon: <Download className=\"size-4\" />,\n onClick: () => {\n const first = entitlements[0];\n if (first) installInWorkspace(first.productId, first.licenseKey);\n },\n done: entitlements.length === 0,\n },\n ]\n : []),\n ...(hasPhysical\n ? [\n {\n id: 'track',\n label: 'Track your order',\n description: 'Follow delivery status under My Orders.',\n icon: <Truck className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n ]\n : []),\n {\n id: 'orders',\n label: 'Keep this receipt',\n description: 'Find this order any time under My Orders.',\n icon: <Receipt className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/orders`),\n },\n {\n id: 'shop',\n label: 'Discover more',\n description: 'Browse the marketplace for more apps and tools.',\n icon: <ArrowRight className=\"size-4\" />,\n onClick: () => navigate(`${basePath}/marketplace`),\n },\n ]}\n />\n )}\n\n <div className=\"mt-6 flex flex-col gap-2 sm:flex-row sm:justify-center\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/orders`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Receipt className=\"size-4\" />\n View My Orders\n </button>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/marketplace`)}\n className=\"cursor-pointer inline-flex items-center justify-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-semibold text-text-primary hover:bg-surface-hover\"\n >\n Continue Shopping\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n </main>\n );\n};\n"],"mappings":"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"}
@@ -8,7 +8,7 @@ import { useCallback as o, useMemo as s, useState as c } from "react";
8
8
  import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
9
  import { ArrowLeft as u, CheckCircle as d, ChevronRight as f, Clock as p, Download as m, Loader2 as h, Package as g, RefreshCw as _, ShoppingBag as v, XCircle as y } from "lucide-react";
10
10
  import { jsx as b, jsxs as x } from "react/jsx-runtime";
11
- import { useNavigate as ee } from "react-router-dom";
11
+ import { useNavigate as ee } from "react-router";
12
12
  import { GlassCard as S, IllustratedEmptyState as C, PagePurpose as w } from "@burdenoff/fe-libs/ui";
13
13
  //#region src/pages/OrdersPage.tsx
14
14
  var T = [
@@ -1 +1 @@
1
- {"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useCallback } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\n ChevronRight,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { useOrders } from '../hooks/useOrders';\nimport { formatPrice } from '../utils';\nimport { InstallAppModal, type AppInfo } from '../components/InstallAppModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrderStatusUpdatedSubscription } from '../generated/global-operations';\n\n// Local types for UI\ninterface OrderItem {\n id: string;\n productId: string;\n productName: string;\n productIcon?: string;\n productType: string;\n version?: string;\n quantity: number;\n unitPrice: number;\n totalPrice: number;\n installed?: boolean;\n}\n\ninterface UIOrder {\n id: string;\n orderNumber: string;\n items: OrderItem[];\n subtotal: number;\n tax: number;\n total: number;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n fulfillment?: {\n status?: string | null;\n trackingNumber?: string | null;\n trackingUrl?: string | null;\n carrier?: string | null;\n estimatedDeliveryDate?: string | null;\n } | null;\n}\n\ntype OrdersTab = 'completed' | 'pending';\n\nconst SUCCESSFUL_ORDER_STATUSES: UIOrder['status'][] = ['COMPLETED', 'FULFILLED', 'PROCESSED'];\n\n/**\n * Get status badge config\n */\nconst getStatusDisplay = (status: UIOrder['status']) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n classes:\n 'bg-status-success-bg-subtle/15 text-status-success-text border-status-success-text/20',\n label: 'Completed',\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Processing',\n };\n case 'PENDING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Pending',\n };\n case 'FAILED':\n return {\n icon: XCircle,\n classes: 'bg-status-error-bg-subtle/15 text-status-error-text border-status-error-text/20',\n label: 'Failed',\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Refunded',\n };\n case 'CANCELLED':\n return {\n icon: RefreshCw,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: 'Cancelled',\n };\n default:\n return {\n icon: Clock,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: status,\n };\n }\n};\n\n/**\n * Status badge component\n */\nconst StatusBadge: FC<{ status: UIOrder['status'] }> = ({ status }) => {\n const display = getStatusDisplay(status);\n const Icon = display.icon;\n return (\n <span\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-semibold ${display.classes}`}\n >\n <Icon className=\"size-3.5\" />\n {display.label}\n </span>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a table of all orders with expandable rows showing line items\n * and Install App buttons for digital products.\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const { orders: backendOrders, loading, error, refetchOrders, hasMore, loadMore } = useOrders();\n const { installations, refetch: refetchInstallations } = useInstallations();\n\n useOrderStatusUpdatedSubscription({\n onData: () => {\n void refetchOrders();\n },\n onError: (err) => {\n console.warn('OrderStatusUpdated subscription error:', err.message);\n },\n });\n\n // Expanded rows tracking\n const [expandedOrders, setExpandedOrders] = useState<Set<string>>(new Set());\n const [activeTab, setActiveTab] = useState<OrdersTab>('completed');\n\n // Modal state\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);\n const [selectedApp, setSelectedApp] = useState<AppInfo | null>(null);\n const [selectedItemId, setSelectedItemId] = useState<string | null>(null);\n\n const installedProductIds = useMemo(\n () =>\n new Set(\n installations.flatMap((installation) =>\n installation.status === 'ACTIVE' ? [installation.productId] : []\n )\n ),\n [installations]\n );\n\n // Map backend orders to UI format\n const orders: UIOrder[] = backendOrders.map((order) => {\n const lineItems = order.lineItems || [];\n const subtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n const tax = order.total - subtotal;\n\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`,\n productId: item.productId,\n productName: item.name,\n productIcon: item.productIcon,\n productType:\n item.productType ||\n (typeof item.itemType === 'string' ? item.itemType.toUpperCase() : 'APP'),\n version: item.version,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.subtotal,\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`,\n items,\n subtotal,\n tax,\n total: order.total,\n status: order.status as UIOrder['status'],\n createdAt: order.createdAt,\n fulfillment: order.fulfillment ?? null,\n };\n });\n\n const completedOrders = useMemo(\n () => orders.filter((order) => SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const pendingOrders = useMemo(\n () => orders.filter((order) => !SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const visibleOrders = activeTab === 'completed' ? completedOrders : pendingOrders;\n\n const toggleExpand = useCallback((orderId: string) => {\n setExpandedOrders((prev) => {\n const next = new Set(prev);\n if (next.has(orderId)) {\n next.delete(orderId);\n } else {\n next.add(orderId);\n }\n return next;\n });\n }, []);\n\n const handleOpenInstallModal = (orderId: string, item: OrderItem) => {\n setSelectedOrderId(orderId);\n setSelectedItemId(item.id);\n setSelectedApp({\n id: item.productId,\n name: item.productName,\n icon: item.productIcon,\n type: item.productType,\n version: item.version,\n });\n setIsModalOpen(true);\n };\n\n const handleCloseModal = () => {\n setIsModalOpen(false);\n setSelectedOrderId(null);\n setSelectedApp(null);\n setSelectedItemId(null);\n };\n\n const handleInstallSuccess = (_workspaceId: string, _workspaceName: string) => {\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (_error: Error) => {\n // Error is displayed in modal\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n // ──── Shared table cell classes ────\n const cellCls = 'px-4 py-3 text-sm';\n const headerCls =\n 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-text-muted';\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"flex-shrink-0 border-b border-border-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('pages.orders.title', { defaultValue: 'My Orders' })}\n </h1>\n {orders.length > 0 && (\n <span className=\"rounded-full bg-action-primary-bg/10 px-2.5 py-0.5 text-sm font-medium text-action-primary-bg\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </header>\n\n {/* Scrollable content area */}\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-6xl p-6\">\n <PagePurpose className=\"mb-6\">\n A record of everything you've purchased from the marketplace. Track each order's status,\n review what you paid, and — once an order is complete — install the apps you bought into\n a workspace right from here. Come back to re-install on a new workspace or check a past\n receipt.\n </PagePurpose>\n\n {loading && orders.length === 0 ? (\n // ── Loading state ──\n <div className=\"flex flex-col items-center justify-center py-24\">\n <Loader2 className=\"size-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">Loading your orders…</p>\n </div>\n ) : error ? (\n // ── Error state ──\n <div className=\"flex flex-col items-center justify-center py-24 text-center\">\n <div className=\"mb-6 rounded-full bg-status-error-bg-subtle/10 p-6\">\n <XCircle className=\"size-16 text-status-error-text\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.failedToLoad', { defaultValue: 'Failed to load orders' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {error.message || 'An error occurred while loading your orders. Please try again.'}\n </p>\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n Browse Marketplace\n </button>\n </div>\n ) : orders.length === 0 ? (\n // ── Empty state ──\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n description=\"You have not placed any orders yet. Browse the marketplace to discover apps and products!\"\n action={\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n Browse Marketplace\n </button>\n }\n />\n ) : (\n // ── Table ──\n <div className=\"space-y-4\">\n <div className=\"inline-flex rounded-xl border border-border-seam bg-bg-surface p-1\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('completed')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'completed'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n Completed\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {completedOrders.length}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('pending')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'pending'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n Pending\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {pendingOrders.length}\n </span>\n </button>\n </div>\n\n {visibleOrders.length === 0 ? (\n <GlassCard className=\"p-10 text-center\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <h2 className=\"mb-2 text-lg font-semibold text-text-primary\">\n {activeTab === 'completed' ? 'No completed orders yet' : 'No pending orders'}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {activeTab === 'completed'\n ? 'Completed purchases will appear here once payment is successful.'\n : 'Orders that are pending, processing, failed, refunded, or cancelled will appear here.'}\n </p>\n </GlassCard>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n {/* Table Header */}\n <div className=\"hidden border-b border-border-seam bg-bg-sunken/50 md:grid md:grid-cols-[40px_1fr_180px_140px_120px_160px]\">\n <div className={headerCls} />\n <div className={headerCls}>Order</div>\n <div className={headerCls}>Date</div>\n <div className={headerCls}>Items</div>\n <div className={`${headerCls} text-right`}>Total</div>\n <div className={`${headerCls} text-center`}>Status</div>\n </div>\n\n {/* Table Body */}\n {visibleOrders.map((order) => {\n const isExpanded = expandedOrders.has(order.id);\n const isSuccessful =\n order.status === 'COMPLETED' ||\n order.status === 'FULFILLED' ||\n order.status === 'PROCESSED';\n\n return (\n <div key={order.id} className=\"border-b border-border-seam last:border-b-0\">\n {/* Order Row */}\n <button\n type=\"button\"\n onClick={() => toggleExpand(order.id)}\n className=\"grid w-full cursor-pointer grid-cols-[40px_1fr_180px_140px_120px_160px] items-center transition-colors hover:bg-bg-sunken/50 md:grid\"\n >\n {/* Expand/collapse chevron */}\n <div className={`${cellCls} flex justify-center`}>\n <ChevronRight\n className={`size-4 text-text-muted transition-transform duration-200 ${\n isExpanded ? 'rotate-90' : ''\n }`}\n />\n </div>\n\n {/* Order number */}\n <div className={`${cellCls} font-medium text-text-primary`}>\n {order.orderNumber}\n </div>\n\n {/* Date */}\n <div className={`${cellCls} text-text-muted`}>\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </div>\n\n {/* Items summary */}\n <div className={`${cellCls} text-text-muted`}>\n {order.items.length} {order.items.length === 1 ? 'item' : 'items'}\n <span className=\"block text-xs text-text-muted/70\">\n {order.items.map((i) => i.productName).join(', ')}\n </span>\n </div>\n\n {/* Total */}\n <div\n className={`${cellCls} text-right font-semibold tabular-nums text-text-primary`}\n >\n {formatPrice(order.total)}\n </div>\n\n {/* Status */}\n <div className={`${cellCls} flex justify-center`}>\n <StatusBadge status={order.status} />\n </div>\n </button>\n\n {/* Expanded: Line items */}\n {isExpanded && (\n <div className=\"border-t border-border-seam bg-bg-sunken/30\">\n {order.fulfillment && (\n <div className=\"mx-4 mt-4 rounded-xl border border-border-seam bg-bg-surface px-4 py-4 md:mx-12\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">\n Order tracking\n </div>\n <div className=\"mt-1 text-xs text-text-muted\">\n {order.fulfillment.status || 'PENDING'}\n {order.fulfillment.carrier\n ? ` · ${order.fulfillment.carrier}`\n : ''}\n {order.fulfillment.estimatedDeliveryDate\n ? ` · Est. delivery ${new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString()}`\n : ''}\n </div>\n </div>\n\n <div className=\"flex flex-col items-start gap-2 md:items-end\">\n {order.fulfillment.trackingNumber && (\n <div className=\"text-xs text-text-muted\">\n Tracking #:{' '}\n <span className=\"font-medium text-text-primary\">\n {order.fulfillment.trackingNumber}\n </span>\n </div>\n )}\n {order.fulfillment.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Track package\n <ChevronRight className=\"size-3.5\" />\n </a>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-2 sm:grid-cols-4\">\n {[\n { key: 'PROCESSING', label: 'Processing' },\n { key: 'SHIPPED', label: 'Shipped' },\n { key: 'OUT_FOR_DELIVERY', label: 'Out for delivery' },\n { key: 'DELIVERED', label: 'Delivered' },\n ].map((step) => {\n const current = order.fulfillment?.status || 'PENDING';\n const reached =\n [\n 'PROCESSING',\n 'READY_TO_SHIP',\n 'SHIPPED',\n 'IN_TRANSIT',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(current) >=\n [\n 'PROCESSING',\n 'SHIPPED',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(step.key);\n return (\n <div\n key={step.key}\n className={`rounded-lg border px-3 py-2 text-xs font-medium ${\n reached\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-seam bg-bg-sunken/40 text-text-muted'\n }`}\n >\n {step.label}\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Column headers for items */}\n <div className=\"hidden grid-cols-[1fr_100px_120px_160px] gap-4 px-12 py-2 md:grid\">\n <span className=\"text-xs font-medium text-text-muted\">Product</span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n Qty\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n Price\n </span>\n <span className=\"text-xs font-medium text-text-muted text-center\">\n Action\n </span>\n </div>\n\n {order.items.map((item) => {\n const isInstallable =\n item.productType !== 'PHYSICAL' &&\n item.productType !== 'MERCHANDISE';\n\n return (\n <div\n key={item.id}\n className=\"grid grid-cols-[auto_1fr_auto] items-center gap-3 px-4 py-3 md:px-12 md:grid-cols-[1fr_100px_120px_160px]\"\n >\n {/* Product info */}\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.productIcon ? (\n <img\n src={item.productIcon}\n alt={item.productName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-sm font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {item.productName}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.productType}\n </span>\n </div>\n </div>\n\n {/* Quantity */}\n <div className=\"hidden text-right text-sm text-text-muted md:block\">\n {item.quantity}\n </div>\n\n {/* Price */}\n <div className=\"hidden text-right text-sm font-semibold tabular-nums text-text-primary md:block\">\n {formatPrice(item.totalPrice)}\n </div>\n\n {/* Mobile price + qty */}\n <div className=\"text-right text-sm text-text-muted md:hidden\">\n <span className=\"tabular-nums\">{item.quantity} &times; </span>\n <span className=\"font-semibold text-text-primary tabular-nums\">\n {formatPrice(item.unitPrice)}\n </span>\n </div>\n\n {/* Install button */}\n <div className=\"flex justify-end md:justify-center\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <CheckCircle className=\"size-3.5\" />\n Installed\n </span>\n ) : isSuccessful && isInstallable ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleOpenInstallModal(order.id, item);\n }}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-all hover:opacity-90 active:scale-95\"\n >\n <Download className=\"size-3.5\" />\n Install App\n </button>\n ) : null}\n </div>\n </div>\n );\n })}\n\n {/* Order summary footer */}\n <div className=\"border-t border-border-seam/50 px-4 py-2 md:px-12\">\n <div className=\"flex justify-end gap-6 text-xs text-text-muted\">\n <span>\n Subtotal:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.subtotal)}\n </span>\n </span>\n <span>\n Tax:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.tax)}\n </span>\n </span>\n <span className=\"font-semibold\">\n Total:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.total)}\n </span>\n </span>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {/* Load More */}\n {hasMore && (\n <div className=\"flex justify-center px-4 py-5\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Install App Modal */}\n {selectedApp && (\n <InstallAppModal\n isOpen={isModalOpen}\n onClose={handleCloseModal}\n app={selectedApp}\n orderId={selectedOrderId}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"OrdersPage.js","names":[],"sources":["../../src/pages/OrdersPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useCallback } from 'react';\nimport { useNavigate } from 'react-router';\nimport {\n Package,\n ArrowLeft,\n ShoppingBag,\n Download,\n CheckCircle,\n Clock,\n XCircle,\n RefreshCw,\n Loader2,\n ChevronRight,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallations } from '../hooks/useInstallations';\nimport { useOrders } from '../hooks/useOrders';\nimport { formatPrice } from '../utils';\nimport { InstallAppModal, type AppInfo } from '../components/InstallAppModal';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useOrderStatusUpdatedSubscription } from '../generated/global-operations';\n\n// Local types for UI\ninterface OrderItem {\n id: string;\n productId: string;\n productName: string;\n productIcon?: string;\n productType: string;\n version?: string;\n quantity: number;\n unitPrice: number;\n totalPrice: number;\n installed?: boolean;\n}\n\ninterface UIOrder {\n id: string;\n orderNumber: string;\n items: OrderItem[];\n subtotal: number;\n tax: number;\n total: number;\n status:\n | 'PENDING'\n | 'PROCESSING'\n | 'COMPLETED'\n | 'FAILED'\n | 'CANCELLED'\n | 'FULFILLED'\n | 'PROCESSED'\n | 'REFUNDED';\n createdAt: string;\n fulfillment?: {\n status?: string | null;\n trackingNumber?: string | null;\n trackingUrl?: string | null;\n carrier?: string | null;\n estimatedDeliveryDate?: string | null;\n } | null;\n}\n\ntype OrdersTab = 'completed' | 'pending';\n\nconst SUCCESSFUL_ORDER_STATUSES: UIOrder['status'][] = ['COMPLETED', 'FULFILLED', 'PROCESSED'];\n\n/**\n * Get status badge config\n */\nconst getStatusDisplay = (status: UIOrder['status']) => {\n switch (status) {\n case 'COMPLETED':\n case 'FULFILLED':\n case 'PROCESSED':\n return {\n icon: CheckCircle,\n classes:\n 'bg-status-success-bg-subtle/15 text-status-success-text border-status-success-text/20',\n label: 'Completed',\n };\n case 'PROCESSING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Processing',\n };\n case 'PENDING':\n return {\n icon: Clock,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Pending',\n };\n case 'FAILED':\n return {\n icon: XCircle,\n classes: 'bg-status-error-bg-subtle/15 text-status-error-text border-status-error-text/20',\n label: 'Failed',\n };\n case 'REFUNDED':\n return {\n icon: RefreshCw,\n classes:\n 'bg-status-warning-bg-subtle/15 text-status-warning-text border-status-warning-text/20',\n label: 'Refunded',\n };\n case 'CANCELLED':\n return {\n icon: RefreshCw,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: 'Cancelled',\n };\n default:\n return {\n icon: Clock,\n classes: 'bg-bg-sunken text-text-muted border-border-default',\n label: status,\n };\n }\n};\n\n/**\n * Status badge component\n */\nconst StatusBadge: FC<{ status: UIOrder['status'] }> = ({ status }) => {\n const display = getStatusDisplay(status);\n const Icon = display.icon;\n return (\n <span\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-semibold ${display.classes}`}\n >\n <Icon className=\"size-3.5\" />\n {display.label}\n </span>\n );\n};\n\n/**\n * OrdersPage component\n *\n * Displays a table of all orders with expandable rows showing line items\n * and Install App buttons for digital products.\n */\nexport const OrdersPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { t } = useI18n();\n const { orders: backendOrders, loading, error, refetchOrders, hasMore, loadMore } = useOrders();\n const { installations, refetch: refetchInstallations } = useInstallations();\n\n useOrderStatusUpdatedSubscription({\n onData: () => {\n void refetchOrders();\n },\n onError: (err) => {\n console.warn('OrderStatusUpdated subscription error:', err.message);\n },\n });\n\n // Expanded rows tracking\n const [expandedOrders, setExpandedOrders] = useState<Set<string>>(new Set());\n const [activeTab, setActiveTab] = useState<OrdersTab>('completed');\n\n // Modal state\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [selectedOrderId, setSelectedOrderId] = useState<string | null>(null);\n const [selectedApp, setSelectedApp] = useState<AppInfo | null>(null);\n const [selectedItemId, setSelectedItemId] = useState<string | null>(null);\n\n const installedProductIds = useMemo(\n () =>\n new Set(\n installations.flatMap((installation) =>\n installation.status === 'ACTIVE' ? [installation.productId] : []\n )\n ),\n [installations]\n );\n\n // Map backend orders to UI format\n const orders: UIOrder[] = backendOrders.map((order) => {\n const lineItems = order.lineItems || [];\n const subtotal = lineItems.reduce((sum, item) => sum + item.subtotal, 0);\n const tax = order.total - subtotal;\n\n const items: OrderItem[] = lineItems.map((item, index) => ({\n id: `${order.id}-${index}`,\n productId: item.productId,\n productName: item.name,\n productIcon: item.productIcon,\n productType:\n item.productType ||\n (typeof item.itemType === 'string' ? item.itemType.toUpperCase() : 'APP'),\n version: item.version,\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n totalPrice: item.subtotal,\n installed: installedProductIds.has(item.productId),\n }));\n\n return {\n id: order.id,\n orderNumber: `#${order.id.slice(-8).toUpperCase()}`,\n items,\n subtotal,\n tax,\n total: order.total,\n status: order.status as UIOrder['status'],\n createdAt: order.createdAt,\n fulfillment: order.fulfillment ?? null,\n };\n });\n\n const completedOrders = useMemo(\n () => orders.filter((order) => SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const pendingOrders = useMemo(\n () => orders.filter((order) => !SUCCESSFUL_ORDER_STATUSES.includes(order.status)),\n [orders]\n );\n\n const visibleOrders = activeTab === 'completed' ? completedOrders : pendingOrders;\n\n const toggleExpand = useCallback((orderId: string) => {\n setExpandedOrders((prev) => {\n const next = new Set(prev);\n if (next.has(orderId)) {\n next.delete(orderId);\n } else {\n next.add(orderId);\n }\n return next;\n });\n }, []);\n\n const handleOpenInstallModal = (orderId: string, item: OrderItem) => {\n setSelectedOrderId(orderId);\n setSelectedItemId(item.id);\n setSelectedApp({\n id: item.productId,\n name: item.productName,\n icon: item.productIcon,\n type: item.productType,\n version: item.version,\n });\n setIsModalOpen(true);\n };\n\n const handleCloseModal = () => {\n setIsModalOpen(false);\n setSelectedOrderId(null);\n setSelectedApp(null);\n setSelectedItemId(null);\n };\n\n const handleInstallSuccess = (_workspaceId: string, _workspaceName: string) => {\n handleCloseModal();\n void refetchInstallations();\n };\n\n const handleInstallError = (_error: Error) => {\n // Error is displayed in modal\n };\n\n const handleBrowseMarketplace = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n // ──── Shared table cell classes ────\n const cellCls = 'px-4 py-3 text-sm';\n const headerCls =\n 'px-4 py-3 text-left text-xs font-semibold uppercase tracking-wider text-text-muted';\n\n return (\n <div className=\"flex h-full flex-col\">\n {/* Header */}\n <header className=\"flex-shrink-0 border-b border-border-seam bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-4\">\n <button\n type=\"button\"\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <Package className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('pages.orders.title', { defaultValue: 'My Orders' })}\n </h1>\n {orders.length > 0 && (\n <span className=\"rounded-full bg-action-primary-bg/10 px-2.5 py-0.5 text-sm font-medium text-action-primary-bg\">\n {orders.length}\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n </header>\n\n {/* Scrollable content area */}\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"mx-auto max-w-6xl p-6\">\n <PagePurpose className=\"mb-6\">\n A record of everything you've purchased from the marketplace. Track each order's status,\n review what you paid, and — once an order is complete — install the apps you bought into\n a workspace right from here. Come back to re-install on a new workspace or check a past\n receipt.\n </PagePurpose>\n\n {loading && orders.length === 0 ? (\n // ── Loading state ──\n <div className=\"flex flex-col items-center justify-center py-24\">\n <Loader2 className=\"size-12 animate-spin text-text-muted mb-4\" aria-hidden=\"true\" />\n <p className=\"text-text-muted\">Loading your orders…</p>\n </div>\n ) : error ? (\n // ── Error state ──\n <div className=\"flex flex-col items-center justify-center py-24 text-center\">\n <div className=\"mb-6 rounded-full bg-status-error-bg-subtle/10 p-6\">\n <XCircle className=\"size-16 text-status-error-text\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('pages.orders.failedToLoad', { defaultValue: 'Failed to load orders' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {error.message || 'An error occurred while loading your orders. Please try again.'}\n </p>\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n Browse Marketplace\n </button>\n </div>\n ) : orders.length === 0 ? (\n // ── Empty state ──\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('pages.orders.emptyTitle', { defaultValue: 'No orders yet' })}\n description=\"You have not placed any orders yet. Browse the marketplace to discover apps and products!\"\n action={\n <button\n type=\"button\"\n onClick={handleBrowseMarketplace}\n className=\"cursor-pointer flex items-center gap-2 rounded-lg bg-action-primary-bg px-6 py-3 font-semibold text-action-primary-text transition-opacity hover:opacity-90\"\n >\n <ShoppingBag className=\"size-5\" />\n Browse Marketplace\n </button>\n }\n />\n ) : (\n // ── Table ──\n <div className=\"space-y-4\">\n <div className=\"inline-flex rounded-xl border border-border-seam bg-bg-surface p-1\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('completed')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'completed'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n Completed\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {completedOrders.length}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('pending')}\n className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === 'pending'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-muted hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n Pending\n <span className=\"ml-2 rounded-full bg-black/10 px-2 py-0.5 text-xs\">\n {pendingOrders.length}\n </span>\n </button>\n </div>\n\n {visibleOrders.length === 0 ? (\n <GlassCard className=\"p-10 text-center\">\n <div className=\"mx-auto mb-4 flex size-14 items-center justify-center rounded-full bg-bg-sunken text-text-muted\">\n <Package className=\"size-6\" />\n </div>\n <h2 className=\"mb-2 text-lg font-semibold text-text-primary\">\n {activeTab === 'completed' ? 'No completed orders yet' : 'No pending orders'}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {activeTab === 'completed'\n ? 'Completed purchases will appear here once payment is successful.'\n : 'Orders that are pending, processing, failed, refunded, or cancelled will appear here.'}\n </p>\n </GlassCard>\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n {/* Table Header */}\n <div className=\"hidden border-b border-border-seam bg-bg-sunken/50 md:grid md:grid-cols-[40px_1fr_180px_140px_120px_160px]\">\n <div className={headerCls} />\n <div className={headerCls}>Order</div>\n <div className={headerCls}>Date</div>\n <div className={headerCls}>Items</div>\n <div className={`${headerCls} text-right`}>Total</div>\n <div className={`${headerCls} text-center`}>Status</div>\n </div>\n\n {/* Table Body */}\n {visibleOrders.map((order) => {\n const isExpanded = expandedOrders.has(order.id);\n const isSuccessful =\n order.status === 'COMPLETED' ||\n order.status === 'FULFILLED' ||\n order.status === 'PROCESSED';\n\n return (\n <div key={order.id} className=\"border-b border-border-seam last:border-b-0\">\n {/* Order Row */}\n <button\n type=\"button\"\n onClick={() => toggleExpand(order.id)}\n className=\"grid w-full cursor-pointer grid-cols-[40px_1fr_180px_140px_120px_160px] items-center transition-colors hover:bg-bg-sunken/50 md:grid\"\n >\n {/* Expand/collapse chevron */}\n <div className={`${cellCls} flex justify-center`}>\n <ChevronRight\n className={`size-4 text-text-muted transition-transform duration-200 ${\n isExpanded ? 'rotate-90' : ''\n }`}\n />\n </div>\n\n {/* Order number */}\n <div className={`${cellCls} font-medium text-text-primary`}>\n {order.orderNumber}\n </div>\n\n {/* Date */}\n <div className={`${cellCls} text-text-muted`}>\n {new Date(order.createdAt).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n })}\n </div>\n\n {/* Items summary */}\n <div className={`${cellCls} text-text-muted`}>\n {order.items.length} {order.items.length === 1 ? 'item' : 'items'}\n <span className=\"block text-xs text-text-muted/70\">\n {order.items.map((i) => i.productName).join(', ')}\n </span>\n </div>\n\n {/* Total */}\n <div\n className={`${cellCls} text-right font-semibold tabular-nums text-text-primary`}\n >\n {formatPrice(order.total)}\n </div>\n\n {/* Status */}\n <div className={`${cellCls} flex justify-center`}>\n <StatusBadge status={order.status} />\n </div>\n </button>\n\n {/* Expanded: Line items */}\n {isExpanded && (\n <div className=\"border-t border-border-seam bg-bg-sunken/30\">\n {order.fulfillment && (\n <div className=\"mx-4 mt-4 rounded-xl border border-border-seam bg-bg-surface px-4 py-4 md:mx-12\">\n <div className=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n <div>\n <div className=\"text-sm font-semibold text-text-primary\">\n Order tracking\n </div>\n <div className=\"mt-1 text-xs text-text-muted\">\n {order.fulfillment.status || 'PENDING'}\n {order.fulfillment.carrier\n ? ` · ${order.fulfillment.carrier}`\n : ''}\n {order.fulfillment.estimatedDeliveryDate\n ? ` · Est. delivery ${new Date(order.fulfillment.estimatedDeliveryDate).toLocaleDateString()}`\n : ''}\n </div>\n </div>\n\n <div className=\"flex flex-col items-start gap-2 md:items-end\">\n {order.fulfillment.trackingNumber && (\n <div className=\"text-xs text-text-muted\">\n Tracking #:{' '}\n <span className=\"font-medium text-text-primary\">\n {order.fulfillment.trackingNumber}\n </span>\n </div>\n )}\n {order.fulfillment.trackingUrl && (\n <a\n href={order.fulfillment.trackingUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Track package\n <ChevronRight className=\"size-3.5\" />\n </a>\n )}\n </div>\n </div>\n\n <div className=\"mt-4 grid gap-2 sm:grid-cols-4\">\n {[\n { key: 'PROCESSING', label: 'Processing' },\n { key: 'SHIPPED', label: 'Shipped' },\n { key: 'OUT_FOR_DELIVERY', label: 'Out for delivery' },\n { key: 'DELIVERED', label: 'Delivered' },\n ].map((step) => {\n const current = order.fulfillment?.status || 'PENDING';\n const reached =\n [\n 'PROCESSING',\n 'READY_TO_SHIP',\n 'SHIPPED',\n 'IN_TRANSIT',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(current) >=\n [\n 'PROCESSING',\n 'SHIPPED',\n 'OUT_FOR_DELIVERY',\n 'DELIVERED',\n ].indexOf(step.key);\n return (\n <div\n key={step.key}\n className={`rounded-lg border px-3 py-2 text-xs font-medium ${\n reached\n ? 'border-status-success-border bg-status-success-bg-subtle text-status-success-text'\n : 'border-border-seam bg-bg-sunken/40 text-text-muted'\n }`}\n >\n {step.label}\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* Column headers for items */}\n <div className=\"hidden grid-cols-[1fr_100px_120px_160px] gap-4 px-12 py-2 md:grid\">\n <span className=\"text-xs font-medium text-text-muted\">Product</span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n Qty\n </span>\n <span className=\"text-xs font-medium text-text-muted text-right\">\n Price\n </span>\n <span className=\"text-xs font-medium text-text-muted text-center\">\n Action\n </span>\n </div>\n\n {order.items.map((item) => {\n const isInstallable =\n item.productType !== 'PHYSICAL' &&\n item.productType !== 'MERCHANDISE';\n\n return (\n <div\n key={item.id}\n className=\"grid grid-cols-[auto_1fr_auto] items-center gap-3 px-4 py-3 md:px-12 md:grid-cols-[1fr_100px_120px_160px]\"\n >\n {/* Product info */}\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.productIcon ? (\n <img\n src={item.productIcon}\n alt={item.productName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-sm font-bold text-text-muted\">\n {item.productName.charAt(0)}\n </div>\n )}\n </div>\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {item.productName}\n </p>\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.productType}\n </span>\n </div>\n </div>\n\n {/* Quantity */}\n <div className=\"hidden text-right text-sm text-text-muted md:block\">\n {item.quantity}\n </div>\n\n {/* Price */}\n <div className=\"hidden text-right text-sm font-semibold tabular-nums text-text-primary md:block\">\n {formatPrice(item.totalPrice)}\n </div>\n\n {/* Mobile price + qty */}\n <div className=\"text-right text-sm text-text-muted md:hidden\">\n <span className=\"tabular-nums\">{item.quantity} &times; </span>\n <span className=\"font-semibold text-text-primary tabular-nums\">\n {formatPrice(item.unitPrice)}\n </span>\n </div>\n\n {/* Install button */}\n <div className=\"flex justify-end md:justify-center\">\n {item.installed ? (\n <span className=\"inline-flex items-center gap-1 rounded-lg px-3 py-1.5 text-xs font-medium text-status-success-text\">\n <CheckCircle className=\"size-3.5\" />\n Installed\n </span>\n ) : isSuccessful && isInstallable ? (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleOpenInstallModal(order.id, item);\n }}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text transition-all hover:opacity-90 active:scale-95\"\n >\n <Download className=\"size-3.5\" />\n Install App\n </button>\n ) : null}\n </div>\n </div>\n );\n })}\n\n {/* Order summary footer */}\n <div className=\"border-t border-border-seam/50 px-4 py-2 md:px-12\">\n <div className=\"flex justify-end gap-6 text-xs text-text-muted\">\n <span>\n Subtotal:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.subtotal)}\n </span>\n </span>\n <span>\n Tax:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.tax)}\n </span>\n </span>\n <span className=\"font-semibold\">\n Total:{' '}\n <span className=\"tabular-nums text-text-primary\">\n {formatPrice(order.total)}\n </span>\n </span>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n })}\n\n {/* Load More */}\n {hasMore && (\n <div className=\"flex justify-center px-4 py-5\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"flex items-center gap-2 rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-subtle disabled:opacity-50\"\n aria-label={t('common.loadMore', { defaultValue: 'Load more' })}\n >\n {loading ? (\n <Loader2 className=\"size-4 animate-spin\" aria-hidden=\"true\" />\n ) : null}\n {t('common.loadMore', { defaultValue: 'Load more' })}\n </button>\n </div>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n\n {/* Install App Modal */}\n {selectedApp && (\n <InstallAppModal\n isOpen={isModalOpen}\n onClose={handleCloseModal}\n app={selectedApp}\n orderId={selectedOrderId}\n onInstallSuccess={handleInstallSuccess}\n onInstallError={handleInstallError}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AAkEA,IAAM,IAAiD;CAAC;CAAa;CAAa;CAAY,EAKxF,KAAoB,MAA8B;AACtD,SAAQ,GAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK,YACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO;GACR;EACH,KAAK,aACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO;GACR;EACH,KAAK,UACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO;GACR;EACH,KAAK,SACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO;GACR;EACH,KAAK,WACH,QAAO;GACL,MAAM;GACN,SACE;GACF,OAAO;GACR;EACH,KAAK,YACH,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO;GACR;EACH,QACE,QAAO;GACL,MAAM;GACN,SAAS;GACT,OAAO;GACR;;GAOD,KAAkD,EAAE,gBAAa;CACrE,IAAM,IAAU,EAAiB,EAAO,EAClC,IAAO,EAAQ;AACrB,QACE,kBAAC,QAAD;EACE,WAAW,0FAA0F,EAAQ;YAD/G,CAGE,kBAAC,GAAD,EAAM,WAAU,YAAa,CAAA,EAC5B,EAAQ,MACJ;;GAUE,UAAuB;CAClC,IAAM,IAAW,IAAa,EACxB,EAAE,gBAAa,GAAU,EACzB,EAAE,SAAM,GAAS,EACjB,EAAE,QAAQ,GAAe,YAAS,UAAO,kBAAe,YAAS,gBAAa,GAAW,EACzF,EAAE,kBAAe,SAAS,OAAyB,GAAkB;AAE3E,GAAkC;EAChC,cAAc;AACP,MAAe;;EAEtB,UAAU,MAAQ;AAChB,WAAQ,KAAK,0CAA0C,EAAI,QAAQ;;EAEtE,CAAC;CAGF,IAAM,CAAC,IAAgB,MAAqB,kBAAsB,IAAI,KAAK,CAAC,EACtE,CAAC,GAAW,KAAgB,EAAoB,YAAY,EAG5D,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAiB,KAAsB,EAAwB,KAAK,EACrE,CAAC,GAAa,KAAkB,EAAyB,KAAK,EAC9D,CAAC,IAAgB,KAAqB,EAAwB,KAAK,EAEnE,IAAsB,QAExB,IAAI,IACF,EAAc,SAAS,MACrB,EAAa,WAAW,WAAW,CAAC,EAAa,UAAU,GAAG,EAAE,CACjE,CACF,EACH,CAAC,EAAc,CAChB,EAGK,IAAoB,EAAc,KAAK,MAAU;EACrD,IAAM,IAAY,EAAM,aAAa,EAAE,EACjC,IAAW,EAAU,QAAQ,GAAK,MAAS,IAAM,EAAK,UAAU,EAAE,EAClE,IAAM,EAAM,QAAQ,GAEpB,IAAqB,EAAU,KAAK,GAAM,OAAW;GACzD,IAAI,GAAG,EAAM,GAAG,GAAG;GACnB,WAAW,EAAK;GAChB,aAAa,EAAK;GAClB,aAAa,EAAK;GAClB,aACE,EAAK,gBACJ,OAAO,EAAK,YAAa,WAAW,EAAK,SAAS,aAAa,GAAG;GACrE,SAAS,EAAK;GACd,UAAU,EAAK;GACf,WAAW,EAAK;GAChB,YAAY,EAAK;GACjB,WAAW,EAAoB,IAAI,EAAK,UAAU;GACnD,EAAE;AAEH,SAAO;GACL,IAAI,EAAM;GACV,aAAa,IAAI,EAAM,GAAG,MAAM,GAAG,CAAC,aAAa;GACjD;GACA;GACA;GACA,OAAO,EAAM;GACb,QAAQ,EAAM;GACd,WAAW,EAAM;GACjB,aAAa,EAAM,eAAe;GACnC;GACD,EAEI,IAAkB,QAChB,EAAO,QAAQ,MAAU,EAA0B,SAAS,EAAM,OAAO,CAAC,EAChF,CAAC,EAAO,CACT,EAEK,IAAgB,QACd,EAAO,QAAQ,MAAU,CAAC,EAA0B,SAAS,EAAM,OAAO,CAAC,EACjF,CAAC,EAAO,CACT,EAEK,IAAgB,MAAc,cAAc,IAAkB,GAE9D,IAAe,GAAa,MAAoB;AACpD,MAAmB,MAAS;GAC1B,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,EAAK,IAAI,EAAQ,GACnB,EAAK,OAAO,EAAQ,GAEpB,EAAK,IAAI,EAAQ,EAEZ;IACP;IACD,EAAE,CAAC,EAEA,MAA0B,GAAiB,MAAoB;AAUnE,EATA,EAAmB,EAAQ,EAC3B,EAAkB,EAAK,GAAG,EAC1B,EAAe;GACb,IAAI,EAAK;GACT,MAAM,EAAK;GACX,MAAM,EAAK;GACX,MAAM,EAAK;GACX,SAAS,EAAK;GACf,CAAC,EACF,EAAe,GAAK;IAGhB,UAAyB;AAI7B,EAHA,EAAe,GAAM,EACrB,EAAmB,KAAK,EACxB,EAAe,KAAK,EACpB,EAAkB,KAAK;IAGnB,MAAwB,GAAsB,MAA2B;AAExE,EADL,GAAkB,EACb,IAAsB;IAGvB,MAAsB,MAAkB,IAIxC,UAAgC;AACpC,IAAS,GAAG,EAAS,cAAc;IAI/B,IAAU,qBACV,IACJ;AAEF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,UAAD;IAAQ,WAAU;cAChB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,GAAG;QAC3B,cAAW;QACX,WAAU;kBAEV,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;QACzB,CAAA,EACT,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA;SAChD,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAE,sBAAsB,EAAE,cAAc,aAAa,CAAC;UACpD,CAAA;SACJ,EAAO,SAAS,KACf,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAO;UACH,CAAA;SAEL;UACF;;MACF,CAAA;KACF,CAAA;IACC,CAAA;GAGT,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAa,WAAU;gBAAO;MAKhB,CAAA,EAEb,KAAW,EAAO,WAAW,IAE5B,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAS,WAAU;OAA4C,eAAY;OAAS,CAAA,EACpF,kBAAC,KAAD;OAAG,WAAU;iBAAkB;OAAwB,CAAA,CACnD;UACJ,IAEF,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAS,WAAU,kCAAmC,CAAA;QAClD,CAAA;OACN,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,6BAA6B,EAAE,cAAc,yBAAyB,CAAC;QACvE,CAAA;OACL,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAM,WAAW;QAChB,CAAA;OACJ,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAHZ,CAKE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EAAA,qBAE3B;;OACL;UACJ,EAAO,WAAW,IAEpB,kBAAC,GAAD;MACE,cAAa;MACb,OAAO,EAAE,2BAA2B,EAAE,cAAc,iBAAiB,CAAC;MACtE,aAAY;MACZ,QACE,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAa,WAAU,UAAW,CAAA,EAAA,qBAE3B;;MAEX,CAAA,GAGF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAa,YAAY;QACxC,WAAW,8DACT,MAAc,cACV,kDACA;kBANR,CAQC,aAEC,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAgB;SACZ,CAAA,CACA;WACT,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAa,UAAU;QACtC,WAAW,8DACT,MAAc,YACV,kDACA;kBANR,CAQC,WAEC,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAc;SACV,CAAA,CACA;UACL;UAEL,EAAc,WAAW,IACxB,kBAAC,GAAD;OAAW,WAAU;iBAArB;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;SAC1B,CAAA;QACN,kBAAC,MAAD;SAAI,WAAU;mBACX,MAAc,cAAc,4BAA4B;SACtD,CAAA;QACL,kBAAC,KAAD;SAAG,WAAU;mBACV,MAAc,cACX,qEACA;SACF,CAAA;QACM;WAEZ,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD,EAAK,WAAW,GAAa,CAAA;UAC7B,kBAAC,OAAD;WAAK,WAAW;qBAAW;WAAW,CAAA;UACtC,kBAAC,OAAD;WAAK,WAAW;qBAAW;WAAU,CAAA;UACrC,kBAAC,OAAD;WAAK,WAAW;qBAAW;WAAW,CAAA;UACtC,kBAAC,OAAD;WAAK,WAAW,GAAG,EAAU;qBAAc;WAAW,CAAA;UACtD,kBAAC,OAAD;WAAK,WAAW,GAAG,EAAU;qBAAe;WAAY,CAAA;UACpD;;QAGL,EAAc,KAAK,MAAU;SAC5B,IAAM,IAAa,GAAe,IAAI,EAAM,GAAG,EACzC,IACJ,EAAM,WAAW,eACjB,EAAM,WAAW,eACjB,EAAM,WAAW;AAEnB,gBACE,kBAAC,OAAD;UAAoB,WAAU;oBAA9B,CAEE,kBAAC,UAAD;WACE,MAAK;WACL,eAAe,EAAa,EAAM,GAAG;WACrC,WAAU;qBAHZ;YAME,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACzB,kBAAC,GAAD,EACE,WAAW,4DACT,IAAa,cAAc,MAE7B,CAAA;aACE,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACxB,EAAM;aACH,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACxB,IAAI,KAAK,EAAM,UAAU,CAAC,mBAAmB,SAAS;cACrD,MAAM;cACN,OAAO;cACP,KAAK;cACN,CAAC;aACE,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBAA3B;cACG,EAAM,MAAM;cAAO;cAAE,EAAM,MAAM,WAAW,IAAI,SAAS;cAC1D,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAM,MAAM,KAAK,MAAM,EAAE,YAAY,CAAC,KAAK,KAAK;eAC5C,CAAA;cACH;;YAGN,kBAAC,OAAD;aACE,WAAW,GAAG,EAAQ;uBAErB,EAAY,EAAM,MAAM;aACrB,CAAA;YAGN,kBAAC,OAAD;aAAK,WAAW,GAAG,EAAQ;uBACzB,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA;aACjC,CAAA;YACC;cAGR,KACC,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACG,EAAM,eACL,kBAAC,OAAD;aAAK,WAAU;uBAAf,CACE,kBAAC,OAAD;cAAK,WAAU;wBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;eAAK,WAAU;yBAA0C;eAEnD,CAAA,EACN,kBAAC,OAAD;eAAK,WAAU;yBAAf;gBACG,EAAM,YAAY,UAAU;gBAC5B,EAAM,YAAY,UACf,MAAM,EAAM,YAAY,YACxB;gBACH,EAAM,YAAY,wBACf,oBAAoB,IAAI,KAAK,EAAM,YAAY,sBAAsB,CAAC,oBAAoB,KAC1F;gBACA;iBACF,EAAA,CAAA,EAEN,kBAAC,OAAD;eAAK,WAAU;yBAAf,CACG,EAAM,YAAY,kBACjB,kBAAC,OAAD;gBAAK,WAAU;0BAAf;iBAAyC;iBAC3B;iBACZ,kBAAC,QAAD;kBAAM,WAAU;4BACb,EAAM,YAAY;kBACd,CAAA;iBACH;mBAEP,EAAM,YAAY,eACjB,kBAAC,KAAD;gBACE,MAAM,EAAM,YAAY;gBACxB,QAAO;gBACP,KAAI;gBACJ,WAAU;0BAJZ,CAKC,iBAEC,kBAAC,GAAD,EAAc,WAAU,YAAa,CAAA,CACnC;kBAEF;iBACF;iBAEN,kBAAC,OAAD;cAAK,WAAU;wBACZ;eACC;gBAAE,KAAK;gBAAc,OAAO;gBAAc;eAC1C;gBAAE,KAAK;gBAAW,OAAO;gBAAW;eACpC;gBAAE,KAAK;gBAAoB,OAAO;gBAAoB;eACtD;gBAAE,KAAK;gBAAa,OAAO;gBAAa;eACzC,CAAC,KAAK,MAAS;eACd,IAAM,IAAU,EAAM,aAAa,UAAU;AAgB7C,sBACE,kBAAC,OAAD;gBAEE,WAAW,mDAjBb;iBACE;iBACA;iBACA;iBACA;iBACA;iBACA;iBACD,CAAC,QAAQ,EAAQ,IAClB;iBACE;iBACA;iBACA;iBACA;iBACD,CAAC,QAAQ,EAAK,IAAI,GAMX,sFACA;0BAGL,EAAK;gBACF,EARC,EAAK,IAQN;gBAER;cACE,CAAA,CACF;;YAIR,kBAAC,OAAD;aAAK,WAAU;uBAAf;cACE,kBAAC,QAAD;eAAM,WAAU;yBAAsC;eAAc,CAAA;cACpE,kBAAC,QAAD;eAAM,WAAU;yBAAiD;eAE1D,CAAA;cACP,kBAAC,QAAD;eAAM,WAAU;yBAAiD;eAE1D,CAAA;cACP,kBAAC,QAAD;eAAM,WAAU;yBAAkD;eAE3D,CAAA;cACH;;YAEL,EAAM,MAAM,KAAK,MAAS;aACzB,IAAM,IACJ,EAAK,gBAAgB,cACrB,EAAK,gBAAgB;AAEvB,oBACE,kBAAC,OAAD;cAEE,WAAU;wBAFZ;eAKE,kBAAC,OAAD;gBAAK,WAAU;0BAAf,CACE,kBAAC,OAAD;iBAAK,WAAU;2BACZ,EAAK,cACJ,kBAAC,OAAD;kBACE,KAAK,EAAK;kBACV,KAAK,EAAK;kBACV,WAAU;kBACV,CAAA,GAEF,kBAAC,OAAD;kBAAK,WAAU;4BACZ,EAAK,YAAY,OAAO,EAAE;kBACvB,CAAA;iBAEJ,CAAA,EACN,kBAAC,OAAD;iBAAK,WAAU;2BAAf,CACE,kBAAC,KAAD;kBAAG,WAAU;4BACV,EAAK;kBACJ,CAAA,EACJ,kBAAC,QAAD;kBAAM,WAAU;4BACb,EAAK;kBACD,CAAA,CACH;mBACF;;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAK;gBACF,CAAA;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAY,EAAK,WAAW;gBACzB,CAAA;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BAAf,CACE,kBAAC,QAAD;iBAAM,WAAU;2BAAhB,CAAgC,EAAK,UAAS,MAAgB;oBAC9D,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAY,EAAK,UAAU;iBACvB,CAAA,CACH;;eAGN,kBAAC,OAAD;gBAAK,WAAU;0BACZ,EAAK,YACJ,kBAAC,QAAD;iBAAM,WAAU;2BAAhB,CACE,kBAAC,GAAD,EAAa,WAAU,YAAa,CAAA,EAAA,YAE/B;qBACL,KAAgB,IAClB,kBAAC,UAAD;iBACE,MAAK;iBACL,UAAU,MAAM;AAEd,kBADA,EAAE,iBAAiB,EACnB,GAAuB,EAAM,IAAI,EAAK;;iBAExC,WAAU;2BANZ,CAQE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EAAA,cAE1B;qBACP;gBACA,CAAA;eACF;gBAnEC,EAAK,GAmEN;cAER;YAGF,kBAAC,OAAD;aAAK,WAAU;uBACb,kBAAC,OAAD;cAAK,WAAU;wBAAf;eACE,kBAAC,QAAD,EAAA,UAAA;gBAAM;gBACM;gBACV,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAY,EAAM,SAAS;iBACvB,CAAA;gBACF,EAAA,CAAA;eACP,kBAAC,QAAD,EAAA,UAAA;gBAAM;gBACC;gBACL,kBAAC,QAAD;iBAAM,WAAU;2BACb,EAAY,EAAM,IAAI;iBAClB,CAAA;gBACF,EAAA,CAAA;eACP,kBAAC,QAAD;gBAAM,WAAU;0BAAhB;iBAAgC;iBACvB;iBACP,kBAAC,QAAD;kBAAM,WAAU;4BACb,EAAY,EAAM,MAAM;kBACpB,CAAA;iBACF;;eACH;;aACF,CAAA;YACF;aAEJ;YA5PI,EAAM,GA4PV;UAER;QAGD,KACC,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,UAAD;UACE,MAAK;UACL,SAAS;UACT,UAAU;UACV,WAAU;UACV,cAAY,EAAE,mBAAmB,EAAE,cAAc,aAAa,CAAC;oBALjE,CAOG,IACC,kBAAC,GAAD;WAAS,WAAU;WAAsB,eAAY;WAAS,CAAA,GAC5D,MACH,EAAE,mBAAmB,EAAE,cAAc,aAAa,CAAC,CAC7C;;SACL,CAAA;QAEJ;SAEJ;QAEJ;;IACF,CAAA;GAGL,KACC,kBAAC,GAAD;IACE,QAAQ;IACR,SAAS;IACT,KAAK;IACL,SAAS;IACT,kBAAkB;IAClB,gBAAgB;IAChB,CAAA;GAEA"}
@@ -9,7 +9,7 @@ import { nativeConfirm as se, nativeImpact as ce, nativeNotify as le } from "../
9
9
  import { useCallback as a, useEffect as ue, useMemo as de, useRef as fe, useState as o } from "react";
10
10
  import { AlertCircle as s, ArrowLeft as pe, Check as me, Loader2 as he, Package as c, ShieldCheck as l, ShoppingCart as ge, Star as u, ThumbsUp as d } from "lucide-react";
11
11
  import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
12
- import { useLocation as _e, useNavigate as ve, useParams as ye } from "react-router-dom";
12
+ import { useLocation as _e, useNavigate as ve, useParams as ye } from "react-router";
13
13
  import { EmphasisPanel as be, GlassCard as h, IllustratedEmptyState as xe, NextSteps as Se, PagePurpose as Ce } from "@burdenoff/fe-libs/ui";
14
14
  import { useEventBus as we } from "@burdenoff/fe-libs/shared/events";
15
15
  //#region src/pages/ProductDetailPage.tsx