@burdenoff/microfe-store 2026.703.1 → 2026.704.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.
- package/dist/components/InstallAppModal.js +305 -140
- package/dist/components/InstallAppModal.js.map +1 -1
- package/dist/generated/global-operations.js +7 -0
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.js.map +1 -1
- package/dist/hooks/useInstallations.js.map +1 -1
- package/dist/hooks/useStoreGraphQL.js +42 -34
- package/dist/hooks/useStoreGraphQL.js.map +1 -1
- package/dist/hooks/useStoreGraphQLWithContext.js +10 -7
- package/dist/hooks/useStoreGraphQLWithContext.js.map +1 -1
- package/dist/pages/AdminDashboardPage.js +451 -378
- package/dist/pages/AdminDashboardPage.js.map +1 -1
- package/dist/pages/AdminReviewModerationPage.js +182 -150
- package/dist/pages/AdminReviewModerationPage.js.map +1 -1
- package/dist/pages/AdminStoresPage.js.map +1 -1
- package/dist/pages/AdminSubmissionsQueuePage.js +642 -305
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/AppDetailPage.js +200 -153
- package/dist/pages/AppDetailPage.js.map +1 -1
- package/dist/pages/AppSettingsPage.js.map +1 -1
- package/dist/pages/CartPage.js.map +1 -1
- package/dist/pages/InstalledAppsPage.js +335 -154
- package/dist/pages/InstalledAppsPage.js.map +1 -1
- package/dist/pages/MyLicensesPage.js.map +1 -1
- package/dist/pages/OrdersPage.js +5 -5
- package/dist/pages/ProductDetailPage.js +505 -451
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport { ArrowLeft, Check, X, RefreshCw, PackageCheck, ChevronDown, ChevronUp } from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\nimport { PagePurpose, IllustratedEmptyState, NextSteps } from '@burdenoff/fe-libs/ui';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL';\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'All', value: 'ALL' },\n];\n\nexport const AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n\n const loadProducts = useCallback(\n async (filter: ProductStatus) => {\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n },\n [authToken]\n );\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [loadProducts, statusFilter]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n return (\n <div className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <header className=\"border-b border-border-seam bg-surface-raised\">\n <div className=\"mx-auto max-w-7xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm text-text-secondary hover:bg-surface-hover hover:text-text-primary\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n <div className=\"flex items-center gap-2\">\n <PackageCheck className=\"size-5 text-text-primary\" />\n <h1 className=\"text-xl font-semibold text-text-primary\">Submission queue</h1>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {totalCount}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-default bg-surface-default px-3 py-1.5 text-sm text-text-primary hover:bg-surface-hover\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-4 flex gap-2\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-surface-accent text-text-on-accent'\n : 'bg-surface-muted text-text-secondary hover:bg-surface-hover'\n }`}\n >\n {f.label}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-7xl p-6\">\n <PagePurpose className=\"mb-4\">\n This is the marketplace gatekeeper. Every product a publisher submits lands here for\n review before customers can see it — open each one to inspect its details, pricing and\n publisher, then approve it to go live or reject it with a clear reason the publisher will\n read. Keeping this queue empty means new apps reach customers quickly.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"store-admin-submissions\"\n steps={[\n {\n id: 'review-pending',\n label: 'Review the pending queue',\n description: 'Start with submissions awaiting review and expand Details to inspect.',\n onClick: () => setStatusFilter('PENDING_REVIEW'),\n },\n {\n id: 'approve',\n label: 'Approve solid submissions',\n description: 'Approve a product to publish it instantly to the marketplace.',\n done: statusFilter === 'PUBLISHED' && products.length > 0,\n },\n {\n id: 'reject-clear',\n label: 'Reject with a clear reason',\n description: 'Rejections require a reason — publishers see it, so be specific.',\n },\n ]}\n />\n\n {error && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md border border-border-danger bg-surface-danger-muted px-4 py-3 text-sm text-text-danger\"\n >\n {error}\n </div>\n )}\n\n {loading && products.length === 0 ? (\n <output className=\"block py-16 text-center text-text-secondary\">\n Loading submissions…\n </output>\n ) : products.length === 0 ? (\n <IllustratedEmptyState\n illustration={statusFilter === 'PENDING_REVIEW' ? 'empty-notifications' : 'empty-data'}\n title={\n statusFilter === 'PENDING_REVIEW'\n ? 'Queue is clear'\n : 'No products match this filter'\n }\n description={\n statusFilter === 'PENDING_REVIEW'\n ? 'There are no submissions awaiting review right now. New submissions will appear here as publishers send them in.'\n : 'Nothing matches the current status filter. Switch filters above to see products in other states.'\n }\n />\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-surface-raised transition-[box-shadow,border-color] hover:border-border-strong hover:shadow-[var(--shadow-pop)]\"\n >\n <div className=\"flex items-start gap-4 p-4\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-14 flex-shrink-0 rounded-md object-cover\"\n />\n ) : (\n <div className=\"flex size-14 flex-shrink-0 items-center justify-center rounded-md bg-surface-muted text-xs text-text-secondary\">\n {p.type}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {p.name}\n </h3>\n <span className=\"rounded-full bg-surface-accent-muted px-2 py-0.5 text-xs font-medium text-text-accent\">\n {p.type}\n </span>\n <span className=\"rounded-full bg-surface-muted px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {p.nature}\n </span>\n </div>\n <div className=\"mt-0.5 text-xs text-text-tertiary\">{p.slug}</div>\n <div className=\"mt-1 flex items-center gap-3 text-xs text-text-secondary\">\n <span>Publisher: {p.publisher?.name ?? 'Unknown'}</span>\n <span>Price: {formatPriceForModel(p.price, p.currency, p.pricingModel)}</span>\n {p.submittedAt && (\n <span>Submitted {new Date(p.submittedAt).toLocaleString()}</span>\n )}\n </div>\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">\n {p.description}\n </p>\n )}\n </div>\n <div className=\"flex flex-shrink-0 flex-col gap-2\">\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-default px-3 py-1.5 text-xs text-text-secondary hover:bg-surface-hover\"\n >\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n Details\n </button>\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-surface-success px-3 py-1.5 text-xs font-medium text-text-on-success hover:bg-surface-success-hover disabled:opacity-60\"\n >\n <Check className=\"size-3.5\" />\n Approve\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-danger bg-surface-danger-muted px-3 py-1.5 text-xs font-medium text-text-danger hover:bg-surface-danger hover:text-text-on-danger disabled:opacity-60\"\n >\n <X className=\"size-3.5\" />\n Reject\n </button>\n </div>\n </div>\n\n {viewingId === p.id && (\n <div className=\"border-t border-border-seam bg-surface-muted p-4\">\n <dl className=\"grid grid-cols-2 gap-x-6 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-text-secondary\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.slug}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Type</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.type}</dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Nature</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.nature}</dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Category</dt>\n <dd className=\"mt-0.5 text-text-primary\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-text-secondary\">Pricing</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-text-tertiary\">({p.pricingModel})</span>\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-text-secondary\">Publisher</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {p.publisher?.name ?? '—'}\n {p.publisher?.email && (\n <span className=\"block text-text-tertiary\">{p.publisher.email}</span>\n )}\n </dd>\n </div>\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted by</dt>\n <dd className=\"mt-0.5 font-mono text-text-primary\">{p.submittedBy}</dd>\n </div>\n )}\n {p.submittedAt && (\n <div>\n <dt className=\"font-medium text-text-secondary\">Submitted at</dt>\n <dd className=\"mt-0.5 text-text-primary\">\n {new Date(p.submittedAt).toLocaleString()}\n </dd>\n </div>\n )}\n {p.description && (\n <div className=\"col-span-2 sm:col-span-3\">\n <dt className=\"font-medium text-text-secondary\">Description</dt>\n <dd className=\"mt-0.5 whitespace-pre-wrap text-text-primary\">\n {p.description}\n </dd>\n </div>\n )}\n </dl>\n </div>\n )}\n\n {rejectingId === p.id && (\n <div className=\"border-t border-border-seam bg-surface-muted px-4 py-3\">\n <label\n htmlFor={`reject-reason-${p.id}`}\n className=\"block text-xs font-medium text-text-secondary\"\n >\n Rejection reason (required)\n </label>\n <textarea\n id={`reject-reason-${p.id}`}\n aria-label=\"Rejection reason\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\n className=\"mt-1 w-full rounded-md border border-border-default bg-surface-default px-3 py-2 text-sm text-text-primary focus:border-border-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n placeholder=\"Explain why this submission is being rejected…\"\n />\n <div className=\"mt-2 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\n }}\n className=\"rounded-md border border-border-default bg-surface-default px-3 py-1 text-xs text-text-primary hover:bg-surface-hover\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.id}\n className=\"rounded-md bg-surface-danger px-3 py-1 text-xs font-medium text-text-on-danger hover:bg-surface-danger-hover disabled:opacity-60\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n ArrowLeft,\n Check,\n X,\n RefreshCw,\n PackageCheck,\n ChevronDown,\n ChevronUp,\n ExternalLink,\n FileJson,\n ShieldCheck,\n FlaskConical,\n Download,\n Clock,\n Tag,\n User,\n Hash,\n GitBranch,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { InstallAppModal } from '../components/InstallAppModal';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n productId?: string | null;\n manifest?: Record<string, unknown> | null;\n requiredPermissions?: string[] | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL' | 'VERSION_UPDATES';\n\ntype PendingVersion = {\n id: string;\n version: string;\n status: string;\n releaseNotes?: string | null;\n packageUrl?: string | null;\n packageSize?: number | null;\n isLatest?: boolean | null;\n reviewNotes?: string | null;\n manifest?: Record<string, unknown> | null;\n createdAt: string;\n};\n\ntype VersionUpdateApp = {\n id: string;\n name: string;\n slug: string;\n iconUrl?: string | null;\n shortDescription?: string | null;\n description?: string | null;\n versions: { edges: Array<{ node: PendingVersion }> };\n};\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst VERSION_UPDATES_QUERY = /* GraphQL */ `\n query AdminPendingVersionApps($limit: Int, $offset: Int) {\n applicationsWithPendingVersions(limit: $limit, offset: $offset) {\n totalCount\n edges {\n node {\n id\n name\n slug\n iconUrl\n shortDescription\n description\n versions(first: 5) {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n packageSize\n isLatest\n reviewNotes\n manifest\n createdAt\n }\n }\n }\n }\n }\n }\n }\n`;\n\nconst APPROVE_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\nconst REJECT_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'Version Updates', value: 'VERSION_UPDATES' },\n { label: 'All', value: 'ALL' },\n];\n\nconst TYPE_COLORS: Record<string, string> = {\n NODE: 'bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]',\n WORKFLOW: 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n INTEGRATION: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n VIBEMODULE: 'bg-[var(--color-surface-accent-muted)] text-[var(--color-text-accent)]',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n PENDING_REVIEW:\n 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n PUBLISHED: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n REJECTED: 'bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)]',\n};\n\nconst PACKAGE_TYPES = new Set(['NODE', 'WORKFLOW', 'VIBEMODULE', 'PARSER', 'DATASINK', 'CONSOLE']);\n\nfunction typeColor(type: string) {\n return TYPE_COLORS[type] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n}\n\nfunction statusColor(status: string) {\n return (\n STATUS_COLORS[status] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]'\n );\n}\n\nfunction formatDate(iso?: string | null) {\n if (!iso) return '—';\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport const AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n const [testingProduct, setTestingProduct] = useState<PendingProduct | null>(null);\n const [versionApps, setVersionApps] = useState<VersionUpdateApp[]>([]);\n const [versionUpdateCount, setVersionUpdateCount] = useState(0);\n const [versionActingId, setVersionActingId] = useState<string | null>(null);\n const [versionRejectingId, setVersionRejectingId] = useState<string | null>(null);\n const [versionRejectReason, setVersionRejectReason] = useState('');\n const [versionRejectAppId, setVersionRejectAppId] = useState<string | null>(null);\n\n const loadVersionApps = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n applicationsWithPendingVersions: {\n totalCount: number;\n edges: Array<{ node: VersionUpdateApp }>;\n };\n }>({\n gateway: 'global',\n query: VERSION_UPDATES_QUERY,\n variables: { limit: 50, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length)\n throw new Error(result.errors[0]?.message ?? 'Failed to load version updates');\n const edges = result.data?.applicationsWithPendingVersions.edges ?? [];\n setVersionApps(edges.map((e) => e.node));\n setVersionUpdateCount(result.data?.applicationsWithPendingVersions.totalCount ?? 0);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load version updates');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n const approveVersion = async (versionId: string, appId: string, notes: string) => {\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_VERSION_MUTATION,\n variables: { id: versionId, notes: notes || null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Approval failed');\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const rejectVersion = async (versionId: string, appId: string, reason: string) => {\n if (!reason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_VERSION_MUTATION,\n variables: { id: versionId, reason: reason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const loadProducts = useCallback(\n async (filter: ProductStatus) => {\n if (filter === 'VERSION_UPDATES') {\n await loadVersionApps();\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n },\n [authToken, loadVersionApps]\n );\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [loadProducts, statusFilter]);\n\n // Keep version update count fresh for the badge even when not on that tab\n useEffect(() => {\n void loadVersionApps();\n }, [loadVersionApps]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const pendingCount = products.filter((p) => p.status === 'PENDING_REVIEW').length;\n\n return (\n <div className=\"min-h-screen bg-[var(--color-surface-default)]\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"mx-auto max-w-5xl px-6 py-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-sm text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ArrowLeft className=\"size-4 shrink-0\" />\n Back\n </button>\n <div className=\"h-5 w-px bg-[var(--color-border-seam)]\" />\n <PackageCheck className=\"size-5 shrink-0 text-[var(--color-text-primary)]\" />\n <h1 className=\"text-lg font-semibold text-[var(--color-text-primary)] truncate\">\n Submission Queue\n </h1>\n {totalCount > 0 && (\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-xs font-semibold text-[var(--color-status-warning-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"shrink-0 inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors disabled:opacity-50\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-3 flex gap-1.5 flex-wrap\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3.5 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]'\n : 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]'\n }`}\n >\n {f.label}\n {f.value === 'PENDING_REVIEW' &&\n pendingCount > 0 &&\n statusFilter !== 'PENDING_REVIEW' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-warning-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {pendingCount}\n </span>\n )}\n {f.value === 'VERSION_UPDATES' &&\n versionUpdateCount > 0 &&\n statusFilter !== 'VERSION_UPDATES' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-info-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {versionUpdateCount}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-5xl space-y-4 p-6\">\n <PagePurpose className=\"mb-2\">\n Every publisher submission lands here for your review. Inspect the manifest, permissions,\n and package details — then approve to publish or reject with a clear reason the developer\n will see.\n </PagePurpose>\n\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-3 text-sm text-[var(--color-text-danger)]\"\n >\n {error}\n </div>\n )}\n\n {statusFilter === 'VERSION_UPDATES' ? (\n loading && versionApps.length === 0 ? (\n <div className=\"flex items-center justify-center py-24\">\n <RefreshCw className=\"size-6 animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : versionApps.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No pending version updates\"\n description=\"All published apps are up to date. When a developer submits a new version, it will appear here for your review.\"\n />\n ) : (\n <ul className=\"space-y-4\">\n {versionApps.map((app) => {\n const pendingVersions = app.versions.edges\n .map((e) => e.node)\n .filter((v) => v.status === 'pending_review');\n if (pendingVersions.length === 0) return null;\n return (\n <li\n key={app.id}\n className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\"\n >\n {/* App header */}\n <div className=\"flex items-center gap-4 p-5 border-b border-[var(--color-border-seam)]\">\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"size-12 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {app.name.slice(0, 2)}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {app.name}\n </h3>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-[var(--color-status-info-bg-subtle)] px-2 py-0.5 text-[11px] font-semibold text-[var(--color-status-info-text)]\">\n <GitBranch className=\"size-3\" />\n {pendingVersions.length} version\n {pendingVersions.length !== 1 ? 's' : ''} pending\n </span>\n </div>\n {(app.shortDescription ?? app.description) && (\n <p className=\"mt-0.5 text-sm text-[var(--color-text-secondary)] line-clamp-1\">\n {app.shortDescription ?? app.description}\n </p>\n )}\n </div>\n <a\n href={`/devportal/apps/${app.id}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"shrink-0 inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n </div>\n\n {/* Pending versions */}\n <div className=\"divide-y divide-[var(--color-border-seam)]\">\n {pendingVersions.map((ver) => (\n <div key={ver.id} className=\"p-5 space-y-3\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <GitBranch className=\"size-4 text-[var(--color-text-secondary)] shrink-0\" />\n <span className=\"font-mono font-semibold text-sm text-[var(--color-text-primary)]\">\n {ver.version}\n </span>\n {ver.isLatest && (\n <span className=\"rounded-full bg-[var(--color-surface-muted)] px-2 py-0.5 text-[10px] text-[var(--color-text-secondary)]\">\n Latest\n </span>\n )}\n <span className=\"rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-[10px] font-medium text-[var(--color-status-warning-text)] uppercase\">\n New Version\n </span>\n <span className=\"ml-auto flex items-center gap-1 text-xs text-[var(--color-text-secondary)]\">\n <Clock className=\"size-3\" />\n {formatDate(ver.createdAt)}\n </span>\n </div>\n\n {ver.releaseNotes && (\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {ver.releaseNotes}\n </p>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {ver.packageUrl && (\n <a\n href={ver.packageUrl}\n download\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Download Package\n {ver.packageSize != null && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({ver.packageSize} bytes)\n </span>\n )}\n </a>\n )}\n {ver.manifest && (\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === ver.id ? null : ver.id)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === ver.id ? 'Hide manifest' : 'View manifest'}\n {viewingId === ver.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n )}\n </div>\n\n {viewingId === ver.id && ver.manifest && (\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-48 overflow-y-auto\">\n {JSON.stringify(ver.manifest, null, 2)}\n </pre>\n )}\n\n {/* Reject reason input */}\n {versionRejectingId === ver.id && versionRejectAppId === app.id && (\n <div className=\"rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)]/30 p-3 space-y-2\">\n <p className=\"text-xs font-semibold text-[var(--color-text-danger)]\">\n Rejection reason (the developer will see this)\n </p>\n <textarea\n value={versionRejectReason}\n onChange={(e) => setVersionRejectReason(e.target.value)}\n rows={2}\n className=\"w-full rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"Be specific — what needs to change before this version can be approved…\"\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\n }}\n className=\"rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() =>\n void rejectVersion(ver.id, app.id, versionRejectReason)\n }\n disabled={\n !versionRejectReason.trim() || versionActingId === ver.id\n }\n className=\"rounded-md bg-[var(--color-surface-danger)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text-on-danger)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void approveVersion(ver.id, app.id, '')}\n disabled={versionActingId === ver.id}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-[var(--color-surface-success)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-on-success)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {versionActingId === ver.id ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n Approve version\n </button>\n {versionRejectingId !== ver.id && (\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(ver.id);\n setVersionRejectReason('');\n setVersionRejectAppId(app.id);\n }}\n disabled={versionActingId === ver.id}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-danger)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject version\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n </li>\n );\n })}\n </ul>\n )\n ) : loading && products.length === 0 ? (\n <div className=\"flex items-center justify-center py-24\">\n <RefreshCw className=\"size-6 animate-spin text-[var(--color-text-muted)]\" />\n </div>\n ) : products.length === 0 ? (\n <IllustratedEmptyState\n illustration={statusFilter === 'PENDING_REVIEW' ? 'empty-notifications' : 'empty-data'}\n title={\n statusFilter === 'PENDING_REVIEW' ? 'Queue is clear' : 'No products match this filter'\n }\n description={\n statusFilter === 'PENDING_REVIEW'\n ? 'No submissions are awaiting review. New submissions will appear here as publishers send them in.'\n : 'Nothing matches the current filter. Try switching to a different status above.'\n }\n />\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)] transition-[box-shadow,border-color] hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)]\"\n >\n {/* Card body */}\n <div className=\"flex items-start gap-4 p-5\">\n {/* App icon */}\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-14 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-14 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {p.type.slice(0, 2)}\n </div>\n )}\n\n {/* Main info */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {p.name}\n </h3>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${typeColor(p.type)}`}\n >\n {p.type}\n </span>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${statusColor(p.status)}`}\n >\n {p.status.replace('_', ' ')}\n </span>\n </div>\n\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[var(--color-text-secondary)]\">\n <span className=\"flex items-center gap-1\">\n <Hash className=\"size-3\" />\n <span className=\"font-mono\">{p.slug}</span>\n </span>\n {p.publisher && (\n <span className=\"flex items-center gap-1\">\n <User className=\"size-3\" />\n {p.publisher.name}\n {p.publisher.email && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({p.publisher.email})\n </span>\n )}\n </span>\n )}\n {p.submittedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatDate(p.submittedAt)}\n </span>\n )}\n <span className=\"flex items-center gap-1\">\n <Tag className=\"size-3\" />\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}\n </span>\n </div>\n\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-[var(--color-text-secondary)]\">\n {p.description}\n </p>\n )}\n </div>\n </div>\n\n {/* Action bar */}\n <div className=\"flex flex-wrap items-center justify-between gap-2 border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] px-5 py-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {/* View in devportal */}\n {p.productId && (\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n )}\n\n {/* Package download for NODE/WORKFLOW */}\n {PACKAGE_TYPES.has(p.type) && p.productId && (\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Package File\n </a>\n )}\n\n {/* Test in workspace for NODE/WORKFLOW */}\n {PACKAGE_TYPES.has(p.type) && (\n <button\n type=\"button\"\n onClick={() => setTestingProduct(p)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-accent)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-accent)] hover:bg-[var(--color-surface-accent-muted)] transition-colors\"\n >\n <FlaskConical className=\"size-3.5\" />\n Test in workspace\n </button>\n )}\n\n {/* Show manifest / details toggle */}\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === p.id ? 'Hide details' : 'Inspect details'}\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {p.status !== 'PUBLISHED' && (\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-[var(--color-surface-success)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-on-success)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Check className=\"size-3.5\" />\n Approve\n </button>\n )}\n {p.status !== 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-danger)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Details panel */}\n {viewingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5 space-y-5\">\n {/* Metadata grid */}\n <div className=\"grid grid-cols-2 gap-x-8 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Store product ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.id}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)]\">\n {p.slug}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Type / Nature\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.type} · {p.nature}\n </dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Category\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Pricing</dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-[var(--color-text-muted)]\">({p.pricingModel})</span>\n </dd>\n </div>\n {p.productId && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Devportal app ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.productId}\n </dd>\n </div>\n )}\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Submitted by (actor)\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.submittedBy}\n </dd>\n </div>\n )}\n </div>\n\n {/* Required permissions */}\n {p.requiredPermissions && p.requiredPermissions.length > 0 && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <ShieldCheck className=\"size-3.5 text-[var(--color-status-success-text)]\" />\n Required Permissions\n </h4>\n <div className=\"flex flex-wrap gap-1.5\">\n {p.requiredPermissions.map((perm) => (\n <span\n key={perm}\n className=\"inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-default)] border border-[var(--color-border-seam)] px-2 py-1 text-xs font-mono text-[var(--color-text-primary)]\"\n >\n {perm}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Manifest JSON */}\n {p.manifest && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <FileJson className=\"size-3.5\" />\n Manifest\n </h4>\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-72 overflow-y-auto scrollbar-thin\">\n {JSON.stringify(p.manifest, null, 2)}\n </pre>\n </div>\n )}\n </div>\n )}\n\n {/* Rejection form */}\n {rejectingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-danger-muted)]/30 px-5 py-4\">\n <p className=\"mb-2 text-xs font-semibold text-[var(--color-text-danger)]\">\n Reject — provide a reason (the publisher will see this)\n </p>\n <textarea\n id={`reject-reason-${p.id}`}\n aria-label=\"Rejection reason\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus:border-[var(--color-border-accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"Be specific — e.g. 'Manifest is missing the identity.version field' or 'ZIP file is empty'…\"\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\n }}\n className=\"rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.id}\n className=\"rounded-md bg-[var(--color-surface-danger)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text-on-danger)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n\n {/* Test in workspace modal */}\n {testingProduct && (\n <InstallAppModal\n isOpen={true}\n onClose={() => setTestingProduct(null)}\n app={{\n id: testingProduct.id,\n name: testingProduct.name,\n slug: testingProduct.slug,\n icon: testingProduct.icon ?? undefined,\n type: testingProduct.type,\n permissions: testingProduct.requiredPermissions?.length\n ? testingProduct.requiredPermissions\n : undefined,\n }}\n purchaseState=\"free\"\n onInstallSuccess={() => {\n setTestingProduct(null);\n }}\n onInstallError={() => {}}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|