@burdenoff/microfe-store 2026.525.2 → 2026.527.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 (120) hide show
  1. package/dist/StoreAdminRoutes.js +10 -9
  2. package/dist/StoreAdminRoutes.js.map +1 -1
  3. package/dist/StoreRoutes.js +2 -1
  4. package/dist/StoreRoutes.js.map +1 -1
  5. package/dist/components/AccessDenied.js +23 -0
  6. package/dist/components/AccessDenied.js.map +1 -0
  7. package/dist/components/InstallAppModal.js +183 -129
  8. package/dist/components/InstallAppModal.js.map +1 -1
  9. package/dist/components/PermissionGuard.js +8 -0
  10. package/dist/components/PermissionGuard.js.map +1 -0
  11. package/dist/components/RouteGuards.js +7 -21
  12. package/dist/components/RouteGuards.js.map +1 -1
  13. package/dist/components/cart/CartDrawer.js +20 -14
  14. package/dist/components/cart/CartDrawer.js.map +1 -1
  15. package/dist/hooks/useStoreGraphQL.js +22 -20
  16. package/dist/hooks/useStoreGraphQL.js.map +1 -1
  17. package/dist/hooks/useStoreGraphQLWithContext.js.map +1 -1
  18. package/dist/hooks/useStorePermissions.js +4 -1
  19. package/dist/hooks/useStorePermissions.js.map +1 -1
  20. package/dist/index.js +4 -4
  21. package/dist/pages/AdminDashboardPage.js +182 -183
  22. package/dist/pages/AdminDashboardPage.js.map +1 -1
  23. package/dist/pages/AdminReviewModerationPage.js +7 -6
  24. package/dist/pages/AdminReviewModerationPage.js.map +1 -1
  25. package/dist/pages/AdminStoresPage.js +193 -185
  26. package/dist/pages/AdminStoresPage.js.map +1 -1
  27. package/dist/pages/AdminSubmissionsQueuePage.js +140 -142
  28. package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
  29. package/dist/pages/AppDetailPage.js +6 -3
  30. package/dist/pages/AppDetailPage.js.map +1 -1
  31. package/dist/pages/AppSettingsPage.js +6 -3
  32. package/dist/pages/AppSettingsPage.js.map +1 -1
  33. package/dist/pages/CartPage.js +23 -15
  34. package/dist/pages/CartPage.js.map +1 -1
  35. package/dist/pages/InstalledAppsPage.js +26 -15
  36. package/dist/pages/InstalledAppsPage.js.map +1 -1
  37. package/dist/pages/MarketplaceHomePage.js +275 -247
  38. package/dist/pages/MarketplaceHomePage.js.map +1 -1
  39. package/dist/pages/MyLicensesPage.js +10 -8
  40. package/dist/pages/MyLicensesPage.js.map +1 -1
  41. package/dist/pages/OrderConfirmationPage.js +72 -75
  42. package/dist/pages/OrderConfirmationPage.js.map +1 -1
  43. package/dist/pages/OrdersPage.js +22 -20
  44. package/dist/pages/OrdersPage.js.map +1 -1
  45. package/dist/pages/ProductCategoryPage.js +3 -1
  46. package/dist/pages/ProductCategoryPage.js.map +1 -1
  47. package/dist/pages/ProductDetailPage.js +458 -443
  48. package/dist/pages/ProductDetailPage.js.map +1 -1
  49. package/dist/pages/ProductReviewsPage.js +4 -3
  50. package/dist/pages/ProductReviewsPage.js.map +1 -1
  51. package/dist/pages/PublisherDashboardPage.js +13 -7
  52. package/dist/pages/PublisherDashboardPage.js.map +1 -1
  53. package/dist/pages/PublisherRevenuePage.js +7 -5
  54. package/dist/pages/PublisherRevenuePage.js.map +1 -1
  55. package/dist/pages/PublisherSubmissionsPage.js +12 -7
  56. package/dist/pages/PublisherSubmissionsPage.js.map +1 -1
  57. package/dist/pages/PublisherSubmitPage.js +6 -0
  58. package/dist/pages/PublisherSubmitPage.js.map +1 -1
  59. package/dist/pages/SearchResultsPage.js +6 -3
  60. package/dist/pages/SearchResultsPage.js.map +1 -1
  61. package/dist/pages/StoresPage.js +73 -74
  62. package/dist/pages/StoresPage.js.map +1 -1
  63. package/dist/providers/StoreProvider.js +25 -25
  64. package/dist/providers/StoreProvider.js.map +1 -1
  65. package/dist/utils/index.js +29 -19
  66. package/dist/utils/index.js.map +1 -1
  67. package/package.json +1 -1
  68. package/dist/StoreAdminRoot.d.ts +0 -22
  69. package/dist/StoreAdminRoutes.d.ts +0 -9
  70. package/dist/StoreRoot.d.ts +0 -31
  71. package/dist/StoreRoutes.d.ts +0 -12
  72. package/dist/components/InstallAppModal.d.ts +0 -26
  73. package/dist/components/RouteGuards.d.ts +0 -17
  74. package/dist/components/cart/CartDrawer.d.ts +0 -35
  75. package/dist/components/index.d.ts +0 -9
  76. package/dist/components/index.js +0 -3
  77. package/dist/components/marketplace/ProductCard.d.ts +0 -18
  78. package/dist/components/marketplace/ProductCard.js +0 -2
  79. package/dist/dev/main.d.ts +0 -1
  80. package/dist/generated/global-operations.d.ts +0 -2012
  81. package/dist/generated/global-types.d.ts +0 -23526
  82. package/dist/generated/wspace-operations.d.ts +0 -1
  83. package/dist/generated/wspace-types.d.ts +0 -57850
  84. package/dist/hooks/index.d.ts +0 -12
  85. package/dist/hooks/useBackendCart.d.ts +0 -73
  86. package/dist/hooks/useCart.d.ts +0 -62
  87. package/dist/hooks/useInstallations.d.ts +0 -43
  88. package/dist/hooks/useOrders.d.ts +0 -40
  89. package/dist/hooks/useProductSearch.d.ts +0 -22
  90. package/dist/hooks/useStoreGraphQL.d.ts +0 -475
  91. package/dist/hooks/useStoreGraphQLWithContext.d.ts +0 -48
  92. package/dist/hooks/useStorePermissions.d.ts +0 -63
  93. package/dist/index.d.ts +0 -44
  94. package/dist/pages/AdminDashboardPage.d.ts +0 -3
  95. package/dist/pages/AdminReviewModerationPage.d.ts +0 -3
  96. package/dist/pages/AdminStoresPage.d.ts +0 -3
  97. package/dist/pages/AdminSubmissionsQueuePage.d.ts +0 -3
  98. package/dist/pages/AppDetailPage.d.ts +0 -3
  99. package/dist/pages/AppSettingsPage.d.ts +0 -3
  100. package/dist/pages/CartPage.d.ts +0 -8
  101. package/dist/pages/InstalledAppsPage.d.ts +0 -8
  102. package/dist/pages/MarketplaceHomePage.d.ts +0 -3
  103. package/dist/pages/MyLicensesPage.d.ts +0 -3
  104. package/dist/pages/OrderConfirmationPage.d.ts +0 -3
  105. package/dist/pages/OrdersPage.d.ts +0 -8
  106. package/dist/pages/ProductCategoryPage.d.ts +0 -3
  107. package/dist/pages/ProductDetailPage.d.ts +0 -8
  108. package/dist/pages/ProductReviewsPage.d.ts +0 -3
  109. package/dist/pages/PublisherDashboardPage.d.ts +0 -3
  110. package/dist/pages/PublisherRevenuePage.d.ts +0 -3
  111. package/dist/pages/PublisherSubmissionsPage.d.ts +0 -3
  112. package/dist/pages/PublisherSubmitPage.d.ts +0 -3
  113. package/dist/pages/SearchResultsPage.d.ts +0 -3
  114. package/dist/pages/StoresPage.d.ts +0 -3
  115. package/dist/providers/StoreProvider.d.ts +0 -38
  116. package/dist/store/storeStore.d.ts +0 -69
  117. package/dist/translations/en.d.ts +0 -518
  118. package/dist/types/index.d.ts +0 -575
  119. package/dist/utils/index.d.ts +0 -67
  120. package/dist/utils/nativeBridge.d.ts +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { ArrowLeft, Check, X, RefreshCw, PackageCheck, ChevronDown, ChevronUp } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL';\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'All', value: 'ALL' },\n];\n\nexport const AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n\n const loadProducts = async (filter: ProductStatus) => {\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [statusFilter]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const formatPrice = (price: number, currency: string, model: string) => {\n if (model === 'FREE') return 'Free';\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: currency || 'USD',\n }).format(price);\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <PackageCheck className=\"h-5 w-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Submission queue</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {totalCount}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-4 flex gap-2\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-surface-accent text-text-on-accent'\n : 'bg-surface-muted text-text-secondary hover:bg-surface-hover'\n }`}\n >\n {f.label}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl px-6 py-6\">\n {error && (\n <div\n role=\"alert\"\n className=\"mb-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 && products.length === 0 ? (\n <div role=\"status\" className=\"py-16 text-center text-text-secondary\">\n Loading submissions…\n </div>\n ) : products.length === 0 ? (\n <div className=\"py-16 text-center text-text-secondary\">\n No products matching the current filter.\n </div>\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-lg border border-border-default bg-surface-raised\"\n >\n <div className=\"flex items-start gap-4 p-4\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"h-14 w-14 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex h-14 w-14 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {p.type}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {p.name}\n </h3>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {p.type}\n </span>\n <span className=\"rounded-full bg-surface-muted px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {p.nature}\n </span>\n </div>\n <div className=\"mt-0.5 text-xs text-text-tertiary\">{p.slug}</div>\n <div className=\"mt-1 flex items-center gap-3 text-xs text-text-secondary\">\n <span>Publisher: {p.publisher?.name ?? 'Unknown'}</span>\n <span>Price: {formatPrice(p.price, p.currency, p.pricingModel)}</span>\n {p.submittedAt && (\n <span>Submitted {new Date(p.submittedAt).toLocaleString()}</span>\n )}\n </div>\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">\n {p.description}\n </p>\n )}\n </div>\n <div className=\"flex flex-shrink-0 flex-col gap-2\">\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs text-text-secondary hover:bg-surface-hover\"\n >\n {viewingId === p.id ? (\n <ChevronUp className=\"h-3.5 w-3.5\" />\n ) : (\n <ChevronDown className=\"h-3.5 w-3.5\" />\n )}\n Details\n </button>\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-surface-success px-3 py-1.5 text-xs font-medium text-text-on-success hover:bg-surface-success-hover disabled:opacity-60\"\n >\n <Check className=\"h-3.5 w-3.5\" />\n Approve\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-danger bg-surface-danger-muted px-3 py-1.5 text-xs font-medium text-text-danger hover:bg-surface-danger hover:text-text-on-danger disabled:opacity-60\"\n >\n <X className=\"h-3.5 w-3.5\" />\n Reject\n </button>\n </div>\n </div>\n\n {viewingId === p.id && (\n <div className=\"border-t border-border-default bg-surface-muted px-4 py-4\">\n <dl className=\"grid grid-cols-2 gap-x-6 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-text-secondary\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.slug}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Type</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.type}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Nature</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.nature}</dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Category</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-text-secondary\">Pricing</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {formatPrice(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-text-tertiary\">({p.pricingModel})</span>\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Publisher</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {p.publisher?.name ?? '—'}\n {p.publisher?.email && (\n <span className=\"block text-text-tertiary\">{p.publisher.email}</span>\n )}\n </dd>\n </div>\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted by</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.submittedBy}</dd>\n </div>\n )}\n {p.submittedAt && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted at</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {new Date(p.submittedAt).toLocaleString()}\n </dd>\n </div>\n )}\n {p.description && (\n <div className=\"col-span-2 sm:col-span-3\">\n <dt className=\"font-medium text-text-secondary\">Description</dt>\n <dd className=\"mt-0.5 whitespace-pre-wrap text-text-primary\">\n {p.description}\n </dd>\n </div>\n )}\n </dl>\n </div>\n )}\n\n {rejectingId === p.id && (\n <div className=\"border-t border-border-default bg-surface-muted px-4 py-3\">\n <label\n htmlFor={`reject-reason-${p.id}`}\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Rejection reason (required)\n </label>\n <textarea\n id={`reject-reason-${p.id}`}\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-2 text-sm text-text-primary focus:border-border-accent focus:outline-none\"\n placeholder=\"Explain why this submission is being rejected…\"\n />\n <div className=\"mt-2 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.id}\n className=\"rounded-md bg-surface-danger px-3 py-1 text-xs font-medium text-text-on-danger hover:bg-surface-danger-hover disabled:opacity-60\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, Check, X, RefreshCw, PackageCheck, ChevronDown, ChevronUp } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL';\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'All', value: 'ALL' },\n];\n\nexport const AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n\n const loadProducts = useCallback(\n async (filter: ProductStatus) => {\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [loadProducts, statusFilter]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <header className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <PackageCheck className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Submission queue</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {totalCount}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-4 flex gap-2\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-surface-accent text-text-on-accent'\n : 'bg-surface-muted text-text-secondary hover:bg-surface-hover'\n }`}\n >\n {f.label}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n {error && (\n <div\n role=\"alert\"\n className=\"mb-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 && products.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">\n Loading submissions…\n </output>\n ) : products.length === 0 ? (\n <div className=\"py-16 text-center text-text-secondary\">\n No products matching the current filter.\n </div>\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-lg border border-border-default bg-surface-raised\"\n >\n <div className=\"flex items-start gap-4 p-4\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-14 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {p.type}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {p.name}\n </h3>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {p.type}\n </span>\n <span className=\"rounded-full bg-surface-muted px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {p.nature}\n </span>\n </div>\n <div className=\"mt-0.5 text-xs text-text-tertiary\">{p.slug}</div>\n <div className=\"mt-1 flex items-center gap-3 text-xs text-text-secondary\">\n <span>Publisher: {p.publisher?.name ?? 'Unknown'}</span>\n <span>Price: {formatPriceForModel(p.price, p.currency, p.pricingModel)}</span>\n {p.submittedAt && (\n <span>Submitted {new Date(p.submittedAt).toLocaleString()}</span>\n )}\n </div>\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">\n {p.description}\n </p>\n )}\n </div>\n <div className=\"flex flex-shrink-0 flex-col gap-2\">\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs text-text-secondary hover:bg-surface-hover\"\n >\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n Details\n </button>\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-surface-success px-3 py-1.5 text-xs font-medium text-text-on-success hover:bg-surface-success-hover disabled:opacity-60\"\n >\n <Check className=\"size-3.5\" />\n Approve\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-danger bg-surface-danger-muted px-3 py-1.5 text-xs font-medium text-text-danger hover:bg-surface-danger hover:text-text-on-danger disabled:opacity-60\"\n >\n <X className=\"size-3.5\" />\n Reject\n </button>\n </div>\n </div>\n\n {viewingId === p.id && (\n <div className=\"border-t border-border-default bg-surface-muted p-4\">\n <dl className=\"grid grid-cols-2 gap-x-6 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-text-secondary\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.slug}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Type</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.type}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Nature</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.nature}</dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Category</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-text-secondary\">Pricing</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-text-tertiary\">({p.pricingModel})</span>\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Publisher</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {p.publisher?.name ?? '—'}\n {p.publisher?.email && (\n <span className=\"block text-text-tertiary\">{p.publisher.email}</span>\n )}\n </dd>\n </div>\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted by</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.submittedBy}</dd>\n </div>\n )}\n {p.submittedAt && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted at</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {new Date(p.submittedAt).toLocaleString()}\n </dd>\n </div>\n )}\n {p.description && (\n <div className=\"col-span-2 sm:col-span-3\">\n <dt className=\"font-medium text-text-secondary\">Description</dt>\n <dd className=\"mt-0.5 whitespace-pre-wrap text-text-primary\">\n {p.description}\n </dd>\n </div>\n )}\n </dl>\n </div>\n )}\n\n {rejectingId === p.id && (\n <div className=\"border-t border-border-default bg-surface-muted px-4 py-3\">\n <label\n htmlFor={`reject-reason-${p.id}`}\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Rejection reason (required)\n </label>\n <textarea\n id={`reject-reason-${p.id}`}\n aria-label=\"Rejection reason\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-2 text-sm text-text-primary focus:border-border-accent focus:outline-none\"\n placeholder=\"Explain why this submission is being rejected…\"\n />\n <div className=\"mt-2 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.id}\n className=\"rounded-md bg-surface-danger px-3 py-1 text-xs font-medium text-text-on-danger hover:bg-surface-danger-hover disabled:opacity-60\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -14,10 +14,11 @@ var l = () => {
14
14
  children: /* @__PURE__ */ o("div", {
15
15
  className: "mx-auto flex max-w-4xl items-center gap-3",
16
16
  children: [/* @__PURE__ */ a("button", {
17
+ type: "button",
17
18
  onClick: () => l(-1),
18
19
  "aria-label": "Go back",
19
20
  className: "cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary",
20
- children: /* @__PURE__ */ a(r, { className: "h-5 w-5" })
21
+ children: /* @__PURE__ */ a(r, { className: "size-5" })
21
22
  }), /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h1", {
22
23
  className: "text-2xl font-bold text-text-primary",
23
24
  children: h("pages.appDetail.title", { defaultValue: "Installed App Details" })
@@ -33,7 +34,7 @@ var l = () => {
33
34
  children: p ? /* @__PURE__ */ a("p", {
34
35
  role: "status",
35
36
  className: "text-sm text-text-muted",
36
- children: "Loading installation..."
37
+ children: "Loading installation"
37
38
  }) : m ? /* @__PURE__ */ a("p", {
38
39
  role: "alert",
39
40
  className: "text-sm text-status-error-text",
@@ -142,10 +143,12 @@ var l = () => {
142
143
  /* @__PURE__ */ o("div", {
143
144
  className: "mt-4 flex flex-wrap gap-3",
144
145
  children: [/* @__PURE__ */ o("button", {
146
+ type: "button",
145
147
  onClick: () => l(`${u}/installed/${f.id}/settings`),
146
148
  className: "inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
147
- children: [/* @__PURE__ */ a(i, { className: "h-4 w-4" }), h("pages.appDetail.manageConfig", { defaultValue: "Manage Configuration" })]
149
+ children: [/* @__PURE__ */ a(i, { className: "size-4" }), h("pages.appDetail.manageConfig", { defaultValue: "Manage Configuration" })]
148
150
  }), /* @__PURE__ */ a("button", {
151
+ type: "button",
149
152
  onClick: () => l(`${u}/marketplace/product/${f.productId}`),
150
153
  className: "inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
151
154
  children: h("pages.appDetail.viewProduct", { defaultValue: "View Product" })
@@ -1 +1 @@
1
- {"version":3,"file":"AppDetailPage.js","names":[],"sources":["../../src/pages/AppDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Settings } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const AppDetailPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\n <button\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appDetail.title', { defaultValue: 'Installed App Details' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appDetail.subtitle', {\n defaultValue: 'Inspect installation status and metadata',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading installation...\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-5\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h2>\n <p className=\"mt-1 text-sm text-text-muted\">Product {installation.productId}</p>\n </div>\n <span className=\"inline-flex w-fit rounded-full bg-bg-sunken px-3 py-1 text-xs font-medium text-text-primary\">\n {installation.status}\n </span>\n </div>\n\n <div className=\"mt-4 grid gap-4 md:grid-cols-2\">\n <div className=\"rounded-lg bg-bg-sunken p-4 text-sm text-text-muted\">\n <p>\n <span className=\"font-medium text-text-primary\">Version:</span>{' '}\n {installation.version ?? 'Not provided'}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Installed at:</span>{' '}\n {new Date(installation.installedAt).toLocaleString()}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Updated at:</span>{' '}\n {new Date(installation.updatedAt).toLocaleString()}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Workspace ID:</span>{' '}\n {installation.workspaceId}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Organization ID:</span>{' '}\n {installation.organizationId ?? 'N/A'}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Installed by:</span>{' '}\n {installation.installedById}\n </p>\n </div>\n\n <div className=\"rounded-lg bg-bg-sunken p-4 text-sm text-text-muted\">\n <p className=\"font-medium text-text-primary\">Runtime metadata</p>\n <pre className=\"mt-3 overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-surface p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata,\n context: installation.context,\n uninstalledAt: installation.uninstalledAt,\n uninstalledById: installation.uninstalledById,\n },\n null,\n 2\n )}\n </pre>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n onClick={() => navigate(`${basePath}/installed/${installation.id}/settings`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Settings className=\"h-4 w-4\" />\n {t('pages.appDetail.manageConfig', { defaultValue: 'Manage Configuration' })}\n </button>\n <button\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AppDetailPage.js","names":[],"sources":["../../src/pages/AppDetailPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Settings } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const AppDetailPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\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 hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appDetail.title', { defaultValue: 'Installed App Details' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appDetail.subtitle', {\n defaultValue: 'Inspect installation status and metadata',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading installation…\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"rounded-xl border border-border-default bg-bg-surface p-5\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {installation.workspaceName ?? installation.workspaceId}\n </h2>\n <p className=\"mt-1 text-sm text-text-muted\">Product {installation.productId}</p>\n </div>\n <span className=\"inline-flex w-fit rounded-full bg-bg-sunken px-3 py-1 text-xs font-medium text-text-primary\">\n {installation.status}\n </span>\n </div>\n\n <div className=\"mt-4 grid gap-4 md:grid-cols-2\">\n <div className=\"rounded-lg bg-bg-sunken p-4 text-sm text-text-muted\">\n <p>\n <span className=\"font-medium text-text-primary\">Version:</span>{' '}\n {installation.version ?? 'Not provided'}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Installed at:</span>{' '}\n {new Date(installation.installedAt).toLocaleString()}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Updated at:</span>{' '}\n {new Date(installation.updatedAt).toLocaleString()}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Workspace ID:</span>{' '}\n {installation.workspaceId}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Organization ID:</span>{' '}\n {installation.organizationId ?? 'N/A'}\n </p>\n <p className=\"mt-2\">\n <span className=\"font-medium text-text-primary\">Installed by:</span>{' '}\n {installation.installedById}\n </p>\n </div>\n\n <div className=\"rounded-lg bg-bg-sunken p-4 text-sm text-text-muted\">\n <p className=\"font-medium text-text-primary\">Runtime metadata</p>\n <pre className=\"mt-3 overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-surface p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata,\n context: installation.context,\n uninstalledAt: installation.uninstalledAt,\n uninstalledById: installation.uninstalledById,\n },\n null,\n 2\n )}\n </pre>\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}/settings`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Settings className=\"size-4\" />\n {t('pages.appDetail.manageConfig', { defaultValue: 'Manage Configuration' })}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -14,10 +14,11 @@ var l = () => {
14
14
  children: /* @__PURE__ */ o("div", {
15
15
  className: "mx-auto flex max-w-4xl items-center gap-3",
16
16
  children: [/* @__PURE__ */ a("button", {
17
+ type: "button",
17
18
  onClick: () => l(-1),
18
19
  "aria-label": "Go back",
19
20
  className: "cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary",
20
- children: /* @__PURE__ */ a(r, { className: "h-5 w-5" })
21
+ children: /* @__PURE__ */ a(r, { className: "size-5" })
21
22
  }), /* @__PURE__ */ o("div", { children: [/* @__PURE__ */ a("h1", {
22
23
  className: "text-2xl font-bold text-text-primary",
23
24
  children: h("pages.appSettings.title", { defaultValue: "App Settings" })
@@ -33,7 +34,7 @@ var l = () => {
33
34
  children: p ? /* @__PURE__ */ a("p", {
34
35
  role: "status",
35
36
  className: "text-sm text-text-muted",
36
- children: "Loading settings..."
37
+ children: "Loading settings"
37
38
  }) : m ? /* @__PURE__ */ a("p", {
38
39
  role: "alert",
39
40
  className: "text-sm text-status-error-text",
@@ -43,7 +44,7 @@ var l = () => {
43
44
  children: [
44
45
  /* @__PURE__ */ o("div", {
45
46
  className: "flex items-center gap-2",
46
- children: [/* @__PURE__ */ a(i, { className: "h-5 w-5 text-text-primary" }), /* @__PURE__ */ a("h2", {
47
+ children: [/* @__PURE__ */ a(i, { className: "size-5 text-text-primary" }), /* @__PURE__ */ a("h2", {
47
48
  className: "text-lg font-semibold text-text-primary",
48
49
  children: h("pages.appSettings.configSnapshot", { defaultValue: "Configuration snapshot" })
49
50
  })]
@@ -76,10 +77,12 @@ var l = () => {
76
77
  /* @__PURE__ */ o("div", {
77
78
  className: "flex flex-wrap gap-3",
78
79
  children: [/* @__PURE__ */ a("button", {
80
+ type: "button",
79
81
  onClick: () => l(`${u}/installed/${f.id}`),
80
82
  className: "inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
81
83
  children: h("pages.appSettings.viewInstallation", { defaultValue: "View Installation" })
82
84
  }), /* @__PURE__ */ a("button", {
85
+ type: "button",
83
86
  onClick: () => l(`${u}/marketplace/product/${f.productId}`),
84
87
  className: "inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90",
85
88
  children: h("pages.appDetail.viewProduct", { defaultValue: "View Product" })
@@ -1 +1 @@
1
- {"version":3,"file":"AppSettingsPage.js","names":[],"sources":["../../src/pages/AppSettingsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Settings } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const AppSettingsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\n <button\n onClick={() => navigate(-1)}\n aria-label=\"Go back\"\n className=\"cursor-pointer rounded-lg p-2 text-text-muted hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-5 w-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appSettings.title', { defaultValue: 'App Settings' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.subtitle', {\n defaultValue: 'Review current installation configuration',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading settings...\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"space-y-4 rounded-xl border border-border-default bg-bg-surface p-5\">\n <div className=\"flex items-center gap-2\">\n <Settings className=\"h-5 w-5 text-text-primary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.appSettings.configSnapshot', {\n defaultValue: 'Configuration snapshot',\n })}\n </h2>\n </div>\n\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.configDescription', {\n defaultValue:\n 'Store-level configuration for this installation is read-only here.',\n })}\n </p>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Manifest</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(installation.manifest ?? {}, null, 2)}\n </pre>\n </div>\n\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Metadata & Context</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata ?? {},\n context: installation.context ?? {},\n version: installation.version,\n status: installation.status,\n },\n null,\n 2\n )}\n </pre>\n </div>\n </div>\n\n <div className=\"flex flex-wrap gap-3\">\n <button\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {t('pages.appSettings.viewInstallation', { defaultValue: 'View Installation' })}\n </button>\n <button\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,UAA4B;CACvC,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAU,EACzB,EAAE,sBAAmB,GAAuC,EAC5D,EAAE,iBAAc,YAAS,aAAU,EAAgB,EAAe,EAClE,EAAE,SAAM,GAAS;AAEvB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GAAQ,WAAU;aAChB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,eAAe,EAAS,GAAG;KAC3B,cAAW;KACX,WAAU;eAEV,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;KAC1B,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,2BAA2B,EAAE,cAAc,gBAAgB,CAAC;KAC5D,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,8BAA8B,EAC/B,cAAc,6CACf,CAAC;KACA,CAAA,CACA,EAAA,CAAA,CACF;;GACC,CAAA,EAET,kBAAC,QAAD;GAAM,WAAU;aACd,kBAAC,OAAD;IAAK,WAAU;cACZ,IACC,kBAAC,KAAD;KAAG,MAAK;KAAS,WAAU;eAA0B;KAEjD,CAAA,GACF,IACF,kBAAC,KAAD;KAAG,MAAK;KAAQ,WAAU;eACvB,EAAM;KACL,CAAA,GACD,IAGH,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,6BAA8B,CAAA,EAClD,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,oCAAoC,EACrC,cAAc,0BACf,CAAC;QACC,CAAA,CACD;;MAEN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,uCAAuC,EACxC,cACE,sEACH,CAAC;OACA,CAAA;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAY,CAAA,EACtE,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UAAU,EAAa,YAAY,EAAE,EAAE,MAAM,EAAE;QACjD,CAAA,CACF,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAsB,CAAA,EAChF,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UACJ;SACE,UAAU,EAAa,YAAY,EAAE;SACrC,SAAS,EAAa,WAAW,EAAE;SACnC,SAAS,EAAa;SACtB,QAAQ,EAAa;SACtB,EACD,MACA,EACD;QACG,CAAA,CACF,EAAA,CAAA,CACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,eAAe,EAAS,GAAG,EAAS,aAAa,EAAa,KAAK;QACnE,WAAU;kBAET,EAAE,sCAAsC,EAAE,cAAc,qBAAqB,CAAC;QACxE,CAAA,EACT,kBAAC,UAAD;QACE,eACE,EAAS,GAAG,EAAS,uBAAuB,EAAa,YAAY;QAEvE,WAAU;kBAET,EAAE,+BAA+B,EAAE,cAAc,gBAAgB,CAAC;QAC5D,CAAA,CACL;;MACF;SA5DN,kBAAC,KAAD;KAAG,WAAU;eAA0B;KAA2B,CAAA;IA8DhE,CAAA;GACD,CAAA,CACH"}
1
+ {"version":3,"file":"AppSettingsPage.js","names":[],"sources":["../../src/pages/AppSettingsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { ArrowLeft, Settings } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useInstallation } from '../hooks/useInstallations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const AppSettingsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { installationId } = useParams<{ installationId: string }>();\n const { installation, loading, error } = useInstallation(installationId);\n const { t } = useI18n();\n\n return (\n <div className=\"flex h-full flex-col\">\n <header className=\"border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto flex max-w-4xl items-center gap-3\">\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 hover:bg-bg-sunken hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-5\" />\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.appSettings.title', { defaultValue: 'App Settings' })}\n </h1>\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.subtitle', {\n defaultValue: 'Review current installation configuration',\n })}\n </p>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-4xl\">\n {loading ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Loading settings…\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : !installation ? (\n <p className=\"text-sm text-text-muted\">Installation not found.</p>\n ) : (\n <div className=\"space-y-4 rounded-xl border border-border-default bg-bg-surface p-5\">\n <div className=\"flex items-center gap-2\">\n <Settings className=\"size-5 text-text-primary\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.appSettings.configSnapshot', {\n defaultValue: 'Configuration snapshot',\n })}\n </h2>\n </div>\n\n <p className=\"text-sm text-text-muted\">\n {t('pages.appSettings.configDescription', {\n defaultValue:\n 'Store-level configuration for this installation is read-only here.',\n })}\n </p>\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Manifest</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(installation.manifest ?? {}, null, 2)}\n </pre>\n </div>\n\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">Metadata & Context</p>\n <pre className=\"overflow-x-auto whitespace-pre-wrap rounded-lg bg-bg-sunken p-3 text-xs text-text-muted\">\n {JSON.stringify(\n {\n metadata: installation.metadata ?? {},\n context: installation.context ?? {},\n version: installation.version,\n status: installation.status,\n },\n null,\n 2\n )}\n </pre>\n </div>\n </div>\n\n <div className=\"flex flex-wrap gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/installed/${installation.id}`)}\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border-default px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n {t('pages.appSettings.viewInstallation', { defaultValue: 'View Installation' })}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n navigate(`${basePath}/marketplace/product/${installation.productId}`)\n }\n className=\"inline-flex cursor-pointer items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text hover:opacity-90\"\n >\n {t('pages.appDetail.viewProduct', { defaultValue: 'View Product' })}\n </button>\n </div>\n </div>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAOA,IAAa,UAA4B;CACvC,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAU,EACzB,EAAE,sBAAmB,GAAuC,EAC5D,EAAE,iBAAc,YAAS,aAAU,EAAgB,EAAe,EAClE,EAAE,SAAM,GAAS;AAEvB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GAAQ,WAAU;aAChB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAS,GAAG;KAC3B,cAAW;KACX,WAAU;eAEV,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;KACzB,CAAA,EACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,2BAA2B,EAAE,cAAc,gBAAgB,CAAC;KAC5D,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,8BAA8B,EAC/B,cAAc,6CACf,CAAC;KACA,CAAA,CACA,EAAA,CAAA,CACF;;GACC,CAAA,EAET,kBAAC,QAAD;GAAM,WAAU;aACd,kBAAC,OAAD;IAAK,WAAU;cACZ,IACC,kBAAC,KAAD;KAAG,MAAK;KAAS,WAAU;eAA0B;KAEjD,CAAA,GACF,IACF,kBAAC,KAAD;KAAG,MAAK;KAAQ,WAAU;eACvB,EAAM;KACL,CAAA,GACD,IAGH,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA,EACjD,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAE,oCAAoC,EACrC,cAAc,0BACf,CAAC;QACC,CAAA,CACD;;MAEN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,uCAAuC,EACxC,cACE,sEACH,CAAC;OACA,CAAA;MAEJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAY,CAAA,EACtE,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UAAU,EAAa,YAAY,EAAE,EAAE,MAAM,EAAE;QACjD,CAAA,CACF,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA6C;QAAsB,CAAA,EAChF,kBAAC,OAAD;QAAK,WAAU;kBACZ,KAAK,UACJ;SACE,UAAU,EAAa,YAAY,EAAE;SACrC,SAAS,EAAa,WAAW,EAAE;SACnC,SAAS,EAAa;SACtB,QAAQ,EAAa;SACtB,EACD,MACA,EACD;QACG,CAAA,CACF,EAAA,CAAA,CACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAS,GAAG,EAAS,aAAa,EAAa,KAAK;QACnE,WAAU;kBAET,EAAE,sCAAsC,EAAE,cAAc,qBAAqB,CAAC;QACxE,CAAA,EACT,kBAAC,UAAD;QACE,MAAK;QACL,eACE,EAAS,GAAG,EAAS,uBAAuB,EAAa,YAAY;QAEvE,WAAU;kBAET,EAAE,+BAA+B,EAAE,cAAc,gBAAgB,CAAC;QAC5D,CAAA,CACL;;MACF;SA9DN,kBAAC,KAAD;KAAG,WAAU;eAA0B;KAA2B,CAAA;IAgEhE,CAAA;GACD,CAAA,CACH"}
@@ -56,14 +56,15 @@ var x = () => {
56
56
  children: /* @__PURE__ */ g("div", {
57
57
  className: "flex items-center gap-4",
58
58
  children: [/* @__PURE__ */ h("button", {
59
+ type: "button",
59
60
  onClick: () => x(-1),
60
61
  "aria-label": "Go back",
61
62
  className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-bg-sunken hover:text-text-primary",
62
- children: /* @__PURE__ */ h(a, { className: "h-5 w-5" })
63
+ children: /* @__PURE__ */ h(a, { className: "size-5" })
63
64
  }), /* @__PURE__ */ g("div", {
64
65
  className: "flex items-center gap-3",
65
66
  children: [
66
- /* @__PURE__ */ h(u, { className: "h-6 w-6 text-text-primary" }),
67
+ /* @__PURE__ */ h(u, { className: "size-6 text-text-primary" }),
67
68
  /* @__PURE__ */ h("h1", {
68
69
  className: "text-xl font-bold text-text-primary",
69
70
  children: P("cart.title", { defaultValue: "Shopping Cart" })
@@ -87,13 +88,13 @@ var x = () => {
87
88
  })
88
89
  })
89
90
  }), /* @__PURE__ */ h("main", {
90
- className: "mx-auto max-w-6xl px-6 py-6",
91
+ className: "mx-auto max-w-6xl p-6",
91
92
  children: C.length === 0 ? /* @__PURE__ */ g("div", {
92
93
  className: "flex flex-col items-center justify-center py-16 text-center",
93
94
  children: [
94
95
  /* @__PURE__ */ h("div", {
95
96
  className: "mb-6 rounded-full bg-bg-sunken p-6",
96
- children: /* @__PURE__ */ h(l, { className: "h-16 w-16 text-text-muted" })
97
+ children: /* @__PURE__ */ h(l, { className: "size-16 text-text-muted" })
97
98
  }),
98
99
  /* @__PURE__ */ h("h2", {
99
100
  className: "mb-2 text-2xl font-bold text-text-primary",
@@ -104,9 +105,10 @@ var x = () => {
104
105
  children: P("pages.cart.emptyDescription", { defaultValue: "Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!" })
105
106
  }),
106
107
  /* @__PURE__ */ g("button", {
108
+ type: "button",
107
109
  onClick: R,
108
110
  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",
109
- children: [/* @__PURE__ */ h(l, { className: "h-5 w-5" }), P("pages.cart.browseMarketplace", { defaultValue: "Browse Marketplace" })]
111
+ children: [/* @__PURE__ */ h(l, { className: "size-5" }), P("pages.cart.browseMarketplace", { defaultValue: "Browse Marketplace" })]
110
112
  })
111
113
  ]
112
114
  }) : /* @__PURE__ */ h("div", {
@@ -121,9 +123,10 @@ var x = () => {
121
123
  count: w
122
124
  })
123
125
  }), /* @__PURE__ */ g("button", {
126
+ type: "button",
124
127
  onClick: O,
125
128
  className: "cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text",
126
- children: [/* @__PURE__ */ h(f, { className: "h-4 w-4" }), P("cart.clearCart", { defaultValue: "Clear Cart" })]
129
+ children: [/* @__PURE__ */ h(f, { className: "size-4" }), P("cart.clearCart", { defaultValue: "Clear Cart" })]
127
130
  })]
128
131
  }),
129
132
  /* @__PURE__ */ h("ul", {
@@ -131,13 +134,13 @@ var x = () => {
131
134
  children: C.map((e) => /* @__PURE__ */ g("li", {
132
135
  className: "flex gap-4 rounded-xl border border-border-default bg-bg-surface p-4 transition-shadow hover:shadow-md",
133
136
  children: [/* @__PURE__ */ h("div", {
134
- className: "h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
137
+ className: "size-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken",
135
138
  children: e.product?.iconUrl ? /* @__PURE__ */ h("img", {
136
139
  src: e.product.iconUrl,
137
140
  alt: e.product.displayName,
138
- className: "h-full w-full object-cover"
141
+ className: "size-full object-cover"
139
142
  }) : /* @__PURE__ */ h("div", {
140
- className: "flex h-full w-full items-center justify-center text-2xl font-bold text-text-muted",
143
+ className: "flex size-full items-center justify-center text-2xl font-bold text-text-muted",
141
144
  children: e.product?.displayName?.charAt(0) || "P"
142
145
  })
143
146
  }), /* @__PURE__ */ g("div", {
@@ -159,18 +162,19 @@ var x = () => {
159
162
  children: ["Version ", e.variant.version]
160
163
  })
161
164
  ] }), /* @__PURE__ */ h("button", {
165
+ type: "button",
162
166
  onClick: () => D(e.id),
163
167
  className: "cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text",
164
168
  "aria-label": `Remove ${e.product?.displayName ?? "item"} from cart`,
165
169
  title: "Remove item",
166
- children: /* @__PURE__ */ h(f, { className: "h-5 w-5" })
170
+ children: /* @__PURE__ */ h(f, { className: "size-5" })
167
171
  })]
168
172
  }),
169
173
  /* @__PURE__ */ g("div", {
170
174
  className: "mt-2 flex items-center gap-2",
171
175
  children: [/* @__PURE__ */ h("span", {
172
176
  className: "inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted",
173
- children: e.itemType === "digital" ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(p, { className: "h-3 w-3" }), P("pages.cart.digital", { defaultValue: "Digital" })] }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(d, { className: "h-3 w-3" }), P("pages.cart.physical", { defaultValue: "Physical" })] })
177
+ children: e.itemType === "digital" ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(p, { className: "size-3" }), P("pages.cart.digital", { defaultValue: "Digital" })] }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(d, { className: "size-3" }), P("pages.cart.physical", { defaultValue: "Physical" })] })
174
178
  }), e.product?.pricingModel === "SUBSCRIPTION" && /* @__PURE__ */ h("span", {
175
179
  className: "inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link",
176
180
  children: P("store.pricingModels.subscription", { defaultValue: "Subscription" })
@@ -187,19 +191,21 @@ var x = () => {
187
191
  className: "flex items-center rounded-lg border border-border-default",
188
192
  children: [
189
193
  /* @__PURE__ */ h("button", {
194
+ type: "button",
190
195
  onClick: () => E(e.id, e.quantity - 1),
191
196
  className: "cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50",
192
197
  disabled: e.quantity <= 1,
193
- children: /* @__PURE__ */ h(s, { className: "h-4 w-4" })
198
+ children: /* @__PURE__ */ h(s, { className: "size-4" })
194
199
  }),
195
200
  /* @__PURE__ */ h("span", {
196
201
  className: "min-w-[3rem] text-center font-medium",
197
202
  children: e.quantity
198
203
  }),
199
204
  /* @__PURE__ */ h("button", {
205
+ type: "button",
200
206
  onClick: () => E(e.id, e.quantity + 1),
201
207
  className: "cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken",
202
- children: /* @__PURE__ */ h(c, { className: "h-4 w-4" })
208
+ children: /* @__PURE__ */ h(c, { className: "size-4" })
203
209
  })
204
210
  ]
205
211
  })]
@@ -236,17 +242,19 @@ var x = () => {
236
242
  className: "text-2xl font-bold text-text-primary",
237
243
  children: n(T)
238
244
  })] }), /* @__PURE__ */ h("button", {
245
+ type: "button",
239
246
  onClick: L,
240
247
  disabled: A,
241
248
  className: "flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70",
242
- children: A ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(o, { className: "h-4 w-4 animate-spin" }), P("pages.cart.processing", { defaultValue: "Processing..." })] }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(p, { className: "h-4 w-4" }), P("pages.cart.proceedToCheckout", { defaultValue: "Proceed to Checkout" })] })
249
+ children: A ? /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(o, { className: "size-4 animate-spin" }), P("pages.cart.processing", { defaultValue: "Processing..." })] }) : /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h(p, { className: "size-4" }), P("pages.cart.proceedToCheckout", { defaultValue: "Proceed to Checkout" })] })
243
250
  })]
244
251
  })]
245
252
  }),
246
253
  /* @__PURE__ */ g("button", {
254
+ type: "button",
247
255
  onClick: R,
248
256
  className: "cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline",
249
- children: [/* @__PURE__ */ h(a, { className: "h-4 w-4" }), P("cart.continueShopping", { defaultValue: "Continue Shopping" })]
257
+ children: [/* @__PURE__ */ h(a, { className: "size-4" }), P("cart.continueShopping", { defaultValue: "Continue Shopping" })]
250
258
  })
251
259
  ] })
252
260
  })
@@ -1 +1 @@
1
- {"version":3,"file":"CartPage.js","names":[],"sources":["../../src/pages/CartPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useQuotaErrorToast } from '@burdenoff/fe-libs/shared/hooks';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport {\n ShoppingCart,\n Trash2,\n Plus,\n Minus,\n ArrowLeft,\n ShoppingBag,\n Zap,\n Tag,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { formatPrice } from '../utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * CartPage component\n *\n * Full page cart view with items, summary, and checkout\n */\nexport const CartPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { cartItems, itemCount, total, updateQuantity, removeProduct, clearCart, createOrder } =\n useCart();\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n const [checkoutError, setCheckoutError] = useState<string | null>(null);\n const { t } = useI18n();\n const bus = useEventBus();\n // Surface QUOTA_EXHAUSTED gateway errors as a toast (e.g. when the\n // workspace has hit its store-purchases-per-month limit). The hook also\n // listens for the global `burdenoff:quota-exhausted` event so any other\n // mutation in the page that triggers it will toast as well.\n const { report: reportQuotaError } = useQuotaErrorToast();\n\n const handleCheckout = async () => {\n try {\n setIsCheckingOut(true);\n setCheckoutError(null);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.cart.checkout_started' as any, {\n cartId: '',\n itemCount,\n total,\n });\n\n // Build line items from cart\n const lineItems = cartItems.map((item) => ({\n productId: item.productId,\n name: item.product?.displayName || 'Product',\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n subtotal: item.totalPrice,\n itemType: item.itemType.toLowerCase(),\n variantId: item.variantId,\n pricingModel: item.product?.pricingModel || 'PAID_ONETIME',\n }));\n\n // Create order input\n const orderInput = {\n total,\n lineItems,\n billingAccountId: '', // Will be set on billing page\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.order.placed' as any, {\n orderId: order.id,\n total,\n itemCount,\n });\n // Clear cart now that the order is created — non-blocking so a\n // clear failure never prevents the user from reaching billing.\n clearCart().catch(() => {});\n // Navigate to billing page with the order ID\n navigate(`/billing/checkout/store/${order.id}`);\n } else {\n setCheckoutError(\n t('pages.cart.failedToCreate', {\n defaultValue: 'Failed to create order. Please try again.',\n })\n );\n setIsCheckingOut(false);\n }\n } catch (error) {\n console.error('Checkout error:', error);\n // Quota-exhausted errors surface a global toast via the shell's\n // GlobalUiProvider; suppress the inline checkout error so we don't\n // stack two error UIs on top of each other.\n if (isQuotaExhaustedError(error)) {\n reportQuotaError(error);\n setIsCheckingOut(false);\n return;\n }\n setCheckoutError(\n error instanceof Error ? error.message : 'An error occurred during checkout'\n );\n setIsCheckingOut(false);\n }\n };\n\n const handleContinueShopping = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n return (\n <div className=\"h-full overflow-y-auto\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\n <div className=\"flex items-center gap-4\">\n <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=\"h-5 w-5\" />\n </button>\n <div className=\"flex items-center gap-3\">\n <ShoppingCart className=\"h-6 w-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('cart.title', { defaultValue: 'Shopping Cart' })}\n </h1>\n {itemCount > 0 && (\n <span className=\"rounded-full bg-action-primary-bg px-2.5 py-0.5 text-sm font-medium text-action-primary-text\">\n {itemCount}{' '}\n {itemCount === 1\n ? t('pages.cart.item', { defaultValue: `${itemCount} item`, count: itemCount })\n : t('pages.cart.items', {\n defaultValue: `${itemCount} items`,\n count: itemCount,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl px-6 py-6\">\n {cartItems.length === 0 ? (\n // Empty Cart State\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <div className=\"mb-6 rounded-full bg-bg-sunken p-6\">\n <ShoppingBag className=\"h-16 w-16 text-text-muted\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('cart.empty', { defaultValue: 'Your cart is empty' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {t('pages.cart.emptyDescription', {\n defaultValue:\n 'Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!',\n })}\n </p>\n <button\n onClick={handleContinueShopping}\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=\"h-5 w-5\" />\n {t('pages.cart.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n </div>\n ) : (\n // Cart with Items\n <div className=\"mx-auto max-w-3xl\">\n {/* Cart Items */}\n <div>\n {/* Cart Actions */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.cart.cartItems', {\n defaultValue: `Cart Items (${itemCount})`,\n count: itemCount,\n })}\n </h2>\n <button\n onClick={clearCart}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text\"\n >\n <Trash2 className=\"h-4 w-4\" />\n {t('cart.clearCart', { defaultValue: 'Clear Cart' })}\n </button>\n </div>\n\n {/* Items List */}\n <ul className=\"space-y-4\">\n {cartItems.map((item) => (\n <li\n key={item.id}\n className=\"flex gap-4 rounded-xl border border-border-default bg-bg-surface p-4 transition-shadow hover:shadow-md\"\n >\n {/* Product Image */}\n <div className=\"h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.product?.iconUrl ? (\n <img\n src={item.product.iconUrl}\n alt={item.product.displayName}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center text-2xl font-bold text-text-muted\">\n {item.product?.displayName?.charAt(0) || 'P'}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {item.product?.displayName || 'Product'}\n </h3>\n <p className=\"text-sm text-text-muted\">\n {item.product?.publisher?.displayName || 'Unknown Publisher'}\n </p>\n {item.variant && (\n <p className=\"mt-1 text-xs text-text-muted\">\n Version {item.variant.version}\n </p>\n )}\n </div>\n <button\n onClick={() => removeProduct(item.id)}\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text\"\n aria-label={`Remove ${item.product?.displayName ?? 'item'} from cart`}\n title=\"Remove item\"\n >\n <Trash2 className=\"h-5 w-5\" />\n </button>\n </div>\n\n {/* Item Type Badge */}\n <div className=\"mt-2 flex items-center gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.itemType === 'digital' ? (\n <>\n <Zap className=\"h-3 w-3\" />\n {t('pages.cart.digital', { defaultValue: 'Digital' })}\n </>\n ) : (\n <>\n <Tag className=\"h-3 w-3\" />\n {t('pages.cart.physical', { defaultValue: 'Physical' })}\n </>\n )}\n </span>\n {item.product?.pricingModel === 'SUBSCRIPTION' && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link\">\n {t('store.pricingModels.subscription', {\n defaultValue: 'Subscription',\n })}\n </span>\n )}\n </div>\n\n {/* Quantity and Price Row */}\n <div className=\"mt-auto flex items-center justify-between pt-3\">\n {/* Quantity Controls - Only show for physical items */}\n {item.itemType === 'physical' ? (\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm text-text-muted\">Qty:</span>\n <div className=\"flex items-center rounded-lg border border-border-default\">\n <button\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n className=\"cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"h-4 w-4\" />\n </button>\n <span className=\"min-w-[3rem] text-center font-medium\">\n {item.quantity}\n </span>\n <button\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n className=\"cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ) : (\n <div className=\"text-sm text-text-muted\">\n {t('pages.cart.singleLicense', { defaultValue: 'Single license' })}\n </div>\n )}\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"text-lg font-bold text-text-primary\">\n {formatPrice(item.totalPrice)}\n </p>\n {item.quantity > 1 && item.itemType === 'physical' && (\n <p className=\"text-xs text-text-muted\">\n {formatPrice(item.unitPrice)} each\n </p>\n )}\n </div>\n </div>\n </div>\n </li>\n ))}\n </ul>\n\n {/* Checkout Footer */}\n <div className=\"mt-8 rounded-xl border border-border-default bg-bg-surface p-5\">\n {checkoutError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {checkoutError}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-text-muted\">Total</p>\n <p className=\"text-2xl font-bold text-text-primary\">{formatPrice(total)}</p>\n </div>\n <button\n onClick={handleCheckout}\n disabled={isCheckingOut}\n className=\"flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {isCheckingOut ? (\n <>\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n {t('pages.cart.processing', { defaultValue: 'Processing...' })}\n </>\n ) : (\n <>\n <Zap className=\"h-4 w-4\" />\n {t('pages.cart.proceedToCheckout', { defaultValue: 'Proceed to Checkout' })}\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Continue Shopping */}\n <button\n onClick={handleContinueShopping}\n className=\"cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('cart.continueShopping', { defaultValue: 'Continue Shopping' })}\n </button>\n </div>\n </div>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CartPage.js","names":[],"sources":["../../src/pages/CartPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useQuotaErrorToast } from '@burdenoff/fe-libs/shared/hooks';\nimport { isQuotaExhaustedError } from '@burdenoff/fe-libs/shared/utils';\nimport {\n ShoppingCart,\n Trash2,\n Plus,\n Minus,\n ArrowLeft,\n ShoppingBag,\n Zap,\n Tag,\n Loader2,\n} from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useCart } from '../hooks/useCart';\nimport { formatPrice } from '../utils';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * CartPage component\n *\n * Full page cart view with items, summary, and checkout\n */\nexport const CartPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const { cartItems, itemCount, total, updateQuantity, removeProduct, clearCart, createOrder } =\n useCart();\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n const [checkoutError, setCheckoutError] = useState<string | null>(null);\n const { t } = useI18n();\n const bus = useEventBus();\n // Surface QUOTA_EXHAUSTED gateway errors as a toast (e.g. when the\n // workspace has hit its store-purchases-per-month limit). The hook also\n // listens for the global `burdenoff:quota-exhausted` event so any other\n // mutation in the page that triggers it will toast as well.\n const { report: reportQuotaError } = useQuotaErrorToast();\n\n const handleCheckout = async () => {\n try {\n setIsCheckingOut(true);\n setCheckoutError(null);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.cart.checkout_started' as any, {\n cartId: '',\n itemCount,\n total,\n });\n\n // Build line items from cart\n const lineItems = cartItems.map((item) => ({\n productId: item.productId,\n name: item.product?.displayName || 'Product',\n quantity: item.quantity,\n unitPrice: item.unitPrice,\n subtotal: item.totalPrice,\n itemType: item.itemType.toLowerCase(),\n variantId: item.variantId,\n pricingModel: item.product?.pricingModel || 'PAID_ONETIME',\n }));\n\n // Create order input\n const orderInput = {\n total,\n lineItems,\n billingAccountId: '', // Will be set on billing page\n };\n\n // Call createStoreOrder mutation to create PENDING order\n const order = await createOrder(orderInput);\n\n if (order?.id) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bus.emit('store.order.placed' as any, {\n orderId: order.id,\n total,\n itemCount,\n });\n // Clear cart now that the order is created — non-blocking so a\n // clear failure never prevents the user from reaching billing.\n clearCart().catch(() => {});\n // Navigate to billing page with the order ID\n navigate(`/billing/checkout/store/${order.id}`);\n } else {\n setCheckoutError(\n t('pages.cart.failedToCreate', {\n defaultValue: 'Failed to create order. Please try again.',\n })\n );\n setIsCheckingOut(false);\n }\n } catch (error) {\n console.error('Checkout error:', error);\n // Quota-exhausted errors surface a global toast via the shell's\n // GlobalUiProvider; suppress the inline checkout error so we don't\n // stack two error UIs on top of each other.\n if (isQuotaExhaustedError(error)) {\n reportQuotaError(error);\n setIsCheckingOut(false);\n return;\n }\n setCheckoutError(\n error instanceof Error ? error.message : 'An error occurred during checkout'\n );\n setIsCheckingOut(false);\n }\n };\n\n const handleContinueShopping = () => {\n navigate(`${basePath}/marketplace`);\n };\n\n return (\n <div className=\"h-full overflow-y-auto\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-border-default bg-bg-surface px-6 py-4\">\n <div className=\"mx-auto max-w-6xl\">\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 <ShoppingCart className=\"size-6 text-text-primary\" />\n <h1 className=\"text-xl font-bold text-text-primary\">\n {t('cart.title', { defaultValue: 'Shopping Cart' })}\n </h1>\n {itemCount > 0 && (\n <span className=\"rounded-full bg-action-primary-bg px-2.5 py-0.5 text-sm font-medium text-action-primary-text\">\n {itemCount}{' '}\n {itemCount === 1\n ? t('pages.cart.item', { defaultValue: `${itemCount} item`, count: itemCount })\n : t('pages.cart.items', {\n defaultValue: `${itemCount} items`,\n count: itemCount,\n })}\n </span>\n )}\n </div>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl p-6\">\n {cartItems.length === 0 ? (\n // Empty Cart State\n <div className=\"flex flex-col items-center justify-center py-16 text-center\">\n <div className=\"mb-6 rounded-full bg-bg-sunken p-6\">\n <ShoppingBag className=\"size-16 text-text-muted\" />\n </div>\n <h2 className=\"mb-2 text-2xl font-bold text-text-primary\">\n {t('cart.empty', { defaultValue: 'Your cart is empty' })}\n </h2>\n <p className=\"mb-6 max-w-md text-text-muted\">\n {t('pages.cart.emptyDescription', {\n defaultValue:\n 'Looks like you have not added any apps or products to your cart yet. Start exploring the marketplace to find something you will love!',\n })}\n </p>\n <button\n type=\"button\"\n onClick={handleContinueShopping}\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 {t('pages.cart.browseMarketplace', { defaultValue: 'Browse Marketplace' })}\n </button>\n </div>\n ) : (\n // Cart with Items\n <div className=\"mx-auto max-w-3xl\">\n {/* Cart Items */}\n <div>\n {/* Cart Actions */}\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('pages.cart.cartItems', {\n defaultValue: `Cart Items (${itemCount})`,\n count: itemCount,\n })}\n </h2>\n <button\n type=\"button\"\n onClick={clearCart}\n className=\"cursor-pointer flex items-center gap-1 text-sm text-text-muted transition-colors hover:text-status-error-text\"\n >\n <Trash2 className=\"size-4\" />\n {t('cart.clearCart', { defaultValue: 'Clear Cart' })}\n </button>\n </div>\n\n {/* Items List */}\n <ul className=\"space-y-4\">\n {cartItems.map((item) => (\n <li\n key={item.id}\n className=\"flex gap-4 rounded-xl border border-border-default bg-bg-surface p-4 transition-shadow hover:shadow-md\"\n >\n {/* Product Image */}\n <div className=\"size-24 flex-shrink-0 overflow-hidden rounded-lg bg-bg-sunken\">\n {item.product?.iconUrl ? (\n <img\n src={item.product.iconUrl}\n alt={item.product.displayName}\n className=\"size-full object-cover\"\n />\n ) : (\n <div className=\"flex size-full items-center justify-center text-2xl font-bold text-text-muted\">\n {item.product?.displayName?.charAt(0) || 'P'}\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"flex flex-1 flex-col\">\n <div className=\"flex items-start justify-between\">\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {item.product?.displayName || 'Product'}\n </h3>\n <p className=\"text-sm text-text-muted\">\n {item.product?.publisher?.displayName || 'Unknown Publisher'}\n </p>\n {item.variant && (\n <p className=\"mt-1 text-xs text-text-muted\">\n Version {item.variant.version}\n </p>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => removeProduct(item.id)}\n className=\"cursor-pointer rounded-lg p-2 text-text-muted transition-colors hover:bg-status-error-bg-subtle/10 hover:text-status-error-text\"\n aria-label={`Remove ${item.product?.displayName ?? 'item'} from cart`}\n title=\"Remove item\"\n >\n <Trash2 className=\"size-5\" />\n </button>\n </div>\n\n {/* Item Type Badge */}\n <div className=\"mt-2 flex items-center gap-2\">\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-muted\">\n {item.itemType === 'digital' ? (\n <>\n <Zap className=\"size-3\" />\n {t('pages.cart.digital', { defaultValue: 'Digital' })}\n </>\n ) : (\n <>\n <Tag className=\"size-3\" />\n {t('pages.cart.physical', { defaultValue: 'Physical' })}\n </>\n )}\n </span>\n {item.product?.pricingModel === 'SUBSCRIPTION' && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-accent/10 px-2 py-0.5 text-xs font-medium text-text-link\">\n {t('store.pricingModels.subscription', {\n defaultValue: 'Subscription',\n })}\n </span>\n )}\n </div>\n\n {/* Quantity and Price Row */}\n <div className=\"mt-auto flex items-center justify-between pt-3\">\n {/* Quantity Controls - Only show for physical items */}\n {item.itemType === 'physical' ? (\n <div className=\"flex items-center gap-3\">\n <span className=\"text-sm text-text-muted\">Qty:</span>\n <div className=\"flex items-center rounded-lg border border-border-default\">\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity - 1)}\n className=\"cursor-pointer rounded-l-lg p-2 transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-50\"\n disabled={item.quantity <= 1}\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"min-w-[3rem] text-center font-medium\">\n {item.quantity}\n </span>\n <button\n type=\"button\"\n onClick={() => updateQuantity(item.id, item.quantity + 1)}\n className=\"cursor-pointer rounded-r-lg p-2 transition-colors hover:bg-bg-sunken\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n </div>\n ) : (\n <div className=\"text-sm text-text-muted\">\n {t('pages.cart.singleLicense', { defaultValue: 'Single license' })}\n </div>\n )}\n\n {/* Price */}\n <div className=\"text-right\">\n <p className=\"text-lg font-bold text-text-primary\">\n {formatPrice(item.totalPrice)}\n </p>\n {item.quantity > 1 && item.itemType === 'physical' && (\n <p className=\"text-xs text-text-muted\">\n {formatPrice(item.unitPrice)} each\n </p>\n )}\n </div>\n </div>\n </div>\n </li>\n ))}\n </ul>\n\n {/* Checkout Footer */}\n <div className=\"mt-8 rounded-xl border border-border-default bg-bg-surface p-5\">\n {checkoutError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {checkoutError}\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm text-text-muted\">Total</p>\n <p className=\"text-2xl font-bold text-text-primary\">{formatPrice(total)}</p>\n </div>\n <button\n type=\"button\"\n onClick={handleCheckout}\n disabled={isCheckingOut}\n className=\"flex cursor-pointer 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 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {isCheckingOut ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {t('pages.cart.processing', { defaultValue: 'Processing...' })}\n </>\n ) : (\n <>\n <Zap className=\"size-4\" />\n {t('pages.cart.proceedToCheckout', { defaultValue: 'Proceed to Checkout' })}\n </>\n )}\n </button>\n </div>\n </div>\n\n {/* Continue Shopping */}\n <button\n type=\"button\"\n onClick={handleContinueShopping}\n className=\"cursor-pointer mt-4 flex items-center gap-2 text-sm font-medium text-text-link hover:underline\"\n >\n <ArrowLeft className=\"size-4\" />\n {t('cart.continueShopping', { defaultValue: 'Continue Shopping' })}\n </button>\n </div>\n </div>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}