@burdenoff/microfe-devportal 2026.707.1 → 2026.711.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 (28) hide show
  1. package/dist/devportal/AdminDevportalRoot.js +33 -27
  2. package/dist/devportal/AdminDevportalRoot.js.map +1 -1
  3. package/dist/devportal/AdminDevportalRoutes.js +9 -5
  4. package/dist/devportal/AdminDevportalRoutes.js.map +1 -1
  5. package/dist/devportal/DevportalRoutes.js +125 -105
  6. package/dist/devportal/DevportalRoutes.js.map +1 -1
  7. package/dist/devportal/pages/AdminOrganizationVerificationPage.js +169 -0
  8. package/dist/devportal/pages/AdminOrganizationVerificationPage.js.map +1 -0
  9. package/dist/devportal/pages/CreatePhysicalItemPage.js +494 -0
  10. package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -0
  11. package/dist/devportal/pages/EditPhysicalItemPage.js +419 -0
  12. package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -0
  13. package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
  14. package/dist/devportal/pages/OrganizationDetailPage.js +441 -295
  15. package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
  16. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  17. package/dist/devportal/pages/RevenueDashboardPage.js +72 -54
  18. package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
  19. package/dist/devportal/pages/SellerDashboardPage.js +364 -0
  20. package/dist/devportal/pages/SellerDashboardPage.js.map +1 -0
  21. package/dist/devportal/pages/SellerItemsPage.js +533 -0
  22. package/dist/devportal/pages/SellerItemsPage.js.map +1 -0
  23. package/dist/devportal/pages/SellerOrdersPage.js +411 -0
  24. package/dist/devportal/pages/SellerOrdersPage.js.map +1 -0
  25. package/dist/devportal/types/index.js.map +1 -1
  26. package/dist/generated/global-operations.js +48 -55
  27. package/dist/generated/global-operations.js.map +1 -1
  28. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SellerDashboardPage.js","names":[],"sources":["../../../src/devportal/pages/SellerDashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Package,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n Download,\n DollarSign,\n Clock,\n CheckCircle2,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\n\n// ---------------------------------------------------------------------------\n// Local types\n// ---------------------------------------------------------------------------\n\ntype ProductStatus =\n | \"DRAFT\"\n | \"PENDING_REVIEW\"\n | \"PUBLISHED\"\n | \"REJECTED\"\n | \"INACTIVE\";\n\ninterface PhysicalProduct {\n id: string;\n name: string;\n slug: string;\n type: string;\n status: ProductStatus;\n price: number;\n currency: string;\n stockQuantity: number;\n icon?: string | null;\n}\n\ninterface OrgSummary {\n id: string;\n name: string;\n slug: string;\n logoUrl?: string | null;\n isVerified: boolean;\n publishedApps?: number | null;\n totalInstalls?: number | null;\n totalRevenue?: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst STATUS_BADGE_STYLES: Record<\n ProductStatus,\n { label: string; className: string }\n> = {\n PUBLISHED: {\n label: \"Published\",\n className: \"text-status-success-text bg-status-success-bg-subtle\",\n },\n PENDING_REVIEW: {\n label: \"Pending Review\",\n className: \"text-status-warning-text bg-status-warning-bg-subtle\",\n },\n DRAFT: {\n label: \"Draft\",\n className: \"text-text-muted bg-bg-muted\",\n },\n REJECTED: {\n label: \"Rejected\",\n className: \"text-status-error-text bg-status-error-bg/10\",\n },\n INACTIVE: {\n label: \"Inactive\",\n className: \"text-text-muted bg-bg-muted\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formatPrice(price: number, currency: string): string {\n const curr = currency || \"USD\";\n const formatted = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: curr,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(price);\n return `${formatted} ${curr}`;\n}\n\n// ---------------------------------------------------------------------------\n// GraphQL query strings\n// ---------------------------------------------------------------------------\n\nconst BOOTSTRAP_QUERY = `\n query SellerDashboardBootstrap {\n myDeveloperOrganizations {\n id\n name\n slug\n logoUrl\n isVerified\n publishedApps\n totalInstalls\n totalRevenue\n }\n myPublisher {\n id\n }\n }\n`;\n\nconst SELLER_PRODUCTS_QUERY = `\n query SellerDashboardProducts($publisherId: String!) {\n products(filter: { publisherId: $publisherId, nature: PHYSICAL }, limit: 3, offset: 0) {\n id\n name\n slug\n type\n status\n price\n currency\n stockQuantity\n icon\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Page component\n// ---------------------------------------------------------------------------\n\nexport const SellerDashboardPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const gqlUrl = apiGatewayUrl ?? \"/global/graphql\";\n\n const [bootstrapLoading, setBootstrapLoading] = useState(true);\n const [productsLoading, setProductsLoading] = useState(false);\n const [orgs, setOrgs] = useState<OrgSummary[]>([]);\n const [publisherId, setPublisherId] = useState<string | null>(null);\n const [products, setProducts] = useState<PhysicalProduct[]>([]);\n\n // Fetch orgs + publisher in one round-trip\n useEffect(() => {\n let cancelled = false;\n setBootstrapLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({ query: BOOTSTRAP_QUERY }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n myDeveloperOrganizations?: OrgSummary[];\n myPublisher?: { id: string } | null;\n };\n };\n if (cancelled) return;\n setOrgs(json.data?.myDeveloperOrganizations ?? []);\n setPublisherId(json.data?.myPublisher?.id ?? null);\n } finally {\n if (!cancelled) setBootstrapLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [gqlUrl, authToken]);\n\n // Fetch recent physical products once publisher is known\n useEffect(() => {\n if (!publisherId) return;\n let cancelled = false;\n setProductsLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({\n query: SELLER_PRODUCTS_QUERY,\n variables: { publisherId },\n }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n products?: Array<\n Omit<PhysicalProduct, \"status\"> & { status: string }\n >;\n };\n };\n if (cancelled) return;\n const DIGITAL_TYPES = new Set([\"APP\", \"NODE\", \"WORKFLOW\"]);\n setProducts(\n (json.data?.products ?? [])\n .filter((p) => !DIGITAL_TYPES.has(p.type))\n .map((p) => ({\n ...p,\n status: p.status as ProductStatus,\n })),\n );\n } finally {\n if (!cancelled) setProductsLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [publisherId, gqlUrl, authToken]);\n\n const isLoading = bootstrapLoading;\n const statsLoading = bootstrapLoading || productsLoading;\n const hasOrgs = !isLoading && orgs.length > 0;\n const hasPublisher = !isLoading && publisherId !== null;\n\n const totalItems = products.length;\n const pendingCount = products.filter(\n (p) => p.status === \"PENDING_REVIEW\",\n ).length;\n const publishedCount = products.filter(\n (p) => p.status === \"PUBLISHED\",\n ).length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={\n <>\n {tr(\"devportal.seller\", \"Seller\")}{\" \"}\n <GradientText>{tr(\"devportal.dashboard\", \"Dashboard\")}</GradientText>\n </>\n }\n actions={\n hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/seller/items/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.listAnItem\", \"List an Item\")}\n </button>\n ) : !isLoading ? (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n <div className=\"flex flex-col gap-8\">\n {/* Compact inline stats */}\n <div className=\"flex items-center gap-6 border-b border-border-seam pb-6\">\n {statsLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<Package size={14} />}\n value={totalItems}\n label={tr(\"devportal.items\", \"Items\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Clock size={14} />}\n value={pendingCount}\n label={tr(\"devportal.pendingReview\", \"Pending Review\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<CheckCircle2 size={14} />}\n value={publishedCount}\n label={tr(\"devportal.published\", \"Published\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={orgs.length}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Listings */}\n <DashSection\n title={tr(\"devportal.recentListings\", \"Recent Listings\")}\n action={\n products.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/seller/items\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading || productsLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : products.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {products.slice(0, 3).map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n onClick={() => handleNavigate(\"/seller/items\")}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title={\n !hasPublisher\n ? tr(\n \"devportal.registerAsPublisherFirst\",\n \"Register as publisher first\",\n )\n : tr(\n \"devportal.listYourFirstItem\",\n \"List your first item\",\n )\n }\n description={\n !hasPublisher\n ? tr(\n \"devportal.createOrgToStartSelling\",\n \"Create an organization to start selling physical products\",\n )\n : tr(\n \"devportal.addPhysicalProductsToYourStore\",\n \"Add physical products to your store and they will appear here\",\n )\n }\n action={\n !hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n </button>\n ) : (\n <button\n onClick={() => handleNavigate(\"/seller/items/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.listAnItem\", \"List an Item\")}\n </button>\n )\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || orgs.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n orgs.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {orgs.slice(0, 3).map((org) => (\n <OrgCard\n key={org.id}\n org={org}\n onClick={() =>\n handleNavigate(`/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nconst StatPill: FC<{ icon: ReactNode; value: number; label: string }> = ({\n icon,\n value,\n label,\n}) => (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-muted\">{icon}</span>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n </div>\n);\n\nconst DashSection: FC<{\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}> = ({ title, action, children }) => (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold uppercase tracking-wider text-text-secondary\">\n {title}\n </h2>\n {action}\n </div>\n {children}\n </section>\n);\n\nconst ProductCard: FC<{\n product: PhysicalProduct;\n onClick: () => void;\n}> = ({ product, onClick }) => {\n const badge =\n STATUS_BADGE_STYLES[product.status] ?? STATUS_BADGE_STYLES.DRAFT;\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"h-10 w-10 rounded-xl object-cover shrink-0\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary shrink-0\">\n <Package size={20} />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">\n {product.name}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {formatPrice(product.price, product.currency)}\n </p>\n </div>\n <span\n className={`shrink-0 rounded-full px-2.5 py-0.5 text-xs font-medium ${badge.className}`}\n >\n {badge.label}\n </span>\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <Package size={12} className=\"text-text-muted shrink-0\" />\n <span\n className={\n product.stockQuantity === 0\n ? \"text-status-error-text\"\n : \"text-text-secondary\"\n }\n >\n {product.stockQuantity === 0\n ? \"Out of stock\"\n : `${product.stockQuantity} in stock`}\n </span>\n </div>\n </GlassCard>\n );\n};\n\nconst OrgCard: FC<{ org: OrgSummary; onClick: () => void }> = ({\n org,\n onClick,\n}) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const initials = org.name\n .split(\" \")\n .map((w: string) => w[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold\">\n {initials}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">{org.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {org.publishedApps ?? 0}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n <ArrowRight size={15} className=\"text-text-muted shrink-0\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <Download size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.installs\", \"Installs\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n {(org.totalInstalls ?? 0).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <DollarSign size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.revenue\", \"Revenue\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n ${(org.totalRevenue ?? 0).toLocaleString()}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,533 @@
1
+ import { useDevportal as e } from "../providers/DevportalProvider.js";
2
+ import { EmptyState as t } from "../components/EmptyState.js";
3
+ import { PageLayout as n } from "../components/PageLayout.js";
4
+ import { PermissionGuard as r } from "../components/PermissionGuard.js";
5
+ import "../components/index.js";
6
+ import { DEVPORTAL_PERMISSIONS as i } from "../constants/permissions.js";
7
+ import { useEffect as a, useMemo as o, useState as s } from "react";
8
+ import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
+ import { AlertCircle as l, Edit as u, Grid as ee, List as d, Loader2 as f, Package as p, Plus as m, Search as h, Tag as g } from "lucide-react";
10
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
11
+ import { EmphasisPanel as y, GlassCard as b } from "@burdenoff/fe-libs/ui";
12
+ //#region src/devportal/pages/SellerItemsPage.tsx
13
+ var x = new Set([
14
+ "APP",
15
+ "NODE",
16
+ "WORKFLOW"
17
+ ]), S = {
18
+ MERCHANDISE: "Merchandise",
19
+ HARDWARE: "Hardware",
20
+ ACCESSORY: "Accessory",
21
+ BOOK: "Book",
22
+ EQUIPMENT: "Equipment",
23
+ APPAREL: "Apparel",
24
+ COLLECTIBLE: "Collectible",
25
+ CONSUMABLE: "Consumable"
26
+ }, C = {
27
+ PUBLISHED: {
28
+ label: "Published",
29
+ className: "text-status-success-text bg-status-success-bg-subtle"
30
+ },
31
+ PENDING_REVIEW: {
32
+ label: "Pending Review",
33
+ className: "text-status-warning-text bg-status-warning-bg-subtle"
34
+ },
35
+ DRAFT: {
36
+ label: "Draft",
37
+ className: "text-text-muted bg-bg-muted"
38
+ },
39
+ REJECTED: {
40
+ label: "Rejected",
41
+ className: "text-status-error-text bg-status-error-bg/10"
42
+ },
43
+ INACTIVE: {
44
+ label: "Inactive",
45
+ className: "text-text-muted bg-bg-muted"
46
+ }
47
+ }, te = [
48
+ {
49
+ value: "",
50
+ label: "All Status"
51
+ },
52
+ {
53
+ value: "DRAFT",
54
+ label: "Draft"
55
+ },
56
+ {
57
+ value: "PENDING_REVIEW",
58
+ label: "Pending Review"
59
+ },
60
+ {
61
+ value: "PUBLISHED",
62
+ label: "Published"
63
+ },
64
+ {
65
+ value: "REJECTED",
66
+ label: "Rejected"
67
+ },
68
+ {
69
+ value: "INACTIVE",
70
+ label: "Inactive"
71
+ }
72
+ ];
73
+ function w(e, t) {
74
+ let n = t || "USD";
75
+ return `${new Intl.NumberFormat("en-US", {
76
+ style: "currency",
77
+ currency: n,
78
+ minimumFractionDigits: 2,
79
+ maximumFractionDigits: 2
80
+ }).format(e)} ${n}`;
81
+ }
82
+ function T(e) {
83
+ return S[e] ?? e.replace(/_/g, " ");
84
+ }
85
+ var E = "\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n", D = "\n query SellerItems($publisherId: String!) {\n products(filter: { publisherId: $publisherId, nature: PHYSICAL }, limit: 100, offset: 0) {\n id\n name\n slug\n type\n status\n price\n currency\n stockQuantity\n sku\n brand\n icon\n createdAt\n }\n }\n", O = () => {
86
+ let { navigate: g, basePath: S, apiGatewayUrl: O, authToken: A } = e(), { t: j } = c(), M = (e, t) => {
87
+ let n = j(e);
88
+ return n === e ? t : n;
89
+ }, N = O ?? "/global/graphql", [P, F] = s(!0), [I, L] = s(!1), [R, z] = s(null), [B, V] = s([]), [H, U] = s(null), [W, G] = s(0), [K, q] = s("grid"), [J, ne] = s(""), [Y, re] = s(""), X = (e) => {
90
+ g?.(`${S}${e}`);
91
+ };
92
+ a(() => {
93
+ let e = !1;
94
+ return F(!0), U(null), (async () => {
95
+ try {
96
+ let t = await fetch(N, {
97
+ method: "POST",
98
+ headers: {
99
+ "Content-Type": "application/json",
100
+ Authorization: `Bearer ${A ?? ""}`
101
+ },
102
+ body: JSON.stringify({ query: E })
103
+ });
104
+ if (e) return;
105
+ if (!t.ok) throw Error(`Request failed with status ${t.status}`);
106
+ let n = await t.json();
107
+ if (e) return;
108
+ if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Unknown GraphQL error");
109
+ z(n.data?.myPublisher?.id ?? null);
110
+ } catch (t) {
111
+ e || U(t instanceof Error ? t.message : "Failed to load publisher");
112
+ } finally {
113
+ e || F(!1);
114
+ }
115
+ })(), () => {
116
+ e = !0;
117
+ };
118
+ }, [
119
+ N,
120
+ A,
121
+ W
122
+ ]), a(() => {
123
+ if (!R) return;
124
+ let e = !1;
125
+ return L(!0), U(null), (async () => {
126
+ try {
127
+ let t = await fetch(N, {
128
+ method: "POST",
129
+ headers: {
130
+ "Content-Type": "application/json",
131
+ Authorization: `Bearer ${A ?? ""}`
132
+ },
133
+ body: JSON.stringify({
134
+ query: D,
135
+ variables: { publisherId: R }
136
+ })
137
+ });
138
+ if (e) return;
139
+ if (!t.ok) throw Error(`Request failed with status ${t.status}`);
140
+ let n = await t.json();
141
+ if (e) return;
142
+ if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Unknown GraphQL error");
143
+ V((n.data?.products ?? []).map((e) => ({
144
+ ...e,
145
+ status: e.status
146
+ })));
147
+ } catch (t) {
148
+ e || U(t instanceof Error ? t.message : "Failed to load items");
149
+ } finally {
150
+ e || L(!1);
151
+ }
152
+ })(), () => {
153
+ e = !0;
154
+ };
155
+ }, [
156
+ R,
157
+ N,
158
+ A,
159
+ W
160
+ ]);
161
+ let Z = o(() => B.filter((e) => !x.has(e.type)), [B]), Q = o(() => Z.filter((e) => {
162
+ let t = !J || e.name.toLowerCase().includes(J.toLowerCase()), n = !Y || e.status === Y;
163
+ return t && n;
164
+ }), [
165
+ Z,
166
+ J,
167
+ Y
168
+ ]), $ = o(() => ({
169
+ total: Z.length,
170
+ published: Z.filter((e) => e.status === "PUBLISHED").length,
171
+ pendingReview: Z.filter((e) => e.status === "PENDING_REVIEW").length,
172
+ outOfStock: Z.filter((e) => e.stockQuantity === 0).length
173
+ }), [Z]);
174
+ if (P && B.length === 0 && !H) return /* @__PURE__ */ _(n, {
175
+ title: M("devportal.myItems", "My Items"),
176
+ description: M("devportal.manageYourPhysicalProductListings", "Manage your physical product listings."),
177
+ children: /* @__PURE__ */ v("div", {
178
+ className: "flex items-center justify-center py-12",
179
+ children: [/* @__PURE__ */ _(f, { className: "h-8 w-8 animate-spin text-text-secondary" }), /* @__PURE__ */ _("span", {
180
+ className: "ml-2 text-text-secondary",
181
+ children: M("devportal.loadingItems", "Loading items...")
182
+ })]
183
+ })
184
+ });
185
+ if (H && B.length === 0) return /* @__PURE__ */ _(n, {
186
+ title: M("devportal.myItems", "My Items"),
187
+ description: M("devportal.manageYourPhysicalProductListings", "Manage your physical product listings."),
188
+ children: /* @__PURE__ */ v("div", {
189
+ className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center",
190
+ children: [
191
+ /* @__PURE__ */ _(l, { className: "mx-auto h-8 w-8 text-status-error-text" }),
192
+ /* @__PURE__ */ _("h3", {
193
+ className: "mt-2 font-medium text-status-error-text",
194
+ children: M("devportal.failedToLoadItems", "Failed to load items")
195
+ }),
196
+ /* @__PURE__ */ _("p", {
197
+ className: "mt-1 text-sm text-text-secondary",
198
+ children: H
199
+ }),
200
+ /* @__PURE__ */ _("button", {
201
+ onClick: () => G((e) => e + 1),
202
+ className: "mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
203
+ children: M("devportal.tryAgain", "Try Again")
204
+ })
205
+ ]
206
+ })
207
+ });
208
+ let ie = !!(J || Y);
209
+ return /* @__PURE__ */ _(r, {
210
+ permission: i.APP_READ,
211
+ children: /* @__PURE__ */ v(n, {
212
+ title: M("devportal.myItems", "My Items"),
213
+ description: M("devportal.manageYourPhysicalProductListings", "Manage your physical product listings."),
214
+ actions: /* @__PURE__ */ v("button", {
215
+ onClick: () => X("/seller/items/new"),
216
+ className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
217
+ children: [/* @__PURE__ */ _(m, {
218
+ size: 16,
219
+ "aria-hidden": "true"
220
+ }), M("devportal.listAnItem", "List an Item")]
221
+ }),
222
+ children: [
223
+ /* @__PURE__ */ _(y, {
224
+ className: "mb-4",
225
+ children: /* @__PURE__ */ v("div", {
226
+ className: "grid grid-cols-2 gap-4 sm:grid-cols-4",
227
+ children: [
228
+ /* @__PURE__ */ v(b, {
229
+ treatment: "metric",
230
+ className: "p-4",
231
+ children: [/* @__PURE__ */ _("p", {
232
+ className: "text-sm text-text-secondary",
233
+ children: M("devportal.totalItems", "Total Items")
234
+ }), /* @__PURE__ */ _("p", {
235
+ className: "mt-1 text-2xl font-semibold tabular-nums",
236
+ children: I ? /* @__PURE__ */ _(f, { className: "h-5 w-5 animate-spin text-text-secondary" }) : $.total
237
+ })]
238
+ }),
239
+ /* @__PURE__ */ v(b, {
240
+ treatment: "metric",
241
+ className: "p-4",
242
+ children: [/* @__PURE__ */ _("p", {
243
+ className: "text-sm text-status-success-text",
244
+ children: M("devportal.published", "Published")
245
+ }), /* @__PURE__ */ _("p", {
246
+ className: "mt-1 text-2xl font-semibold tabular-nums",
247
+ children: I ? /* @__PURE__ */ _(f, { className: "h-5 w-5 animate-spin text-text-secondary" }) : $.published
248
+ })]
249
+ }),
250
+ /* @__PURE__ */ v(b, {
251
+ treatment: "metric",
252
+ className: "p-4",
253
+ children: [/* @__PURE__ */ _("p", {
254
+ className: "text-sm text-status-warning-text",
255
+ children: M("devportal.pendingReview", "Pending Review")
256
+ }), /* @__PURE__ */ _("p", {
257
+ className: "mt-1 text-2xl font-semibold tabular-nums",
258
+ children: I ? /* @__PURE__ */ _(f, { className: "h-5 w-5 animate-spin text-text-secondary" }) : $.pendingReview
259
+ })]
260
+ }),
261
+ /* @__PURE__ */ v(b, {
262
+ treatment: "metric",
263
+ className: "p-4",
264
+ children: [/* @__PURE__ */ _("p", {
265
+ className: "text-sm text-status-error-text",
266
+ children: M("devportal.outOfStock", "Out of Stock")
267
+ }), /* @__PURE__ */ _("p", {
268
+ className: "mt-1 text-2xl font-semibold tabular-nums",
269
+ children: I ? /* @__PURE__ */ _(f, { className: "h-5 w-5 animate-spin text-text-secondary" }) : $.outOfStock
270
+ })]
271
+ })
272
+ ]
273
+ })
274
+ }),
275
+ /* @__PURE__ */ v("div", {
276
+ className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
277
+ children: [/* @__PURE__ */ v("div", {
278
+ className: "flex flex-1 items-center gap-3",
279
+ children: [/* @__PURE__ */ v("div", {
280
+ className: "relative flex-1 max-w-md",
281
+ children: [/* @__PURE__ */ _(h, {
282
+ size: 16,
283
+ className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary"
284
+ }), /* @__PURE__ */ _("input", {
285
+ type: "text",
286
+ placeholder: M("devportal.searchItems", "Search items..."),
287
+ value: J,
288
+ onChange: (e) => ne(e.target.value),
289
+ className: "w-full rounded-md border border-border-seam bg-bg-surface pl-9 pr-4 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-primary"
290
+ })]
291
+ }), /* @__PURE__ */ _("select", {
292
+ value: Y,
293
+ onChange: (e) => re(e.target.value),
294
+ className: "rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-primary",
295
+ children: te.map((e) => /* @__PURE__ */ _("option", {
296
+ value: e.value,
297
+ children: e.label
298
+ }, e.value))
299
+ })]
300
+ }), /* @__PURE__ */ v("div", {
301
+ className: "flex items-center gap-1 rounded-md border border-border-seam p-1",
302
+ children: [/* @__PURE__ */ _("button", {
303
+ onClick: () => q("grid"),
304
+ className: `rounded p-1.5 transition-colors ${K === "grid" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:bg-bg-sunken/50"}`,
305
+ title: M("devportal.gridView", "Grid view"),
306
+ children: /* @__PURE__ */ _(ee, { size: 16 })
307
+ }), /* @__PURE__ */ _("button", {
308
+ onClick: () => q("list"),
309
+ className: `rounded p-1.5 transition-colors ${K === "list" ? "bg-bg-sunken text-text-primary" : "text-text-secondary hover:bg-bg-sunken/50"}`,
310
+ title: M("devportal.listView", "List view"),
311
+ children: /* @__PURE__ */ _(d, { size: 16 })
312
+ })]
313
+ })]
314
+ }),
315
+ Z.length > 0 && /* @__PURE__ */ v("p", {
316
+ className: "text-sm text-text-secondary",
317
+ children: [
318
+ Q.length,
319
+ " item",
320
+ Q.length === 1 ? "" : "s",
321
+ " found"
322
+ ]
323
+ }),
324
+ !R && !P ? /* @__PURE__ */ _(t, {
325
+ illustration: "empty-generic",
326
+ icon: /* @__PURE__ */ _(p, { size: 32 }),
327
+ title: M("devportal.noPublisherProfileYet", "No publisher profile yet"),
328
+ description: M("devportal.listAnItemToGetStarted", "List an item to get started. Your publisher profile will be created automatically."),
329
+ action: /* @__PURE__ */ v("button", {
330
+ onClick: () => X("/seller/items/new"),
331
+ className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
332
+ children: [/* @__PURE__ */ _(m, { size: 16 }), M("devportal.listAnItem", "List an Item")]
333
+ })
334
+ }) : I ? /* @__PURE__ */ v("div", {
335
+ className: "flex items-center justify-center py-12",
336
+ children: [/* @__PURE__ */ _(f, { className: "h-6 w-6 animate-spin text-text-secondary" }), /* @__PURE__ */ _("span", {
337
+ className: "ml-2 text-text-secondary",
338
+ children: M("devportal.loadingItems", "Loading items...")
339
+ })]
340
+ }) : Q.length > 0 ? K === "grid" ? /* @__PURE__ */ _("div", {
341
+ className: "grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
342
+ children: Q.map((e) => /* @__PURE__ */ _(k, {
343
+ item: e,
344
+ onEdit: () => X(`/seller/items/${e.id}`)
345
+ }, e.id))
346
+ }) : /* @__PURE__ */ _("div", {
347
+ className: "overflow-x-auto rounded-lg border border-border-seam",
348
+ children: /* @__PURE__ */ v("table", {
349
+ className: "w-full text-sm",
350
+ children: [/* @__PURE__ */ _("thead", { children: /* @__PURE__ */ v("tr", {
351
+ className: "border-b border-border-seam bg-bg-sunken",
352
+ children: [
353
+ /* @__PURE__ */ _("th", {
354
+ className: "px-4 py-3 text-left font-medium text-text-secondary",
355
+ children: "Item"
356
+ }),
357
+ /* @__PURE__ */ _("th", {
358
+ className: "px-4 py-3 text-left font-medium text-text-secondary",
359
+ children: "Type"
360
+ }),
361
+ /* @__PURE__ */ _("th", {
362
+ className: "px-4 py-3 text-left font-medium text-text-secondary",
363
+ children: "Price"
364
+ }),
365
+ /* @__PURE__ */ _("th", {
366
+ className: "px-4 py-3 text-left font-medium text-text-secondary",
367
+ children: "Stock"
368
+ }),
369
+ /* @__PURE__ */ _("th", {
370
+ className: "px-4 py-3 text-left font-medium text-text-secondary",
371
+ children: "Status"
372
+ }),
373
+ /* @__PURE__ */ _("th", {
374
+ className: "px-4 py-3 text-right font-medium text-text-secondary",
375
+ children: "Actions"
376
+ })
377
+ ]
378
+ }) }), /* @__PURE__ */ _("tbody", { children: Q.map((e) => {
379
+ let t = C[e.status] ?? C.DRAFT;
380
+ return /* @__PURE__ */ v("tr", {
381
+ className: "border-b border-border-subtle last:border-0 hover:bg-bg-sunken/50 transition-colors",
382
+ children: [
383
+ /* @__PURE__ */ _("td", {
384
+ className: "px-4 py-3",
385
+ children: /* @__PURE__ */ v("div", {
386
+ className: "flex items-center gap-3",
387
+ children: [e.icon ? /* @__PURE__ */ _("img", {
388
+ src: e.icon,
389
+ alt: e.name,
390
+ className: "h-8 w-8 rounded-lg object-cover shrink-0"
391
+ }) : /* @__PURE__ */ _("div", {
392
+ className: "flex h-8 w-8 items-center justify-center rounded-lg bg-bg-sunken shrink-0",
393
+ children: /* @__PURE__ */ _(p, {
394
+ size: 14,
395
+ className: "text-text-muted"
396
+ })
397
+ }), /* @__PURE__ */ v("div", {
398
+ className: "min-w-0",
399
+ children: [/* @__PURE__ */ _("p", {
400
+ className: "font-medium text-text-primary truncate",
401
+ children: e.name
402
+ }), e.sku && /* @__PURE__ */ v("p", {
403
+ className: "text-xs text-text-muted",
404
+ children: ["SKU: ", e.sku]
405
+ })]
406
+ })]
407
+ })
408
+ }),
409
+ /* @__PURE__ */ _("td", {
410
+ className: "px-4 py-3 text-text-secondary",
411
+ children: T(e.type)
412
+ }),
413
+ /* @__PURE__ */ _("td", {
414
+ className: "px-4 py-3 text-text-primary tabular-nums",
415
+ children: w(e.price, e.currency)
416
+ }),
417
+ /* @__PURE__ */ _("td", {
418
+ className: "px-4 py-3",
419
+ children: e.stockQuantity === 0 ? /* @__PURE__ */ _("span", {
420
+ className: "text-status-error-text",
421
+ children: "Out of stock"
422
+ }) : /* @__PURE__ */ _("span", {
423
+ className: "text-text-primary tabular-nums",
424
+ children: e.stockQuantity
425
+ })
426
+ }),
427
+ /* @__PURE__ */ _("td", {
428
+ className: "px-4 py-3",
429
+ children: /* @__PURE__ */ _("span", {
430
+ className: `rounded-full px-2.5 py-0.5 text-xs font-medium ${t.className}`,
431
+ children: t.label
432
+ })
433
+ }),
434
+ /* @__PURE__ */ _("td", {
435
+ className: "px-4 py-3 text-right",
436
+ children: /* @__PURE__ */ v("button", {
437
+ onClick: () => X(`/seller/items/${e.id}`),
438
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken transition-colors",
439
+ children: [/* @__PURE__ */ _(u, { size: 12 }), M("devportal.edit", "Edit")]
440
+ })
441
+ })
442
+ ]
443
+ }, e.id);
444
+ }) })]
445
+ })
446
+ }) : Z.length > 0 && ie ? /* @__PURE__ */ _(t, {
447
+ illustration: "empty-search",
448
+ title: M("devportal.noItemsMatchYourSearch", "No items match your search"),
449
+ description: M("devportal.tryAdjustingFilters", "Try adjusting your search or filters")
450
+ }) : /* @__PURE__ */ _(t, {
451
+ illustration: "empty-data",
452
+ icon: /* @__PURE__ */ _(p, { size: 32 }),
453
+ title: M("devportal.noItemsYet", "No items yet"),
454
+ description: M("devportal.addYourFirstPhysicalProduct", "Add your first physical product listing to start selling"),
455
+ action: /* @__PURE__ */ v("button", {
456
+ onClick: () => X("/seller/items/new"),
457
+ className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
458
+ children: [/* @__PURE__ */ _(m, { size: 16 }), M("devportal.listAnItem", "List an Item")]
459
+ })
460
+ })
461
+ ]
462
+ })
463
+ });
464
+ }, k = ({ item: e, onEdit: t }) => {
465
+ let n = C[e.status] ?? C.DRAFT;
466
+ return /* @__PURE__ */ v(b, {
467
+ treatment: "insight",
468
+ className: "flex flex-col gap-3 p-4",
469
+ children: [/* @__PURE__ */ v("div", {
470
+ className: "flex items-start gap-3",
471
+ children: [e.icon ? /* @__PURE__ */ _("img", {
472
+ src: e.icon,
473
+ alt: e.name,
474
+ className: "h-20 w-20 rounded-xl object-cover shrink-0"
475
+ }) : /* @__PURE__ */ _("div", {
476
+ className: "flex h-20 w-20 items-center justify-center rounded-xl bg-bg-sunken shrink-0",
477
+ children: /* @__PURE__ */ _(p, {
478
+ size: 32,
479
+ className: "text-text-muted"
480
+ })
481
+ }), /* @__PURE__ */ v("div", {
482
+ className: "min-w-0 flex-1",
483
+ children: [
484
+ /* @__PURE__ */ _("p", {
485
+ className: "font-semibold text-text-primary truncate",
486
+ children: e.name
487
+ }),
488
+ e.brand && /* @__PURE__ */ _("p", {
489
+ className: "text-xs text-text-muted mt-0.5",
490
+ children: e.brand
491
+ }),
492
+ /* @__PURE__ */ _("p", {
493
+ className: "text-sm text-text-secondary mt-1",
494
+ children: w(e.price, e.currency)
495
+ }),
496
+ /* @__PURE__ */ v("div", {
497
+ className: "flex items-center gap-2 mt-2 flex-wrap",
498
+ children: [/* @__PURE__ */ _("span", {
499
+ className: `rounded-full px-2 py-0.5 text-xs font-medium ${n.className}`,
500
+ children: n.label
501
+ }), /* @__PURE__ */ _("span", {
502
+ className: "rounded-full px-2 py-0.5 text-xs font-medium bg-bg-muted text-text-secondary",
503
+ children: T(e.type)
504
+ })]
505
+ })
506
+ ]
507
+ })]
508
+ }), /* @__PURE__ */ v("div", {
509
+ className: "flex items-center justify-between pt-2 border-t border-border-subtle",
510
+ children: [/* @__PURE__ */ v("div", {
511
+ className: "flex items-center gap-1.5 text-sm",
512
+ children: [/* @__PURE__ */ _(g, {
513
+ size: 12,
514
+ className: "text-text-muted shrink-0"
515
+ }), e.stockQuantity === 0 ? /* @__PURE__ */ _("span", {
516
+ className: "text-status-error-text",
517
+ children: "Out of stock"
518
+ }) : /* @__PURE__ */ v("span", {
519
+ className: "text-text-secondary",
520
+ children: [e.stockQuantity, " in stock"]
521
+ })]
522
+ }), /* @__PURE__ */ v("button", {
523
+ onClick: t,
524
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-secondary hover:text-text-primary hover:bg-bg-sunken transition-colors",
525
+ children: [/* @__PURE__ */ _(u, { size: 12 }), "Edit"]
526
+ })]
527
+ })]
528
+ });
529
+ };
530
+ //#endregion
531
+ export { O as SellerItemsPage };
532
+
533
+ //# sourceMappingURL=SellerItemsPage.js.map