@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":"SearchResultsPage.js","names":[],"sources":["../../src/pages/SearchResultsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useId } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { ArrowLeft, Search } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useBrowseStoreWithContext, type StoreProduct } from '../hooks/useStoreGraphQL';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nconst SearchResultCard: FC<{\n product: StoreProduct;\n onOpen: (id: string) => void;\n noDesc: string;\n}> = ({ product, onOpen, noDesc }) => (\n <button\n onClick={() => onOpen(product.id)}\n className=\"cursor-pointer rounded-xl border border-border-default bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken\"\n >\n <h3 className=\"font-semibold text-text-primary\">{product.name}</h3>\n <p className=\"mt-1 text-xs text-text-muted\">{product.type}</p>\n <p className=\"mt-2 text-sm text-text-muted line-clamp-2\">{product.description || noDesc}</p>\n </button>\n);\n\nexport const SearchResultsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const searchInputId = useId();\n const [searchParams, setSearchParams] = useSearchParams();\n const initialQuery = searchParams.get('q') ?? '';\n const [query, setQuery] = useState(initialQuery);\n const { t } = useI18n();\n\n const trimmedQuery = useMemo(() => query.trim(), [query]);\n const { products, totalCount, hasMore, loading, error, loadMore } = useBrowseStoreWithContext({\n filter: trimmedQuery ? { search: trimmedQuery } : undefined,\n pagination: { limit: 30 },\n });\n\n const handleSearch = () => {\n const next = query.trim();\n if (next) {\n setSearchParams({ q: next });\n } else {\n setSearchParams({});\n }\n };\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 max-w-6xl\">\n <div className=\"mb-3 flex 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 <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.search.title', { defaultValue: 'Search Results' })}\n </h1>\n </div>\n <div className=\"flex gap-2\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {t('pages.search.searchLabel', { defaultValue: 'Search store products' })}\n </label>\n <input\n id={searchInputId}\n aria-label={t('pages.search.searchLabel', { defaultValue: 'Search store products' })}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleSearch();\n }}\n placeholder=\"Search apps, workflows, integrations...\"\n className=\"w-full rounded-lg border border-border-default bg-bg-sunken px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-bg\"\n />\n <button\n onClick={handleSearch}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Search className=\"h-4 w-4\" /> Search\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <p aria-live=\"polite\" className=\"mb-4 text-sm text-text-muted\">\n {trimmedQuery\n ? t('pages.search.resultsFor', {\n defaultValue: `${totalCount} results for \"${trimmedQuery}\"`,\n query: trimmedQuery,\n count: totalCount,\n })\n : t('pages.search.enterQuery', { defaultValue: 'Enter a query to search' })}\n </p>\n {loading && products.length === 0 ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Searching...\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : products.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No matching products found.</p>\n ) : (\n <>\n <ul className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {products.map((product) => (\n <li key={product.id}>\n <SearchResultCard\n product={product}\n noDesc={t('common.noResults', { defaultValue: 'No description available.' })}\n onOpen={(id) => navigate(`${basePath}/marketplace/product/${id}`)}\n />\n </li>\n ))}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-4\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50\"\n >\n {loading ? 'Loading\\u2026' : 'Load more'}\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"SearchResultsPage.js","names":[],"sources":["../../src/pages/SearchResultsPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useMemo, useState, useId } from 'react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { ArrowLeft, Search } from 'lucide-react';\nimport { useStore } from '../providers/StoreProvider';\nimport { useBrowseStoreWithContext, type StoreProduct } from '../hooks/useStoreGraphQL';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nconst SearchResultCard: FC<{\n product: StoreProduct;\n onOpen: (id: string) => void;\n noDesc: string;\n}> = ({ product, onOpen, noDesc }) => (\n <button\n type=\"button\"\n onClick={() => onOpen(product.id)}\n className=\"cursor-pointer rounded-xl border border-border-default bg-bg-surface p-4 text-left transition-colors hover:bg-bg-sunken\"\n >\n <h3 className=\"font-semibold text-text-primary\">{product.name}</h3>\n <p className=\"mt-1 text-xs text-text-muted\">{product.type}</p>\n <p className=\"mt-2 text-sm text-text-muted line-clamp-2\">{product.description || noDesc}</p>\n </button>\n);\n\nexport const SearchResultsPage: FC = () => {\n const navigate = useNavigate();\n const { basePath } = useStore();\n const searchInputId = useId();\n const [searchParams, setSearchParams] = useSearchParams();\n const initialQuery = searchParams.get('q') ?? '';\n const [query, setQuery] = useState(initialQuery);\n const { t } = useI18n();\n\n const trimmedQuery = useMemo(() => query.trim(), [query]);\n const { products, totalCount, hasMore, loading, error, loadMore } = useBrowseStoreWithContext({\n filter: trimmedQuery ? { search: trimmedQuery } : undefined,\n pagination: { limit: 30 },\n });\n\n const handleSearch = () => {\n const next = query.trim();\n if (next) {\n setSearchParams({ q: next });\n } else {\n setSearchParams({});\n }\n };\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 max-w-6xl\">\n <div className=\"mb-3 flex 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 <h1 className=\"text-2xl font-bold text-text-primary\">\n {t('pages.search.title', { defaultValue: 'Search Results' })}\n </h1>\n </div>\n <div className=\"flex gap-2\">\n <label htmlFor={searchInputId} className=\"sr-only\">\n {t('pages.search.searchLabel', { defaultValue: 'Search store products' })}\n </label>\n <input\n id={searchInputId}\n aria-label={t('pages.search.searchLabel', { defaultValue: 'Search store products' })}\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleSearch();\n }}\n placeholder=\"Search apps, workflows, integrations...\"\n className=\"w-full rounded-lg border border-border-default bg-bg-sunken px-3 py-2 text-sm text-text-primary outline-none focus:border-action-primary-bg\"\n />\n <button\n type=\"button\"\n onClick={handleSearch}\n className=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-semibold text-action-primary-text\"\n >\n <Search className=\"size-4\" /> Search\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"mx-auto max-w-6xl\">\n <p aria-live=\"polite\" className=\"mb-4 text-sm text-text-muted\">\n {trimmedQuery\n ? t('pages.search.resultsFor', {\n defaultValue: `${totalCount} results for \"${trimmedQuery}\"`,\n query: trimmedQuery,\n count: totalCount,\n })\n : t('pages.search.enterQuery', { defaultValue: 'Enter a query to search' })}\n </p>\n {loading && products.length === 0 ? (\n <p role=\"status\" className=\"text-sm text-text-muted\">\n Searching…\n </p>\n ) : error ? (\n <p role=\"alert\" className=\"text-sm text-status-error-text\">\n {error.message}\n </p>\n ) : products.length === 0 ? (\n <p className=\"text-sm text-text-muted\">No matching products found.</p>\n ) : (\n <>\n <ul className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {products.map((product) => (\n <li key={product.id}>\n <SearchResultCard\n product={product}\n noDesc={t('common.noResults', { defaultValue: 'No description available.' })}\n onOpen={(id) => navigate(`${basePath}/marketplace/product/${id}`)}\n />\n </li>\n ))}\n </ul>\n {hasMore && (\n <div className=\"flex justify-center pt-4\">\n <button\n type=\"button\"\n onClick={loadMore}\n disabled={loading}\n className=\"rounded-lg border border-border-default px-6 py-2.5 text-sm font-medium text-text-primary hover:bg-bg-subtle disabled:opacity-50\"\n >\n {loading ? 'Loading\\u2026' : 'Load more'}\n </button>\n </div>\n )}\n </>\n )}\n </div>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1,145 +1,144 @@
1
1
  import { useStore as e } from "../providers/StoreProvider.js";
2
- import { useEffect as t, useState as n } from "react";
3
- import { Check as r, Plus as i, RefreshCw as a, Store as o } from "lucide-react";
4
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
5
- import { graphqlFetch as l } from "@burdenoff/fe-libs/shared/graphql";
2
+ import { useCallback as t, useEffect as n, useState as r } from "react";
3
+ import { Check as i, Plus as a, RefreshCw as o, Store as s } from "lucide-react";
4
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
5
+ import { graphqlFetch as u } from "@burdenoff/fe-libs/shared/graphql";
6
6
  //#region src/pages/StoresPage.tsx
7
- var u = "\n query BrowseableStores {\n stores {\n id\n slug\n name\n type\n isPublic\n requiresApproval\n currency\n logoUrl\n description\n }\n mySubscribedStores {\n id\n }\n }\n", d = "\n mutation Subscribe($storeId: ID!) {\n subscribeToStore(storeId: $storeId) {\n id\n storeId\n }\n }\n", f = "\n mutation Unsubscribe($storeId: ID!) {\n unsubscribeFromStore(storeId: $storeId)\n }\n", p = () => {
8
- let { authToken: p } = e(), [m, h] = n([]), [g, _] = n(/* @__PURE__ */ new Set()), [v, y] = n(!1), [b, x] = n(null), [S, C] = n(null), w = async () => {
9
- y(!0), C(null);
7
+ var d = "\n query BrowseableStores {\n stores {\n id\n slug\n name\n type\n isPublic\n requiresApproval\n currency\n logoUrl\n description\n }\n mySubscribedStores {\n id\n }\n }\n", f = "\n mutation Subscribe($storeId: ID!) {\n subscribeToStore(storeId: $storeId) {\n id\n storeId\n }\n }\n", p = "\n mutation Unsubscribe($storeId: ID!) {\n unsubscribeFromStore(storeId: $storeId)\n }\n", m = () => {
8
+ let { authToken: m } = e(), [h, g] = r([]), [_, v] = r(/* @__PURE__ */ new Set()), [y, b] = r(!1), [x, S] = r(null), [C, w] = r(null), T = t(async () => {
9
+ b(!0), w(null);
10
10
  try {
11
- let e = await l({
11
+ let e = await u({
12
12
  gateway: "global",
13
- query: u,
14
- authToken: p || void 0
13
+ query: d,
14
+ authToken: m || void 0
15
15
  });
16
16
  if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load stores");
17
- h(e.data?.stores ?? []), _(new Set((e.data?.mySubscribedStores ?? []).map((e) => e.id)));
17
+ g(e.data?.stores ?? []), v(new Set((e.data?.mySubscribedStores ?? []).map((e) => e.id)));
18
18
  } catch (e) {
19
- C(e instanceof Error ? e.message : "Failed to load stores");
19
+ w(e instanceof Error ? e.message : "Failed to load stores");
20
20
  } finally {
21
- y(!1);
21
+ b(!1);
22
22
  }
23
- };
24
- t(() => {
25
- w();
26
- }, []);
27
- let T = async (e) => {
28
- x(e), C(null);
23
+ }, [m]);
24
+ n(() => {
25
+ T();
26
+ }, [T]);
27
+ let E = async (e) => {
28
+ S(e), w(null);
29
29
  try {
30
- let t = g.has(e);
31
- await l({
30
+ let t = _.has(e);
31
+ await u({
32
32
  gateway: "global",
33
- query: t ? f : d,
33
+ query: t ? p : f,
34
34
  variables: { storeId: e },
35
- authToken: p || void 0
36
- }), _((n) => {
35
+ authToken: m || void 0
36
+ }), v((n) => {
37
37
  let r = new Set(n);
38
38
  return t ? r.delete(e) : r.add(e), r;
39
39
  });
40
40
  } catch (e) {
41
- C(e instanceof Error ? e.message : "Action failed");
41
+ w(e instanceof Error ? e.message : "Action failed");
42
42
  } finally {
43
- x(null);
43
+ S(null);
44
44
  }
45
45
  };
46
- return /* @__PURE__ */ c("div", {
46
+ return /* @__PURE__ */ l("div", {
47
47
  className: "min-h-screen bg-surface-default",
48
- children: [/* @__PURE__ */ s("header", {
48
+ children: [/* @__PURE__ */ c("header", {
49
49
  className: "border-b border-border-default bg-surface-raised",
50
- children: /* @__PURE__ */ c("div", {
50
+ children: /* @__PURE__ */ l("div", {
51
51
  className: "mx-auto max-w-5xl px-6 py-4",
52
- children: [/* @__PURE__ */ c("div", {
52
+ children: [/* @__PURE__ */ l("div", {
53
53
  className: "flex items-center justify-between",
54
- children: [/* @__PURE__ */ c("div", {
54
+ children: [/* @__PURE__ */ l("div", {
55
55
  className: "flex items-center gap-2",
56
- children: [/* @__PURE__ */ s(o, { className: "h-5 w-5 text-text-primary" }), /* @__PURE__ */ s("h1", {
56
+ children: [/* @__PURE__ */ c(s, { className: "size-5 text-text-primary" }), /* @__PURE__ */ c("h1", {
57
57
  className: "text-xl font-semibold text-text-primary",
58
58
  children: "Stores"
59
59
  })]
60
- }), /* @__PURE__ */ c("button", {
60
+ }), /* @__PURE__ */ l("button", {
61
61
  type: "button",
62
- onClick: () => void w(),
63
- disabled: v,
62
+ onClick: () => void T(),
63
+ disabled: y,
64
64
  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",
65
- children: [/* @__PURE__ */ s(a, { className: `h-4 w-4 ${v ? "animate-spin" : ""}` }), "Refresh"]
65
+ children: [/* @__PURE__ */ c(o, { className: `size-4 ${y ? "animate-spin" : ""}` }), "Refresh"]
66
66
  })]
67
- }), /* @__PURE__ */ s("p", {
67
+ }), /* @__PURE__ */ c("p", {
68
68
  className: "mt-1 text-sm text-text-secondary",
69
69
  children: "Subscribe to additional stores to discover more products. Public stores are auto-subscribed."
70
70
  })]
71
71
  })
72
- }), /* @__PURE__ */ c("main", {
73
- className: "mx-auto max-w-5xl px-6 py-6",
74
- children: [S && /* @__PURE__ */ s("div", {
72
+ }), /* @__PURE__ */ l("main", {
73
+ className: "mx-auto max-w-5xl p-6",
74
+ children: [C && /* @__PURE__ */ c("div", {
75
75
  role: "alert",
76
76
  className: "mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger",
77
- children: S
78
- }), v && m.length === 0 ? /* @__PURE__ */ s("div", {
79
- role: "status",
80
- className: "py-16 text-center text-text-secondary",
77
+ children: C
78
+ }), y && h.length === 0 ? /* @__PURE__ */ c("output", {
79
+ className: "block py-16 text-center text-text-secondary",
81
80
  children: "Loading stores…"
82
- }) : m.length === 0 ? /* @__PURE__ */ s("div", {
81
+ }) : h.length === 0 ? /* @__PURE__ */ c("div", {
83
82
  className: "py-16 text-center text-text-secondary",
84
83
  children: "No stores available."
85
- }) : /* @__PURE__ */ s("ul", {
84
+ }) : /* @__PURE__ */ c("ul", {
86
85
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
87
- children: m.map((e) => {
88
- let t = g.has(e.id);
89
- return /* @__PURE__ */ c("li", {
86
+ children: h.map((e) => {
87
+ let t = _.has(e.id);
88
+ return /* @__PURE__ */ l("li", {
90
89
  className: "rounded-lg border border-border-default bg-surface-raised p-4",
91
- children: [/* @__PURE__ */ c("div", {
90
+ children: [/* @__PURE__ */ l("div", {
92
91
  className: "flex items-start gap-3",
93
- children: [e.logoUrl ? /* @__PURE__ */ s("img", {
92
+ children: [e.logoUrl ? /* @__PURE__ */ c("img", {
94
93
  src: e.logoUrl,
95
94
  alt: "",
96
- className: "h-12 w-12 flex-shrink-0 rounded-md object-cover"
97
- }) : /* @__PURE__ */ s("div", {
98
- className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary",
99
- children: /* @__PURE__ */ s(o, { className: "h-5 w-5" })
100
- }), /* @__PURE__ */ c("div", {
95
+ className: "size-12 flex-shrink-0 rounded-md object-cover"
96
+ }) : /* @__PURE__ */ c("div", {
97
+ className: "flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary",
98
+ children: /* @__PURE__ */ c(s, { className: "size-5" })
99
+ }), /* @__PURE__ */ l("div", {
101
100
  className: "min-w-0 flex-1",
102
101
  children: [
103
- /* @__PURE__ */ c("div", {
102
+ /* @__PURE__ */ l("div", {
104
103
  className: "flex items-center gap-2",
105
- children: [/* @__PURE__ */ s("h3", {
104
+ children: [/* @__PURE__ */ c("h3", {
106
105
  className: "truncate text-base font-semibold text-text-primary",
107
106
  children: e.name
108
- }), /* @__PURE__ */ s("span", {
107
+ }), /* @__PURE__ */ c("span", {
109
108
  className: "rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent",
110
109
  children: e.type
111
110
  })]
112
111
  }),
113
- /* @__PURE__ */ s("p", {
112
+ /* @__PURE__ */ c("p", {
114
113
  className: "mt-0.5 font-mono text-xs text-text-tertiary",
115
114
  children: e.slug
116
115
  }),
117
- e.description && /* @__PURE__ */ s("p", {
116
+ e.description && /* @__PURE__ */ c("p", {
118
117
  className: "mt-2 line-clamp-2 text-sm text-text-secondary",
119
118
  children: e.description
120
119
  })
121
120
  ]
122
121
  })]
123
- }), /* @__PURE__ */ c("div", {
122
+ }), /* @__PURE__ */ l("div", {
124
123
  className: "mt-3 flex items-center justify-between",
125
- children: [/* @__PURE__ */ s("span", {
124
+ children: [/* @__PURE__ */ c("span", {
126
125
  className: "text-xs text-text-tertiary",
127
126
  children: e.currency
128
- }), e.slug === "public" ? /* @__PURE__ */ c("span", {
127
+ }), e.slug === "public" ? /* @__PURE__ */ l("span", {
129
128
  className: "inline-flex items-center gap-1 rounded-md bg-surface-accent-muted px-2 py-1 text-xs text-text-accent",
130
- children: [/* @__PURE__ */ s(r, { className: "h-3 w-3" }), "Default"]
131
- }) : t ? /* @__PURE__ */ c("button", {
129
+ children: [/* @__PURE__ */ c(i, { className: "size-3" }), "Default"]
130
+ }) : t ? /* @__PURE__ */ l("button", {
132
131
  type: "button",
133
- onClick: () => void T(e.id),
134
- disabled: b === e.id,
132
+ onClick: () => void E(e.id),
133
+ disabled: x === e.id,
135
134
  className: "inline-flex items-center gap-1 rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover disabled:opacity-60",
136
- children: [/* @__PURE__ */ s(r, { className: "h-3 w-3" }), "Subscribed"]
137
- }) : /* @__PURE__ */ c("button", {
135
+ children: [/* @__PURE__ */ c(i, { className: "size-3" }), "Subscribed"]
136
+ }) : /* @__PURE__ */ l("button", {
138
137
  type: "button",
139
- onClick: () => void T(e.id),
140
- disabled: b === e.id,
138
+ onClick: () => void E(e.id),
139
+ disabled: x === e.id,
141
140
  className: "inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-60",
142
- children: [/* @__PURE__ */ s(i, { className: "h-3 w-3" }), "Subscribe"]
141
+ children: [/* @__PURE__ */ c(a, { className: "size-3" }), "Subscribe"]
143
142
  })]
144
143
  })]
145
144
  }, e.id);
@@ -149,6 +148,6 @@ var u = "\n query BrowseableStores {\n stores {\n id\n slug\n
149
148
  });
150
149
  };
151
150
  //#endregion
152
- export { p as StoresPage };
151
+ export { m as StoresPage };
153
152
 
154
153
  //# sourceMappingURL=StoresPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StoresPage.js","names":[],"sources":["../../src/pages/StoresPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect, useState } from 'react';\nimport { Store as StoreIcon, Check, Plus, RefreshCw } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useStore } from '../providers/StoreProvider';\n\ntype StoreRecord = {\n id: string;\n slug: string;\n name: string;\n type: 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE';\n isPublic: boolean;\n requiresApproval: boolean;\n currency: string;\n logoUrl?: string | null;\n description?: string | null;\n};\n\nconst STORES_QUERY = /* GraphQL */ `\n query BrowseableStores {\n stores {\n id\n slug\n name\n type\n isPublic\n requiresApproval\n currency\n logoUrl\n description\n }\n mySubscribedStores {\n id\n }\n }\n`;\n\nconst SUBSCRIBE_MUTATION = /* GraphQL */ `\n mutation Subscribe($storeId: ID!) {\n subscribeToStore(storeId: $storeId) {\n id\n storeId\n }\n }\n`;\n\nconst UNSUBSCRIBE_MUTATION = /* GraphQL */ `\n mutation Unsubscribe($storeId: ID!) {\n unsubscribeFromStore(storeId: $storeId)\n }\n`;\n\nexport const StoresPage: FC = () => {\n const { authToken } = useStore();\n const [stores, setStores] = useState<StoreRecord[]>([]);\n const [subscribed, setSubscribed] = useState<Set<string>>(new Set());\n const [loading, setLoading] = useState(false);\n const [actingId, setActingId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const load = async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n stores: StoreRecord[];\n mySubscribedStores: Array<{ id: string }>;\n }>({\n gateway: 'global',\n query: STORES_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load stores');\n }\n setStores(result.data?.stores ?? []);\n setSubscribed(new Set((result.data?.mySubscribedStores ?? []).map((s) => s.id)));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load stores');\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void load();\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const toggle = async (storeId: string) => {\n setActingId(storeId);\n setError(null);\n try {\n const isSubbed = subscribed.has(storeId);\n await graphqlFetch({\n gateway: 'global',\n query: isSubbed ? UNSUBSCRIBE_MUTATION : SUBSCRIBE_MUTATION,\n variables: { storeId },\n authToken: authToken || undefined,\n });\n setSubscribed((prev) => {\n const next = new Set(prev);\n if (isSubbed) next.delete(storeId);\n else next.add(storeId);\n return next;\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Action failed');\n } finally {\n setActingId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-5xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <StoreIcon className=\"h-5 w-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Stores</h1>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\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 >\n <RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Subscribe to additional stores to discover more products. Public stores are\n auto-subscribed.\n </p>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-5xl 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 && stores.length === 0 ? (\n <div role=\"status\" className=\"py-16 text-center text-text-secondary\">\n Loading stores…\n </div>\n ) : stores.length === 0 ? (\n <div className=\"py-16 text-center text-text-secondary\">No stores available.</div>\n ) : (\n <ul className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {stores.map((s) => {\n const isSubbed = subscribed.has(s.id);\n return (\n <li\n key={s.id}\n className=\"rounded-lg border border-border-default bg-surface-raised p-4\"\n >\n <div className=\"flex items-start gap-3\">\n {s.logoUrl ? (\n <img\n src={s.logoUrl}\n alt=\"\"\n className=\"h-12 w-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n <StoreIcon className=\"h-5 w-5\" />\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 {s.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 {s.type}\n </span>\n </div>\n <p className=\"mt-0.5 font-mono text-xs text-text-tertiary\">{s.slug}</p>\n {s.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">\n {s.description}\n </p>\n )}\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between\">\n <span className=\"text-xs text-text-tertiary\">{s.currency}</span>\n {s.slug === 'public' ? (\n <span className=\"inline-flex items-center gap-1 rounded-md bg-surface-accent-muted px-2 py-1 text-xs text-text-accent\">\n <Check className=\"h-3 w-3\" />\n Default\n </span>\n ) : isSubbed ? (\n <button\n type=\"button\"\n onClick={() => void toggle(s.id)}\n disabled={actingId === s.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover disabled:opacity-60\"\n >\n <Check className=\"h-3 w-3\" />\n Subscribed\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => void toggle(s.id)}\n disabled={actingId === s.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-60\"\n >\n <Plus className=\"h-3 w-3\" />\n Subscribe\n </button>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"StoresPage.js","names":[],"sources":["../../src/pages/StoresPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { Store as StoreIcon, Check, Plus, RefreshCw } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useStore } from '../providers/StoreProvider';\n\ntype StoreRecord = {\n id: string;\n slug: string;\n name: string;\n type: 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE';\n isPublic: boolean;\n requiresApproval: boolean;\n currency: string;\n logoUrl?: string | null;\n description?: string | null;\n};\n\nconst STORES_QUERY = /* GraphQL */ `\n query BrowseableStores {\n stores {\n id\n slug\n name\n type\n isPublic\n requiresApproval\n currency\n logoUrl\n description\n }\n mySubscribedStores {\n id\n }\n }\n`;\n\nconst SUBSCRIBE_MUTATION = /* GraphQL */ `\n mutation Subscribe($storeId: ID!) {\n subscribeToStore(storeId: $storeId) {\n id\n storeId\n }\n }\n`;\n\nconst UNSUBSCRIBE_MUTATION = /* GraphQL */ `\n mutation Unsubscribe($storeId: ID!) {\n unsubscribeFromStore(storeId: $storeId)\n }\n`;\n\nexport const StoresPage: FC = () => {\n const { authToken } = useStore();\n const [stores, setStores] = useState<StoreRecord[]>([]);\n const [subscribed, setSubscribed] = useState<Set<string>>(new Set());\n const [loading, setLoading] = useState(false);\n const [actingId, setActingId] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n stores: StoreRecord[];\n mySubscribedStores: Array<{ id: string }>;\n }>({\n gateway: 'global',\n query: STORES_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load stores');\n }\n setStores(result.data?.stores ?? []);\n setSubscribed(new Set((result.data?.mySubscribedStores ?? []).map((s) => s.id)));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load stores');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const toggle = async (storeId: string) => {\n setActingId(storeId);\n setError(null);\n try {\n const isSubbed = subscribed.has(storeId);\n await graphqlFetch({\n gateway: 'global',\n query: isSubbed ? UNSUBSCRIBE_MUTATION : SUBSCRIBE_MUTATION,\n variables: { storeId },\n authToken: authToken || undefined,\n });\n setSubscribed((prev) => {\n const next = new Set(prev);\n if (isSubbed) next.delete(storeId);\n else next.add(storeId);\n return next;\n });\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Action failed');\n } finally {\n setActingId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n <header className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-5xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <StoreIcon className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Stores</h1>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\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 >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Subscribe to additional stores to discover more products. Public stores are\n auto-subscribed.\n </p>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-5xl 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 && stores.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">Loading stores…</output>\n ) : stores.length === 0 ? (\n <div className=\"py-16 text-center text-text-secondary\">No stores available.</div>\n ) : (\n <ul className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {stores.map((s) => {\n const isSubbed = subscribed.has(s.id);\n return (\n <li\n key={s.id}\n className=\"rounded-lg border border-border-default bg-surface-raised p-4\"\n >\n <div className=\"flex items-start gap-3\">\n {s.logoUrl ? (\n <img\n src={s.logoUrl}\n alt=\"\"\n className=\"size-12 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-12 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n <StoreIcon className=\"size-5\" />\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 {s.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 {s.type}\n </span>\n </div>\n <p className=\"mt-0.5 font-mono text-xs text-text-tertiary\">{s.slug}</p>\n {s.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">\n {s.description}\n </p>\n )}\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between\">\n <span className=\"text-xs text-text-tertiary\">{s.currency}</span>\n {s.slug === 'public' ? (\n <span className=\"inline-flex items-center gap-1 rounded-md bg-surface-accent-muted px-2 py-1 text-xs text-text-accent\">\n <Check className=\"size-3\" />\n Default\n </span>\n ) : isSubbed ? (\n <button\n type=\"button\"\n onClick={() => void toggle(s.id)}\n disabled={actingId === s.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover disabled:opacity-60\"\n >\n <Check className=\"size-3\" />\n Subscribed\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => void toggle(s.id)}\n disabled={actingId === s.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-3 py-1 text-xs font-medium text-action-primary-text hover:opacity-90 disabled:opacity-60\"\n >\n <Plus className=\"size-3\" />\n Subscribe\n </button>\n )}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1,51 +1,51 @@
1
- import { createContext as e, useContext as t, useMemo as n } from "react";
1
+ import { createContext as e, use as t, useMemo as n } from "react";
2
2
  import { PermissionProvider as r } from "@burdenoff/fe-libs/shared/providers/shell";
3
3
  import { jsx as i } from "react/jsx-runtime";
4
4
  //#region src/providers/StoreProvider.tsx
5
- var a = e(null), o = ({ children: e, basePath: t = "/", apiGatewayUrl: o, authToken: s, features: c = {}, user: l, currentUser: u, productId: d, productName: f, defaultCompatibleWith: p, ...m }) => {
6
- let h = n(() => ({
7
- ...m,
5
+ var a = {}, o = e(null), s = ({ children: e, basePath: t = "/", apiGatewayUrl: s, authToken: c, features: l = a, user: u, currentUser: d, productId: f, productName: p, defaultCompatibleWith: m, ...h }) => {
6
+ let g = n(() => ({
7
+ ...h,
8
8
  basePath: t,
9
- apiGatewayUrl: o || "",
10
- authToken: s,
11
- user: l || u,
12
- currentUser: u || l,
13
- productId: d,
14
- productName: f,
15
- defaultCompatibleWith: p,
9
+ apiGatewayUrl: s || "",
10
+ authToken: c,
11
+ user: u || d,
12
+ currentUser: d || u,
13
+ productId: f,
14
+ productName: p,
15
+ defaultCompatibleWith: m,
16
16
  features: {
17
- enablePhysicalProducts: c.enablePhysicalProducts ?? !0,
18
- enablePublisherPortal: c.enablePublisherPortal ?? !0,
19
- enableReviews: c.enableReviews ?? !0,
20
- enableSubscriptions: c.enableSubscriptions ?? !0
17
+ enablePhysicalProducts: l?.enablePhysicalProducts ?? !0,
18
+ enablePublisherPortal: l?.enablePublisherPortal ?? !0,
19
+ enableReviews: l?.enableReviews ?? !0,
20
+ enableSubscriptions: l?.enableSubscriptions ?? !0
21
21
  }
22
22
  }), [
23
- m,
23
+ h,
24
24
  t,
25
- o,
26
25
  s,
27
- l,
26
+ c,
28
27
  u,
29
28
  d,
30
29
  f,
31
30
  p,
32
- c
31
+ m,
32
+ l
33
33
  ]);
34
- return /* @__PURE__ */ i(a.Provider, {
35
- value: h,
34
+ return /* @__PURE__ */ i(o.Provider, {
35
+ value: g,
36
36
  children: /* @__PURE__ */ i(r, {
37
- scopeId: h.organizationId ?? null,
37
+ scopeId: g.organizationId ?? null,
38
38
  gateway: "global",
39
39
  children: e
40
40
  })
41
41
  });
42
42
  };
43
- function s() {
44
- let e = t(a);
43
+ function c() {
44
+ let e = t(o);
45
45
  if (!e) throw Error("useStore must be used within StoreProvider");
46
46
  return e;
47
47
  }
48
48
  //#endregion
49
- export { o as StoreProvider, s as useStore };
49
+ export { s as StoreProvider, c as useStore };
50
50
 
51
51
  //# sourceMappingURL=StoreProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StoreProvider.js","names":[],"sources":["../../src/providers/StoreProvider.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { createContext, useContext, useMemo } from 'react';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { StoreRootProps, User } from '../types';\n\n/**\n * Store context value\n */\ninterface StoreContextValue extends Omit<StoreRootProps, 'children'> {\n basePath: string;\n apiGatewayUrl: string;\n authToken?: string;\n user?: User;\n productId?: string;\n productName?: string;\n defaultCompatibleWith?: string;\n features: {\n enablePhysicalProducts: boolean;\n enablePublisherPortal: boolean;\n enableReviews: boolean;\n enableSubscriptions: boolean;\n };\n}\n\n/**\n * Store context\n */\nconst StoreContext = createContext<StoreContextValue | null>(null);\n\n/**\n * Props for StoreProvider\n */\ninterface StoreProviderProps extends StoreRootProps {\n children: ReactNode;\n}\n\n/**\n * StoreProvider component\n *\n * Provides store context to all child components.\n */\nexport const StoreProvider: FC<StoreProviderProps> = ({\n children,\n basePath = '/',\n apiGatewayUrl,\n authToken,\n features = {},\n user,\n currentUser,\n productId,\n productName,\n defaultCompatibleWith,\n ...props\n}) => {\n const value = useMemo<StoreContextValue>(\n () => ({\n ...props,\n basePath,\n apiGatewayUrl: apiGatewayUrl || '',\n authToken,\n user: user || currentUser,\n currentUser: currentUser || user,\n productId,\n productName,\n defaultCompatibleWith,\n features: {\n enablePhysicalProducts: features.enablePhysicalProducts ?? true,\n enablePublisherPortal: features.enablePublisherPortal ?? true,\n enableReviews: features.enableReviews ?? true,\n enableSubscriptions: features.enableSubscriptions ?? true,\n },\n }),\n [\n props,\n basePath,\n apiGatewayUrl,\n authToken,\n user,\n currentUser,\n productId,\n productName,\n defaultCompatibleWith,\n features,\n ]\n );\n\n return (\n <StoreContext.Provider value={value}>\n <PermissionProvider scopeId={value.organizationId ?? null} gateway=\"global\">\n {children}\n </PermissionProvider>\n </StoreContext.Provider>\n );\n};\n\n/**\n * Hook to use store context\n */\nexport function useStore(): StoreContextValue {\n const context = useContext(StoreContext);\n\n if (!context) {\n throw new Error('useStore must be used within StoreProvider');\n }\n\n return context;\n}\n"],"mappings":";;;;AA2BA,IAAM,IAAe,EAAwC,KAAK,EAcrD,KAAyC,EACpD,aACA,cAAW,KACX,kBACA,cACA,cAAW,EAAE,EACb,SACA,gBACA,cACA,gBACA,0BACA,GAAG,QACC;CACJ,IAAM,IAAQ,SACL;EACL,GAAG;EACH;EACA,eAAe,KAAiB;EAChC;EACA,MAAM,KAAQ;EACd,aAAa,KAAe;EAC5B;EACA;EACA;EACA,UAAU;GACR,wBAAwB,EAAS,0BAA0B;GAC3D,uBAAuB,EAAS,yBAAyB;GACzD,eAAe,EAAS,iBAAiB;GACzC,qBAAqB,EAAS,uBAAuB;GACtD;EACF,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,kBAAC,EAAa,UAAd;EAA8B;YAC5B,kBAAC,GAAD;GAAoB,SAAS,EAAM,kBAAkB;GAAM,SAAQ;GAChE;GACkB,CAAA;EACC,CAAA;;AAO5B,SAAgB,IAA8B;CAC5C,IAAM,IAAU,EAAW,EAAa;AAExC,KAAI,CAAC,EACH,OAAU,MAAM,6CAA6C;AAG/D,QAAO"}
1
+ {"version":3,"file":"StoreProvider.js","names":[],"sources":["../../src/providers/StoreProvider.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { createContext, use, useMemo } from 'react';\nimport { PermissionProvider } from '@burdenoff/fe-libs/shared/providers/shell';\nimport type { StoreRootProps, User } from '../types';\n\nconst EMPTY_FEATURES: StoreRootProps['features'] = {};\n\n/**\n * Store context value\n */\ninterface StoreContextValue extends Omit<StoreRootProps, 'children'> {\n basePath: string;\n apiGatewayUrl: string;\n authToken?: string;\n user?: User;\n productId?: string;\n productName?: string;\n defaultCompatibleWith?: string;\n features: {\n enablePhysicalProducts: boolean;\n enablePublisherPortal: boolean;\n enableReviews: boolean;\n enableSubscriptions: boolean;\n };\n}\n\n/**\n * Store context\n */\nconst StoreContext = createContext<StoreContextValue | null>(null);\n\n/**\n * Props for StoreProvider\n */\ninterface StoreProviderProps extends StoreRootProps {\n children: ReactNode;\n}\n\n/**\n * StoreProvider component\n *\n * Provides store context to all child components.\n */\nexport const StoreProvider: FC<StoreProviderProps> = ({\n children,\n basePath = '/',\n apiGatewayUrl,\n authToken,\n features = EMPTY_FEATURES,\n user,\n currentUser,\n productId,\n productName,\n defaultCompatibleWith,\n ...props\n}) => {\n const value = useMemo<StoreContextValue>(\n () => ({\n ...props,\n basePath,\n apiGatewayUrl: apiGatewayUrl || '',\n authToken,\n user: user || currentUser,\n currentUser: currentUser || user,\n productId,\n productName,\n defaultCompatibleWith,\n features: {\n enablePhysicalProducts: features?.enablePhysicalProducts ?? true,\n enablePublisherPortal: features?.enablePublisherPortal ?? true,\n enableReviews: features?.enableReviews ?? true,\n enableSubscriptions: features?.enableSubscriptions ?? true,\n },\n }),\n [\n props,\n basePath,\n apiGatewayUrl,\n authToken,\n user,\n currentUser,\n productId,\n productName,\n defaultCompatibleWith,\n features,\n ]\n );\n\n return (\n <StoreContext.Provider value={value}>\n <PermissionProvider scopeId={value.organizationId ?? null} gateway=\"global\">\n {children}\n </PermissionProvider>\n </StoreContext.Provider>\n );\n};\n\n/**\n * Hook to use store context\n */\nexport function useStore(): StoreContextValue {\n const context = use(StoreContext);\n\n if (!context) {\n throw new Error('useStore must be used within StoreProvider');\n }\n\n return context;\n}\n"],"mappings":";;;;AAKA,IAAM,IAA6C,EAAE,EAwB/C,IAAe,EAAwC,KAAK,EAcrD,KAAyC,EACpD,aACA,cAAW,KACX,kBACA,cACA,cAAW,GACX,SACA,gBACA,cACA,gBACA,0BACA,GAAG,QACC;CACJ,IAAM,IAAQ,SACL;EACL,GAAG;EACH;EACA,eAAe,KAAiB;EAChC;EACA,MAAM,KAAQ;EACd,aAAa,KAAe;EAC5B;EACA;EACA;EACA,UAAU;GACR,wBAAwB,GAAU,0BAA0B;GAC5D,uBAAuB,GAAU,yBAAyB;GAC1D,eAAe,GAAU,iBAAiB;GAC1C,qBAAqB,GAAU,uBAAuB;GACvD;EACF,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,kBAAC,EAAa,UAAd;EAA8B;YAC5B,kBAAC,GAAD;GAAoB,SAAS,EAAM,kBAAkB;GAAM,SAAQ;GAChE;GACkB,CAAA;EACC,CAAA;;AAO5B,SAAgB,IAA8B;CAC5C,IAAM,IAAU,EAAI,EAAa;AAEjC,KAAI,CAAC,EACH,OAAU,MAAM,6CAA6C;AAG/D,QAAO"}
@@ -1,14 +1,24 @@
1
1
  //#region src/utils/index.ts
2
- function e(e, t = "USD") {
3
- return new Intl.NumberFormat("en-US", {
2
+ var e = /* @__PURE__ */ new Map();
3
+ function t(t) {
4
+ let n = e.get(t);
5
+ if (n) return n;
6
+ let r = new Intl.NumberFormat("en-US", {
4
7
  style: "currency",
5
8
  currency: t
6
- }).format(e);
9
+ });
10
+ return e.set(t, r), r;
7
11
  }
8
- function t(e) {
12
+ function n(e, n = "USD") {
13
+ return t(n).format(e);
14
+ }
15
+ function r(e, n, r) {
16
+ return r === "FREE" ? "Free" : t(n || "USD").format(e);
17
+ }
18
+ function i(e) {
9
19
  return e >= 1e6 ? `${(e / 1e6).toFixed(1)}M` : e >= 1e3 ? `${(e / 1e3).toFixed(1)}K` : e.toString();
10
20
  }
11
- function n(e, t = .085) {
21
+ function a(e, t = .085) {
12
22
  let n = e.reduce((e, t) => e + t.quantity * t.unitPrice, 0), r = n * t;
13
23
  return {
14
24
  subtotal: n,
@@ -16,13 +26,13 @@ function n(e, t = .085) {
16
26
  total: n + r
17
27
  };
18
28
  }
19
- function r(e) {
29
+ function o(e) {
20
30
  let t = [], n = Math.floor(e), r = e % 1 >= .5;
21
31
  for (let e = 0; e < n; e++) t.push("full");
22
32
  for (r && t.push("half"); t.length < 5;) t.push("empty");
23
33
  return t;
24
34
  }
25
- function i(e) {
35
+ function s(e) {
26
36
  return {
27
37
  APP: "App",
28
38
  WORKFLOW: "Workflow",
@@ -34,7 +44,7 @@ function i(e) {
34
44
  BUNDLE: "Bundle"
35
45
  }[e] || e;
36
46
  }
37
- function a(e) {
47
+ function c(e) {
38
48
  return {
39
49
  FREE: "Free",
40
50
  ONE_TIME: "One-time Purchase",
@@ -44,7 +54,7 @@ function a(e) {
44
54
  PAY_WHAT_YOU_WANT: "Pay What You Want"
45
55
  }[e] || e;
46
56
  }
47
- function o(e) {
57
+ function l(e) {
48
58
  return {
49
59
  DRAFT: "default",
50
60
  PENDING_REVIEW: "warning",
@@ -69,22 +79,22 @@ function o(e) {
69
79
  REFUNDED: "warning"
70
80
  }[e] || "default";
71
81
  }
72
- function s(e) {
82
+ function u(e) {
73
83
  return /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(e);
74
84
  }
75
- function c(e) {
85
+ function d(e) {
76
86
  let t = e.split("-");
77
87
  return t.length === 4 ? `${t[0]}-${t[1]}-****-****` : e;
78
88
  }
79
- function l(e) {
89
+ function f(e) {
80
90
  let t = /* @__PURE__ */ new Date(), n = new Date(e).getTime() - t.getTime();
81
91
  return Math.ceil(n / (1e3 * 60 * 60 * 24));
82
92
  }
83
- function u(e) {
84
- let t = l(e);
93
+ function p(e) {
94
+ let t = f(e);
85
95
  return t > 0 && t <= 30;
86
96
  }
87
- function d(e) {
97
+ function m(e) {
88
98
  if (e === 0) return "0 Bytes";
89
99
  let t = 1024, n = [
90
100
  "Bytes",
@@ -94,10 +104,10 @@ function d(e) {
94
104
  ], r = Math.floor(Math.log(e) / Math.log(t));
95
105
  return `${parseFloat((e / t ** +r).toFixed(2))} ${n[r]}`;
96
106
  }
97
- function f(e, t) {
107
+ function h(e, t) {
98
108
  return t ? `${e}-${t}` : e;
99
109
  }
100
- function p(e, t) {
110
+ function g(e, t) {
101
111
  let n = null;
102
112
  return function(...r) {
103
113
  n && clearTimeout(n), n = setTimeout(() => {
@@ -105,10 +115,10 @@ function p(e, t) {
105
115
  }, t);
106
116
  };
107
117
  }
108
- function m(e, t) {
118
+ function _(e, t) {
109
119
  return e.length <= t ? e : `${e.substring(0, t)}...`;
110
120
  }
111
121
  //#endregion
112
- export { n as calculateCartTotals, p as debounce, d as formatFileSize, t as formatNumber, e as formatPrice, f as generateCartItemId, l as getDaysUntilExpiry, i as getItemTypeName, a as getPricingModelName, r as getRatingStars, o as getStatusColor, u as isLicenseExpiringSoon, c as maskLicenseKey, m as truncate, s as validateLicenseKey };
122
+ export { a as calculateCartTotals, g as debounce, m as formatFileSize, i as formatNumber, n as formatPrice, r as formatPriceForModel, h as generateCartItemId, f as getDaysUntilExpiry, s as getItemTypeName, c as getPricingModelName, o as getRatingStars, l as getStatusColor, p as isLicenseExpiringSoon, d as maskLicenseKey, _ as truncate, u as validateLicenseKey };
113
123
 
114
124
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["/**\n * Format price with currency\n */\nexport function formatPrice(price: number, currency: string = 'USD'): string {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency,\n }).format(price);\n}\n\n/**\n * Format number with abbreviation (1.2K, 3.4M, etc.)\n */\nexport function formatNumber(num: number): string {\n if (num >= 1000000) {\n return `${(num / 1000000).toFixed(1)}M`;\n }\n if (num >= 1000) {\n return `${(num / 1000).toFixed(1)}K`;\n }\n return num.toString();\n}\n\n/**\n * Calculate cart totals\n */\nexport function calculateCartTotals(\n items: Array<{ quantity: number; unitPrice: number }>,\n taxRate: number = 0.085\n) {\n const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);\n const tax = subtotal * taxRate;\n const total = subtotal + tax;\n\n return { subtotal, tax, total };\n}\n\n/**\n * Generate rating stars array\n */\nexport function getRatingStars(rating: number): Array<'full' | 'half' | 'empty'> {\n const stars: Array<'full' | 'half' | 'empty'> = [];\n const fullStars = Math.floor(rating);\n const hasHalfStar = rating % 1 >= 0.5;\n\n for (let i = 0; i < fullStars; i++) {\n stars.push('full');\n }\n\n if (hasHalfStar) {\n stars.push('half');\n }\n\n while (stars.length < 5) {\n stars.push('empty');\n }\n\n return stars;\n}\n\n/**\n * Get item type display name\n */\nexport function getItemTypeName(itemType: string): string {\n const names: Record<string, string> = {\n APP: 'App',\n WORKFLOW: 'Workflow',\n TEMPLATE: 'Template',\n INTEGRATION: 'Integration',\n EXTENSION: 'Extension',\n THEME: 'Theme',\n PHYSICAL: 'Physical Product',\n BUNDLE: 'Bundle',\n };\n\n return names[itemType] || itemType;\n}\n\n/**\n * Get pricing model display name\n */\nexport function getPricingModelName(model: string): string {\n const names: Record<string, string> = {\n FREE: 'Free',\n ONE_TIME: 'One-time Purchase',\n SUBSCRIPTION: 'Subscription',\n USAGE_BASED: 'Usage-based',\n FREEMIUM: 'Freemium',\n PAY_WHAT_YOU_WANT: 'Pay What You Want',\n };\n\n return names[model] || model;\n}\n\n/**\n * Get status color variant\n */\nexport function getStatusColor(\n status: string\n): 'default' | 'success' | 'warning' | 'error' | 'info' {\n const colors: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {\n // Product status\n DRAFT: 'default',\n PENDING_REVIEW: 'warning',\n APPROVED: 'success',\n PUBLISHED: 'success',\n REJECTED: 'error',\n DEPRECATED: 'warning',\n UNLISTED: 'default',\n\n // Installation status\n PENDING: 'warning',\n INSTALLING: 'info',\n CONFIGURING: 'info',\n ACTIVE: 'success',\n INACTIVE: 'default',\n FAILED: 'error',\n UNINSTALLING: 'warning',\n\n // License status (ACTIVE already defined above)\n EXPIRED: 'error',\n REVOKED: 'error',\n SUSPENDED: 'warning',\n PENDING_ACTIVATION: 'warning',\n\n // Order status (FAILED already defined above)\n PROCESSING: 'info',\n COMPLETED: 'success',\n REFUNDED: 'warning',\n };\n\n return colors[status] || 'default';\n}\n\n/**\n * Validate license key format\n */\nexport function validateLicenseKey(key: string): boolean {\n // Expected format: XXXX-XXXX-XXXX-XXXX\n const regex = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;\n return regex.test(key);\n}\n\n/**\n * Mask license key for display\n */\nexport function maskLicenseKey(key: string): string {\n const parts = key.split('-');\n if (parts.length !== 4) return key;\n\n return `${parts[0]}-${parts[1]}-****-****`;\n}\n\n/**\n * Calculate days until expiry\n */\nexport function getDaysUntilExpiry(expiryDate: string): number {\n const now = new Date();\n const expiry = new Date(expiryDate);\n const diffTime = expiry.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Check if license is expiring soon (within 30 days)\n */\nexport function isLicenseExpiringSoon(expiryDate: string): boolean {\n const daysUntilExpiry = getDaysUntilExpiry(expiryDate);\n return daysUntilExpiry > 0 && daysUntilExpiry <= 30;\n}\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Generate unique cart item ID\n */\nexport function generateCartItemId(productId: string, variantId?: string): string {\n return variantId ? `${productId}-${variantId}` : productId;\n}\n\n/**\n * Debounce function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Truncate text with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.substring(0, maxLength)}...`;\n}\n"],"mappings":";AAGA,SAAgB,EAAY,GAAe,IAAmB,OAAe;AAC3E,QAAO,IAAI,KAAK,aAAa,SAAS;EACpC,OAAO;EACP;EACD,CAAC,CAAC,OAAO,EAAM;;AAMlB,SAAgB,EAAa,GAAqB;AAOhD,QANI,KAAO,MACF,IAAI,IAAM,KAAS,QAAQ,EAAE,CAAC,KAEnC,KAAO,MACF,IAAI,IAAM,KAAM,QAAQ,EAAE,CAAC,KAE7B,EAAI,UAAU;;AAMvB,SAAgB,EACd,GACA,IAAkB,MAClB;CACA,IAAM,IAAW,EAAM,QAAQ,GAAK,MAAS,IAAM,EAAK,WAAW,EAAK,WAAW,EAAE,EAC/E,IAAM,IAAW;AAGvB,QAAO;EAAE;EAAU;EAAK,OAFV,IAAW;EAEM;;AAMjC,SAAgB,EAAe,GAAkD;CAC/E,IAAM,IAA0C,EAAE,EAC5C,IAAY,KAAK,MAAM,EAAO,EAC9B,IAAc,IAAS,KAAK;AAElC,MAAK,IAAI,IAAI,GAAG,IAAI,GAAW,IAC7B,GAAM,KAAK,OAAO;AAOpB,MAJI,KACF,EAAM,KAAK,OAAO,EAGb,EAAM,SAAS,GACpB,GAAM,KAAK,QAAQ;AAGrB,QAAO;;AAMT,SAAgB,EAAgB,GAA0B;AAYxD,QAXsC;EACpC,KAAK;EACL,UAAU;EACV,UAAU;EACV,aAAa;EACb,WAAW;EACX,OAAO;EACP,UAAU;EACV,QAAQ;EACT,CAEY,MAAa;;AAM5B,SAAgB,EAAoB,GAAuB;AAUzD,QATsC;EACpC,MAAM;EACN,UAAU;EACV,cAAc;EACd,aAAa;EACb,UAAU;EACV,mBAAmB;EACpB,CAEY,MAAU;;AAMzB,SAAgB,EACd,GACsD;AAgCtD,QA/BqF;EAEnF,OAAO;EACP,gBAAgB;EAChB,UAAU;EACV,WAAW;EACX,UAAU;EACV,YAAY;EACZ,UAAU;EAGV,SAAS;EACT,YAAY;EACZ,aAAa;EACb,QAAQ;EACR,UAAU;EACV,QAAQ;EACR,cAAc;EAGd,SAAS;EACT,SAAS;EACT,WAAW;EACX,oBAAoB;EAGpB,YAAY;EACZ,WAAW;EACX,UAAU;EACX,CAEa,MAAW;;AAM3B,SAAgB,EAAmB,GAAsB;AAGvD,QADc,oDACD,KAAK,EAAI;;AAMxB,SAAgB,EAAe,GAAqB;CAClD,IAAM,IAAQ,EAAI,MAAM,IAAI;AAG5B,QAFI,EAAM,WAAW,IAEd,GAAG,EAAM,GAAG,GAAG,EAAM,GAAG,cAFA;;AAQjC,SAAgB,EAAmB,GAA4B;CAC7D,IAAM,oBAAM,IAAI,MAAM,EAEhB,IADS,IAAI,KAAK,EAAW,CACX,SAAS,GAAG,EAAI,SAAS;AACjD,QAAO,KAAK,KAAK,KAAY,MAAO,KAAK,KAAK,IAAI;;AAMpD,SAAgB,EAAsB,GAA6B;CACjE,IAAM,IAAkB,EAAmB,EAAW;AACtD,QAAO,IAAkB,KAAK,KAAmB;;AAMnD,SAAgB,EAAe,GAAuB;AACpD,KAAI,MAAU,EAAG,QAAO;CAExB,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAS;EAAM;EAAM;EAAK,EACnC,IAAI,KAAK,MAAM,KAAK,IAAI,EAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AAEnD,QAAO,GAAG,YAAY,IAAiB,MAAG,GAAI,QAAQ,EAAE,CAAC,CAAC,GAAG,EAAM;;AAMrE,SAAgB,EAAmB,GAAmB,GAA4B;AAChF,QAAO,IAAY,GAAG,EAAU,GAAG,MAAc;;AAMnD,SAAgB,EACd,GACA,GACkC;CAClC,IAAI,IAAgD;AAEpD,QAAO,SAA0B,GAAG,GAAqB;AASvD,EAHI,KACF,aAAa,EAAQ,EAEvB,IAAU,iBARU;AAElB,GADA,IAAU,MACV,EAAK,GAAG,EAAK;KAMa,EAAK;;;AAOrC,SAAgB,EAAS,GAAc,GAA2B;AAEhE,QADI,EAAK,UAAU,IAAkB,IAC9B,GAAG,EAAK,UAAU,GAAG,EAAU,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["// Cache Intl.NumberFormat instances per currency to avoid per-call allocations.\nconst currencyFormatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getCurrencyFormatter(currency: string): Intl.NumberFormat {\n const cached = currencyFormatterCache.get(currency);\n if (cached) return cached;\n const fmt = new Intl.NumberFormat('en-US', { style: 'currency', currency });\n currencyFormatterCache.set(currency, fmt);\n return fmt;\n}\n\n/**\n * Format price with currency\n */\nexport function formatPrice(price: number, currency: string = 'USD'): string {\n return getCurrencyFormatter(currency).format(price);\n}\n\n/**\n * Format price honoring a pricing model. Returns \"Free\" for FREE model.\n */\nexport function formatPriceForModel(price: number, currency: string, model: string): string {\n if (model === 'FREE') return 'Free';\n return getCurrencyFormatter(currency || 'USD').format(price);\n}\n\n/**\n * Format number with abbreviation (1.2K, 3.4M, etc.)\n */\nexport function formatNumber(num: number): string {\n if (num >= 1000000) {\n return `${(num / 1000000).toFixed(1)}M`;\n }\n if (num >= 1000) {\n return `${(num / 1000).toFixed(1)}K`;\n }\n return num.toString();\n}\n\n/**\n * Calculate cart totals\n */\nexport function calculateCartTotals(\n items: Array<{ quantity: number; unitPrice: number }>,\n taxRate: number = 0.085\n) {\n const subtotal = items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);\n const tax = subtotal * taxRate;\n const total = subtotal + tax;\n\n return { subtotal, tax, total };\n}\n\n/**\n * Generate rating stars array\n */\nexport function getRatingStars(rating: number): Array<'full' | 'half' | 'empty'> {\n const stars: Array<'full' | 'half' | 'empty'> = [];\n const fullStars = Math.floor(rating);\n const hasHalfStar = rating % 1 >= 0.5;\n\n for (let i = 0; i < fullStars; i++) {\n stars.push('full');\n }\n\n if (hasHalfStar) {\n stars.push('half');\n }\n\n while (stars.length < 5) {\n stars.push('empty');\n }\n\n return stars;\n}\n\n/**\n * Get item type display name\n */\nexport function getItemTypeName(itemType: string): string {\n const names: Record<string, string> = {\n APP: 'App',\n WORKFLOW: 'Workflow',\n TEMPLATE: 'Template',\n INTEGRATION: 'Integration',\n EXTENSION: 'Extension',\n THEME: 'Theme',\n PHYSICAL: 'Physical Product',\n BUNDLE: 'Bundle',\n };\n\n return names[itemType] || itemType;\n}\n\n/**\n * Get pricing model display name\n */\nexport function getPricingModelName(model: string): string {\n const names: Record<string, string> = {\n FREE: 'Free',\n ONE_TIME: 'One-time Purchase',\n SUBSCRIPTION: 'Subscription',\n USAGE_BASED: 'Usage-based',\n FREEMIUM: 'Freemium',\n PAY_WHAT_YOU_WANT: 'Pay What You Want',\n };\n\n return names[model] || model;\n}\n\n/**\n * Get status color variant\n */\nexport function getStatusColor(\n status: string\n): 'default' | 'success' | 'warning' | 'error' | 'info' {\n const colors: Record<string, 'default' | 'success' | 'warning' | 'error' | 'info'> = {\n // Product status\n DRAFT: 'default',\n PENDING_REVIEW: 'warning',\n APPROVED: 'success',\n PUBLISHED: 'success',\n REJECTED: 'error',\n DEPRECATED: 'warning',\n UNLISTED: 'default',\n\n // Installation status\n PENDING: 'warning',\n INSTALLING: 'info',\n CONFIGURING: 'info',\n ACTIVE: 'success',\n INACTIVE: 'default',\n FAILED: 'error',\n UNINSTALLING: 'warning',\n\n // License status (ACTIVE already defined above)\n EXPIRED: 'error',\n REVOKED: 'error',\n SUSPENDED: 'warning',\n PENDING_ACTIVATION: 'warning',\n\n // Order status (FAILED already defined above)\n PROCESSING: 'info',\n COMPLETED: 'success',\n REFUNDED: 'warning',\n };\n\n return colors[status] || 'default';\n}\n\n/**\n * Validate license key format\n */\nexport function validateLicenseKey(key: string): boolean {\n // Expected format: XXXX-XXXX-XXXX-XXXX\n const regex = /^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;\n return regex.test(key);\n}\n\n/**\n * Mask license key for display\n */\nexport function maskLicenseKey(key: string): string {\n const parts = key.split('-');\n if (parts.length !== 4) return key;\n\n return `${parts[0]}-${parts[1]}-****-****`;\n}\n\n/**\n * Calculate days until expiry\n */\nexport function getDaysUntilExpiry(expiryDate: string): number {\n const now = new Date();\n const expiry = new Date(expiryDate);\n const diffTime = expiry.getTime() - now.getTime();\n return Math.ceil(diffTime / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Check if license is expiring soon (within 30 days)\n */\nexport function isLicenseExpiringSoon(expiryDate: string): boolean {\n const daysUntilExpiry = getDaysUntilExpiry(expiryDate);\n return daysUntilExpiry > 0 && daysUntilExpiry <= 30;\n}\n\n/**\n * Format file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n\n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\n/**\n * Generate unique cart item ID\n */\nexport function generateCartItemId(productId: string, variantId?: string): string {\n return variantId ? `${productId}-${variantId}` : productId;\n}\n\n/**\n * Debounce function\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Truncate text with ellipsis\n */\nexport function truncate(text: string, maxLength: number): string {\n if (text.length <= maxLength) return text;\n return `${text.substring(0, maxLength)}...`;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-store",
3
- "version": "2026.525.2",
3
+ "version": "2026.527.1",
4
4
  "description": "Store microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [
@@ -1,22 +0,0 @@
1
- import { FC } from 'react';
2
- import { StoreAdminRootProps } from './types';
3
-
4
- /**
5
- * Admin root component for the Store microfrontend.
6
- *
7
- * This is the admin entry point that the admin app shell imports and renders.
8
- * It sets up the necessary providers and renders only admin-specific routing.
9
- * User-facing routes (marketplace, cart, orders, etc.) are NOT included here —
10
- * those are in StoreRoot, which product app shells import instead.
11
- *
12
- * Nav items are registered statically by the admin shell's NavRegistration
13
- * component, so this root only needs to provide context and routes.
14
- *
15
- * @example
16
- * ```tsx
17
- * import { StoreAdminRoot } from '@burdenoff/microfe-store';
18
- *
19
- * <StoreAdminRoot basePath="/store" {...globalBaseProps} />
20
- * ```
21
- */
22
- export declare const StoreAdminRoot: FC<StoreAdminRootProps>;