@burdenoff/microfe-devportal 2026.1112.2 → 2026.1113.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/devportal/components/AppCard.js +73 -70
- package/dist/devportal/components/AppCard.js.map +1 -1
- package/dist/devportal/components/ImageUploadField.js +82 -80
- package/dist/devportal/components/ImageUploadField.js.map +1 -1
- package/dist/devportal/components/StoreImage.d.ts +15 -0
- package/dist/devportal/components/StoreImage.js +30 -0
- package/dist/devportal/components/StoreImage.js.map +1 -0
- package/dist/devportal/components/index.d.ts +1 -0
- package/dist/devportal/components/index.js +17 -16
- package/dist/devportal/hooks/usePackageUpload.d.ts +46 -4
- package/dist/devportal/hooks/usePackageUpload.js +90 -36
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +205 -202
- package/dist/devportal/pages/AdminReviewPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +2 -2
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +201 -199
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.js +2 -2
- package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/EditPhysicalItemPage.js +312 -307
- package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +175 -171
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +118 -116
- package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerItemsPage.js +202 -199
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
- package/dist/devportal/pages/StoreSubmissionPage.js +133 -131
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationsPage.js","names":["EmptyState","EmptyState$1","formatBulkResult","action","succeeded","failed","StatusBadge","status","tr","mapApplication","screenshots","app","pricingDetails","ApplicationsPage","useDevportal","useTr","createPerm","useDevportalPermission","DEVPORTAL_PERMISSIONS","updatePerm","viewMode","setViewMode","useState","searchQuery","setSearchQuery","statusFilter","setStatusFilter","typeFilter","setTypeFilter","selectedAppIds","setSelectedAppIds","bulkAction","setBulkAction","feedback","setFeedback","useGetApplicationsQuery","activeOrgId","applications","useMemo","data","edge","deactivateApp","useDeactivateApplicationMutation","handleNavigate","basePath","path","filteredApps","matchesOrg","matchesSearch","matchesType","appStats","selectedApps","deactivatableApps","allVisibleSelected","partiallySelected","toggleAppSelection","next","prev","checked","appId","toggleAllVisible","clearSelection","loading","PageLayout","Loader2","error","AlertCircle","getErrorMessage","refetch","PermissionGuard","Plus","Search","APP_STATUS_COLORS","APP_TYPE_LABELS","value","label","Grid","List","EmphasisPanel","GlassCard","SelectionCheckbox","Archive","AppCard","AppWindow","ConfirmDialog","open","results","result"],"sources":["../../../src/devportal/pages/ApplicationsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport {\n Plus,\n Search,\n Grid,\n List,\n AppWindow,\n Loader2,\n AlertCircle,\n Archive,\n} from \"lucide-react\";\nimport { EmphasisPanel, GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, AppStatus, AppType } from \"../types\";\nimport { APP_TYPE_LABELS, APP_STATUS_COLORS } from \"../utils/constants\";\nimport {\n useGetApplicationsQuery,\n useDeactivateApplicationMutation,\n getErrorMessage,\n useDevportalPermission,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\ntype BulkAction = \"deactivate\" | null;\n\nfunction formatBulkResult(\n action: string,\n succeeded: number,\n failed: number,\n): string {\n return `${action} ${succeeded} application${succeeded === 1 ? \"\" : \"s\"}${\n failed > 0 ? ` • ${failed} failed` : \"\"\n }`;\n}\n\nfunction StatusBadge({\n status,\n tr,\n}: {\n status: AppStatus;\n tr: (key: string, fallback: string) => string;\n}) {\n const colors: Record<string, string> = {\n ACTIVE: \"bg-status-success-bg-subtle text-status-success-text\",\n PUBLISHED: \"bg-status-success-bg-subtle text-status-success-text\",\n DRAFT: \"bg-bg-sunken text-text-secondary\",\n SUBMITTED: \"bg-status-warning-bg-subtle text-status-warning-text\",\n IN_REVIEW: \"bg-status-warning-bg-subtle text-status-warning-text\",\n APPROVED: \"bg-status-success-bg-subtle text-status-success-text\",\n UNLISTED: \"bg-status-warning-bg-subtle text-status-warning-text\",\n REJECTED: \"bg-status-error-bg-subtle text-status-error-text\",\n SUSPENDED: \"bg-status-error-bg-subtle text-status-error-text\",\n INACTIVE: \"bg-bg-sunken text-text-muted\",\n REVOKED: \"bg-status-error-bg-subtle text-status-error-text\",\n DEPRECATED: \"bg-bg-sunken text-text-muted\",\n };\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${colors[status] ?? \"bg-bg-sunken text-text-secondary\"}`}\n >\n {status === \"INACTIVE\" || status === \"REVOKED\"\n ? tr(\"devportal.status.deactivated\", \"Deactivated\")\n : status.replace(/_/g, \" \")}\n </span>\n );\n}\n\n/**\n * Map GraphQL application to local Application type\n */\nfunction mapApplication(app: ApplicationFieldsFragment): Application {\n // Convert screenshots array to Record if needed\n const screenshots = Array.isArray(app.screenshots)\n ? app.screenshots.length > 0\n ? (app.screenshots as unknown as Record<string, unknown>)\n : undefined\n : (app.screenshots as Record<string, unknown> | undefined);\n\n // Convert pricingDetails\n const pricingDetails =\n app.pricingDetails && typeof app.pricingDetails === \"object\"\n ? (app.pricingDetails as Record<string, unknown>)\n : undefined;\n\n return {\n id: app.id,\n name: app.name,\n slug: app.slug,\n description: app.description ?? undefined,\n shortDescription: app.shortDescription ?? undefined,\n appType: app.appType as AppType,\n status: app.status as AppStatus,\n visibility: app.visibility as \"PUBLIC\" | \"PRIVATE\" | \"UNLISTED\",\n category: app.category ?? undefined,\n tags: app.tags ?? [],\n keywords: app.keywords ?? [],\n iconUrl: app.iconUrl ?? undefined,\n bannerUrl: app.bannerUrl ?? undefined,\n screenshots,\n videoUrl: app.videoUrl ?? undefined,\n websiteUrl: app.websiteUrl ?? undefined,\n documentationUrl: app.documentationUrl ?? undefined,\n supportUrl: app.supportUrl ?? undefined,\n privacyPolicyUrl: app.privacyPolicyUrl ?? undefined,\n termsOfServiceUrl: app.termsOfServiceUrl ?? undefined,\n githubUrl: app.githubUrl ?? undefined,\n pricingType: app.pricingType ?? undefined,\n pricingDetails,\n isVerified: app.isVerified,\n isFeatured: app.isFeatured,\n averageRating: app.averageRating ?? undefined,\n reviewCount: app.reviewCount ?? 0,\n installCount: app.installCount ?? 0,\n activeInstallCount: app.activeInstallCount ?? 0,\n requiresApproval: app.requiresApproval,\n reviewStatus: app.reviewStatus ?? undefined,\n reviewNotes: app.reviewNotes ?? undefined,\n reviewedBy: app.reviewedBy ?? undefined,\n lastReviewedAt: app.lastReviewedAt ?? undefined,\n submittedAt: app.submittedAt ?? undefined,\n publishedAt: app.publishedAt ?? undefined,\n developerId: app.developerId,\n developerOrganizationId: app.developerOrganizationId ?? undefined,\n tenantId: app.tenantId,\n createdAt: app.createdAt,\n updatedAt: app.updatedAt,\n };\n}\n\n/**\n * Applications listing page\n *\n * Displays all applications for the current developer.\n * Uses GraphQL to fetch real data from the DevPortal backend.\n */\nexport const ApplicationsPage: FC = () => {\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const createPerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_CREATE);\n const updatePerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_UPDATE);\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"list\");\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [statusFilter, setStatusFilter] = useState<AppStatus | \"\">(\"\");\n const [typeFilter, setTypeFilter] = useState<AppType | \"\">(\"\");\n const [selectedAppIds, setSelectedAppIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<BulkAction>(null);\n const [feedback, setFeedback] = useState<{\n type: \"success\" | \"error\";\n message: string;\n } | null>(null);\n\n // Fetch applications from GraphQL API — always scope to active org so\n // totalCount from the server matches what the user actually owns.\n const { data, loading, error, refetch } = useGetApplicationsQuery({\n variables: {\n limit: 50,\n offset: 0,\n status: statusFilter || undefined,\n organizationId: activeOrgId ?? undefined,\n },\n fetchPolicy: \"cache-and-network\",\n });\n\n const applications: Application[] = useMemo(\n () =>\n data?.applications?.edges?.map(\n (edge: { node: ApplicationFieldsFragment }) =>\n mapApplication(edge.node),\n ) ?? [],\n [data?.applications?.edges],\n );\n\n const [deactivateApp] = useDeactivateApplicationMutation();\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n // Client-side filtering: org scope + search + type (status filtered server-side)\n const filteredApps = applications.filter((app: Application) => {\n const matchesOrg =\n !activeOrgId || app.developerOrganizationId === activeOrgId;\n const matchesSearch =\n !searchQuery ||\n app.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n app.shortDescription?.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesType = !typeFilter || app.appType === typeFilter;\n return matchesOrg && matchesSearch && matchesType;\n });\n\n const appStats = useMemo(() => {\n // Use filteredApps (org-scoped) for all counts so stats match what's shown.\n const total = data?.applications?.totalCount ?? filteredApps.length;\n const active = filteredApps.filter(\n (app: Application) => app.status === \"ACTIVE\",\n ).length;\n const draft = filteredApps.filter(\n (app: Application) => app.status === \"DRAFT\",\n ).length;\n const published = filteredApps.filter(\n (app: Application) => app.status === \"PUBLISHED\",\n ).length;\n return { total, active, draft, published };\n }, [filteredApps, data?.applications?.totalCount]);\n\n const selectedApps = useMemo(\n () => applications.filter((app: Application) => selectedAppIds.has(app.id)),\n [applications, selectedAppIds],\n );\n\n const deactivatableApps = useMemo(\n () =>\n selectedApps.filter(\n (app: Application) =>\n app.status !== \"INACTIVE\" && app.status !== \"REVOKED\",\n ),\n [selectedApps],\n );\n\n const allVisibleSelected =\n filteredApps.length > 0 &&\n filteredApps.every((app: Application) => selectedAppIds.has(app.id));\n const partiallySelected =\n filteredApps.some((app: Application) => selectedAppIds.has(app.id)) &&\n !allVisibleSelected;\n\n const toggleAppSelection = (appId: string, checked: boolean) => {\n setSelectedAppIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(appId);\n } else {\n next.delete(appId);\n }\n return next;\n });\n };\n\n const toggleAllVisible = (checked: boolean) => {\n setSelectedAppIds((prev) => {\n const next = new Set(prev);\n filteredApps.forEach((app: Application) => {\n if (checked) {\n next.add(app.id);\n } else {\n next.delete(app.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedAppIds(new Set());\n\n const runBulkDeactivate = async () => {\n if (deactivatableApps.length === 0) return;\n\n const results = await Promise.allSettled(\n deactivatableApps.map((app: Application) =>\n deactivateApp({ variables: { id: app.id } }),\n ),\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n\n setFeedback({\n type: failed === 0 ? \"success\" : \"error\",\n message: formatBulkResult(\n tr(\"devportal.deactivated\", \"Deactivated\"),\n succeeded,\n failed,\n ),\n });\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n // Loading state\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n <span className=\"ml-2 text-text-secondary\">\n {tr(\"devportal.loadingApplications\", \"Loading applications...\")}\n </span>\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {tr(\n \"devportal.failedToLoadApplications\",\n \"Failed to load applications\",\n )}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n <button\n onClick={() => refetch()}\n 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\"\n >\n {tr(\"devportal.tryAgain\", \"Try Again\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n purpose={tr(\n \"devportal.applicationsPurpose\",\n \"Applications are the integrations you build on the platform — OAuth apps, store listings, and API consumers. Create one here to get client credentials, request scopes, and ship to the marketplace.\",\n )}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n disabled={!createPerm.allowed}\n aria-disabled={!createPerm.allowed}\n title={\n !createPerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\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 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Plus size={16} aria-hidden=\"true\" />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n >\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-1 items-center gap-4\">\n {/* Search */}\n <div className=\"relative flex-1 max-w-md\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder={tr(\n \"devportal.searchApplications\",\n \"Search applications...\",\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {/* Status Filter */}\n <select\n value={statusFilter}\n onChange={(e) =>\n setStatusFilter(e.target.value as AppStatus | \"\")\n }\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tr(\"devportal.allStatus\", \"All Status\")}\n </option>\n {Object.keys(APP_STATUS_COLORS).map((status) => (\n <option key={status} value={status}>\n {tr(\n `devportal.appStatus.${status}`,\n status.replace(/_/g, \" \"),\n )}\n </option>\n ))}\n </select>\n\n {/* Type Filter */}\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as AppType | \"\")}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr(\"devportal.allTypes\", \"All Types\")}</option>\n {Object.entries(APP_TYPE_LABELS).map(([value, label]) => (\n <option key={value} value={value}>\n {label}\n </option>\n ))}\n </select>\n </div>\n\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 rounded-md border p-1\">\n <button\n onClick={() => setViewMode(\"grid\")}\n className={`rounded p-1.5 ${viewMode === \"grid\" ? \"bg-bg-sunken\" : \"hover:bg-bg-sunken/50\"}`}\n title={tr(\"devportal.gridView\", \"Grid view\")}\n >\n <Grid size={16} />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={`rounded p-1.5 ${viewMode === \"list\" ? \"bg-bg-sunken\" : \"hover:bg-bg-sunken/50\"}`}\n title={tr(\"devportal.tableView\", \"Table view\")}\n >\n <List size={16} />\n </button>\n </div>\n </div>\n\n {/* Summary Stats — primary emphasis zone */}\n <EmphasisPanel className=\"mt-4\">\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.totalApps\", \"Total Apps\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.total}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-status-success-text\">\n {tr(\"devportal.active\", \"Active\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.active}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.published\", \"Published\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.published}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.draft\", \"Draft\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.draft}\n </p>\n </GlassCard>\n </div>\n </EmphasisPanel>\n\n {feedback && (\n <div\n className={`rounded-lg border px-4 py-3 text-sm ${\n feedback.type === \"success\"\n ? \"border-border-subtle bg-status-success-bg-subtle text-status-success-text\"\n : \"border-status-error-border/40 bg-status-error-bg/10 text-status-error-text\"\n }`}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <span>{feedback.message}</span>\n <button\n onClick={() => setFeedback(null)}\n className=\"text-xs font-medium underline underline-offset-2\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {filteredApps.length > 0 && (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-pop)]\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisible}\n ariaLabel={tr(\n \"devportal.selectAllVisibleApplications\",\n \"Select all visible applications\",\n )}\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedAppIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr(\"devportal.clearSelection\", \"Clear selection\")}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedAppIds.size}{\" \"}\n {tr(\"devportal.selectedLower\", \"selected\")}\n </span>\n <button\n onClick={() => setBulkAction(\"deactivate\")}\n disabled={\n deactivatableApps.length === 0 || !updatePerm.allowed\n }\n aria-disabled={\n deactivatableApps.length === 0 || !updatePerm.allowed\n }\n title={\n !updatePerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\n className=\"inline-flex items-center gap-2 rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text transition-colors hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Archive size={16} aria-hidden=\"true\" />\n {tr(\"devportal.deactivateSelected\", \"Deactivate Selected\")}\n {deactivatableApps.length > 0\n ? ` (${deactivatableApps.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {filteredApps.length}{\" \"}\n {filteredApps.length === 1\n ? tr(\"devportal.applicationFound\", \"application found\")\n : tr(\"devportal.applicationsFound\", \"applications found\")}\n </p>\n\n {/* Applications */}\n {filteredApps.length > 0 ? (\n viewMode === \"grid\" ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {filteredApps.map((app: Application) => (\n <AppCard\n key={app.id}\n app={app}\n selected={selectedAppIds.has(app.id)}\n onToggleSelect={(checked) =>\n toggleAppSelection(app.id, checked)\n }\n />\n ))}\n </div>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken text-left\">\n <th className=\"w-10 px-4 py-3\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisible}\n ariaLabel={tr(\"devportal.selectAll\", \"Select all\")}\n />\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.name\", \"Name\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.status\", \"Status\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.type\", \"Type\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.created\", \"Created\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\"></th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam\">\n {filteredApps.map((app: Application) => (\n <tr\n key={app.id}\n className={`hover:bg-bg-surface-hover transition-colors ${selectedAppIds.has(app.id) ? \"bg-action-primary-bg/5\" : \"\"}`}\n >\n <td className=\"w-10 px-4 py-3\">\n <SelectionCheckbox\n checked={selectedAppIds.has(app.id)}\n onChange={(checked) =>\n toggleAppSelection(app.id, checked)\n }\n ariaLabel={tr(\n \"devportal.selectNamed\",\n \"Select {{name}}\",\n { name: app.name },\n )}\n />\n </td>\n <td className=\"px-4 py-3\">\n <button\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n className=\"group flex items-center gap-3 text-left\"\n >\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"h-8 w-8 rounded object-cover\"\n />\n ) : (\n <div className=\"flex h-8 w-8 items-center justify-center rounded bg-bg-sunken text-text-secondary\">\n <AppWindow size={14} />\n </div>\n )}\n <div>\n <p className=\"font-medium text-text-primary group-hover:text-action-primary-text transition-colors\">\n {app.name}\n </p>\n {app.shortDescription && (\n <p className=\"text-xs text-text-secondary line-clamp-1\">\n {app.shortDescription}\n </p>\n )}\n </div>\n </button>\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge status={app.status} tr={tr} />\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {APP_TYPE_LABELS[app.appType] || app.appType}\n </td>\n <td className=\"px-4 py-3 text-text-secondary tabular-nums\">\n {new Date(app.createdAt).toLocaleDateString()}\n </td>\n <td className=\"px-4 py-3 text-right\">\n <button\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.view\", \"View\")}\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n ) : (\n <EmptyState\n illustration={\n searchQuery || statusFilter || typeFilter\n ? \"empty-search\"\n : \"empty-data\"\n }\n icon={<AppWindow size={32} />}\n title={tr(\"devportal.noApplicationsFound\", \"No applications found\")}\n description={\n searchQuery || statusFilter || typeFilter\n ? tr(\n \"devportal.tryAdjustingFilters\",\n \"Try adjusting your filters\",\n )\n : tr(\n \"devportal.createFirstApp\",\n \"Create your first application to get started\",\n )\n }\n action={\n !searchQuery && !statusFilter && !typeFilter ? (\n <button\n onClick={() => handleNavigate(\"/apps/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.createApp\", \"Create App\")}\n </button>\n ) : undefined\n }\n />\n )}\n\n <ConfirmDialog\n open={bulkAction === \"deactivate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={runBulkDeactivate}\n title={tr(\n \"devportal.deactivateSelectedApplications\",\n \"Deactivate selected applications\",\n )}\n description={tr(\n \"devportal.deactivateSelectedApplicationsDescription\",\n \"Deactivate {{count}} selected application(s)? Users and integrations may lose access until these apps are re-enabled.\",\n { count: deactivatableApps.length },\n )}\n confirmLabel={tr(\"devportal.deactivate\", \"Deactivate\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"warning\"\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApplicationsPage.js","names":["EmptyState","EmptyState$1","formatBulkResult","action","succeeded","failed","StatusBadge","status","tr","mapApplication","screenshots","app","pricingDetails","ApplicationsPage","useDevportal","useTr","createPerm","useDevportalPermission","DEVPORTAL_PERMISSIONS","updatePerm","viewMode","setViewMode","useState","searchQuery","setSearchQuery","statusFilter","setStatusFilter","typeFilter","setTypeFilter","selectedAppIds","setSelectedAppIds","bulkAction","setBulkAction","feedback","setFeedback","useGetApplicationsQuery","activeOrgId","applications","useMemo","data","edge","deactivateApp","useDeactivateApplicationMutation","handleNavigate","basePath","path","filteredApps","matchesOrg","matchesSearch","matchesType","appStats","selectedApps","deactivatableApps","allVisibleSelected","partiallySelected","toggleAppSelection","next","prev","checked","appId","toggleAllVisible","clearSelection","loading","PageLayout","Loader2","error","AlertCircle","getErrorMessage","refetch","PermissionGuard","Plus","Search","APP_STATUS_COLORS","APP_TYPE_LABELS","value","label","Grid","List","EmphasisPanel","GlassCard","SelectionCheckbox","Archive","AppCard","StoreImage","AppWindow","ConfirmDialog","open","results","result"],"sources":["../../../src/devportal/pages/ApplicationsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport {\n Plus,\n Search,\n Grid,\n List,\n AppWindow,\n Loader2,\n AlertCircle,\n Archive,\n} from \"lucide-react\";\nimport { EmphasisPanel, GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, AppStatus, AppType } from \"../types\";\nimport { APP_TYPE_LABELS, APP_STATUS_COLORS } from \"../utils/constants\";\nimport {\n useGetApplicationsQuery,\n useDeactivateApplicationMutation,\n getErrorMessage,\n useDevportalPermission,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\nimport { StoreImage } from \"../components/StoreImage\";\n\ntype BulkAction = \"deactivate\" | null;\n\nfunction formatBulkResult(\n action: string,\n succeeded: number,\n failed: number,\n): string {\n return `${action} ${succeeded} application${succeeded === 1 ? \"\" : \"s\"}${\n failed > 0 ? ` • ${failed} failed` : \"\"\n }`;\n}\n\nfunction StatusBadge({\n status,\n tr,\n}: {\n status: AppStatus;\n tr: (key: string, fallback: string) => string;\n}) {\n const colors: Record<string, string> = {\n ACTIVE: \"bg-status-success-bg-subtle text-status-success-text\",\n PUBLISHED: \"bg-status-success-bg-subtle text-status-success-text\",\n DRAFT: \"bg-bg-sunken text-text-secondary\",\n SUBMITTED: \"bg-status-warning-bg-subtle text-status-warning-text\",\n IN_REVIEW: \"bg-status-warning-bg-subtle text-status-warning-text\",\n APPROVED: \"bg-status-success-bg-subtle text-status-success-text\",\n UNLISTED: \"bg-status-warning-bg-subtle text-status-warning-text\",\n REJECTED: \"bg-status-error-bg-subtle text-status-error-text\",\n SUSPENDED: \"bg-status-error-bg-subtle text-status-error-text\",\n INACTIVE: \"bg-bg-sunken text-text-muted\",\n REVOKED: \"bg-status-error-bg-subtle text-status-error-text\",\n DEPRECATED: \"bg-bg-sunken text-text-muted\",\n };\n return (\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${colors[status] ?? \"bg-bg-sunken text-text-secondary\"}`}\n >\n {status === \"INACTIVE\" || status === \"REVOKED\"\n ? tr(\"devportal.status.deactivated\", \"Deactivated\")\n : status.replace(/_/g, \" \")}\n </span>\n );\n}\n\n/**\n * Map GraphQL application to local Application type\n */\nfunction mapApplication(app: ApplicationFieldsFragment): Application {\n // Convert screenshots array to Record if needed\n const screenshots = Array.isArray(app.screenshots)\n ? app.screenshots.length > 0\n ? (app.screenshots as unknown as Record<string, unknown>)\n : undefined\n : (app.screenshots as Record<string, unknown> | undefined);\n\n // Convert pricingDetails\n const pricingDetails =\n app.pricingDetails && typeof app.pricingDetails === \"object\"\n ? (app.pricingDetails as Record<string, unknown>)\n : undefined;\n\n return {\n id: app.id,\n name: app.name,\n slug: app.slug,\n description: app.description ?? undefined,\n shortDescription: app.shortDescription ?? undefined,\n appType: app.appType as AppType,\n status: app.status as AppStatus,\n visibility: app.visibility as \"PUBLIC\" | \"PRIVATE\" | \"UNLISTED\",\n category: app.category ?? undefined,\n tags: app.tags ?? [],\n keywords: app.keywords ?? [],\n iconUrl: app.iconUrl ?? undefined,\n bannerUrl: app.bannerUrl ?? undefined,\n screenshots,\n videoUrl: app.videoUrl ?? undefined,\n websiteUrl: app.websiteUrl ?? undefined,\n documentationUrl: app.documentationUrl ?? undefined,\n supportUrl: app.supportUrl ?? undefined,\n privacyPolicyUrl: app.privacyPolicyUrl ?? undefined,\n termsOfServiceUrl: app.termsOfServiceUrl ?? undefined,\n githubUrl: app.githubUrl ?? undefined,\n pricingType: app.pricingType ?? undefined,\n pricingDetails,\n isVerified: app.isVerified,\n isFeatured: app.isFeatured,\n averageRating: app.averageRating ?? undefined,\n reviewCount: app.reviewCount ?? 0,\n installCount: app.installCount ?? 0,\n activeInstallCount: app.activeInstallCount ?? 0,\n requiresApproval: app.requiresApproval,\n reviewStatus: app.reviewStatus ?? undefined,\n reviewNotes: app.reviewNotes ?? undefined,\n reviewedBy: app.reviewedBy ?? undefined,\n lastReviewedAt: app.lastReviewedAt ?? undefined,\n submittedAt: app.submittedAt ?? undefined,\n publishedAt: app.publishedAt ?? undefined,\n developerId: app.developerId,\n developerOrganizationId: app.developerOrganizationId ?? undefined,\n tenantId: app.tenantId,\n createdAt: app.createdAt,\n updatedAt: app.updatedAt,\n };\n}\n\n/**\n * Applications listing page\n *\n * Displays all applications for the current developer.\n * Uses GraphQL to fetch real data from the DevPortal backend.\n */\nexport const ApplicationsPage: FC = () => {\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const createPerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_CREATE);\n const updatePerm = useDevportalPermission(DEVPORTAL_PERMISSIONS.APP_UPDATE);\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"list\");\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [statusFilter, setStatusFilter] = useState<AppStatus | \"\">(\"\");\n const [typeFilter, setTypeFilter] = useState<AppType | \"\">(\"\");\n const [selectedAppIds, setSelectedAppIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<BulkAction>(null);\n const [feedback, setFeedback] = useState<{\n type: \"success\" | \"error\";\n message: string;\n } | null>(null);\n\n // Fetch applications from GraphQL API — always scope to active org so\n // totalCount from the server matches what the user actually owns.\n const { data, loading, error, refetch } = useGetApplicationsQuery({\n variables: {\n limit: 50,\n offset: 0,\n status: statusFilter || undefined,\n organizationId: activeOrgId ?? undefined,\n },\n fetchPolicy: \"cache-and-network\",\n });\n\n const applications: Application[] = useMemo(\n () =>\n data?.applications?.edges?.map(\n (edge: { node: ApplicationFieldsFragment }) =>\n mapApplication(edge.node),\n ) ?? [],\n [data?.applications?.edges],\n );\n\n const [deactivateApp] = useDeactivateApplicationMutation();\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n // Client-side filtering: org scope + search + type (status filtered server-side)\n const filteredApps = applications.filter((app: Application) => {\n const matchesOrg =\n !activeOrgId || app.developerOrganizationId === activeOrgId;\n const matchesSearch =\n !searchQuery ||\n app.name.toLowerCase().includes(searchQuery.toLowerCase()) ||\n app.shortDescription?.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesType = !typeFilter || app.appType === typeFilter;\n return matchesOrg && matchesSearch && matchesType;\n });\n\n const appStats = useMemo(() => {\n // Use filteredApps (org-scoped) for all counts so stats match what's shown.\n const total = data?.applications?.totalCount ?? filteredApps.length;\n const active = filteredApps.filter(\n (app: Application) => app.status === \"ACTIVE\",\n ).length;\n const draft = filteredApps.filter(\n (app: Application) => app.status === \"DRAFT\",\n ).length;\n const published = filteredApps.filter(\n (app: Application) => app.status === \"PUBLISHED\",\n ).length;\n return { total, active, draft, published };\n }, [filteredApps, data?.applications?.totalCount]);\n\n const selectedApps = useMemo(\n () => applications.filter((app: Application) => selectedAppIds.has(app.id)),\n [applications, selectedAppIds],\n );\n\n const deactivatableApps = useMemo(\n () =>\n selectedApps.filter(\n (app: Application) =>\n app.status !== \"INACTIVE\" && app.status !== \"REVOKED\",\n ),\n [selectedApps],\n );\n\n const allVisibleSelected =\n filteredApps.length > 0 &&\n filteredApps.every((app: Application) => selectedAppIds.has(app.id));\n const partiallySelected =\n filteredApps.some((app: Application) => selectedAppIds.has(app.id)) &&\n !allVisibleSelected;\n\n const toggleAppSelection = (appId: string, checked: boolean) => {\n setSelectedAppIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(appId);\n } else {\n next.delete(appId);\n }\n return next;\n });\n };\n\n const toggleAllVisible = (checked: boolean) => {\n setSelectedAppIds((prev) => {\n const next = new Set(prev);\n filteredApps.forEach((app: Application) => {\n if (checked) {\n next.add(app.id);\n } else {\n next.delete(app.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedAppIds(new Set());\n\n const runBulkDeactivate = async () => {\n if (deactivatableApps.length === 0) return;\n\n const results = await Promise.allSettled(\n deactivatableApps.map((app: Application) =>\n deactivateApp({ variables: { id: app.id } }),\n ),\n );\n\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n\n setFeedback({\n type: failed === 0 ? \"success\" : \"error\",\n message: formatBulkResult(\n tr(\"devportal.deactivated\", \"Deactivated\"),\n succeeded,\n failed,\n ),\n });\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n // Loading state\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n <span className=\"ml-2 text-text-secondary\">\n {tr(\"devportal.loadingApplications\", \"Loading applications...\")}\n </span>\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {tr(\n \"devportal.failedToLoadApplications\",\n \"Failed to load applications\",\n )}\n </h3>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n <button\n onClick={() => refetch()}\n 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\"\n >\n {tr(\"devportal.tryAgain\", \"Try Again\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={tr(\"devportal.myApplications\", \"My Applications\")}\n description={tr(\n \"devportal.manageYourApps\",\n \"Manage your developer applications\",\n )}\n purpose={tr(\n \"devportal.applicationsPurpose\",\n \"Applications are the integrations you build on the platform — OAuth apps, store listings, and API consumers. Create one here to get client credentials, request scopes, and ship to the marketplace.\",\n )}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n disabled={!createPerm.allowed}\n aria-disabled={!createPerm.allowed}\n title={\n !createPerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\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 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Plus size={16} aria-hidden=\"true\" />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n >\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-1 items-center gap-4\">\n {/* Search */}\n <div className=\"relative flex-1 max-w-md\">\n <Search\n size={16}\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary\"\n />\n <input\n type=\"text\"\n placeholder={tr(\n \"devportal.searchApplications\",\n \"Search applications...\",\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n </div>\n\n {/* Status Filter */}\n <select\n value={statusFilter}\n onChange={(e) =>\n setStatusFilter(e.target.value as AppStatus | \"\")\n }\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tr(\"devportal.allStatus\", \"All Status\")}\n </option>\n {Object.keys(APP_STATUS_COLORS).map((status) => (\n <option key={status} value={status}>\n {tr(\n `devportal.appStatus.${status}`,\n status.replace(/_/g, \" \"),\n )}\n </option>\n ))}\n </select>\n\n {/* Type Filter */}\n <select\n value={typeFilter}\n onChange={(e) => setTypeFilter(e.target.value as AppType | \"\")}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">{tr(\"devportal.allTypes\", \"All Types\")}</option>\n {Object.entries(APP_TYPE_LABELS).map(([value, label]) => (\n <option key={value} value={value}>\n {label}\n </option>\n ))}\n </select>\n </div>\n\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 rounded-md border p-1\">\n <button\n onClick={() => setViewMode(\"grid\")}\n className={`rounded p-1.5 ${viewMode === \"grid\" ? \"bg-bg-sunken\" : \"hover:bg-bg-sunken/50\"}`}\n title={tr(\"devportal.gridView\", \"Grid view\")}\n >\n <Grid size={16} />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={`rounded p-1.5 ${viewMode === \"list\" ? \"bg-bg-sunken\" : \"hover:bg-bg-sunken/50\"}`}\n title={tr(\"devportal.tableView\", \"Table view\")}\n >\n <List size={16} />\n </button>\n </div>\n </div>\n\n {/* Summary Stats — primary emphasis zone */}\n <EmphasisPanel className=\"mt-4\">\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.totalApps\", \"Total Apps\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.total}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-status-success-text\">\n {tr(\"devportal.active\", \"Active\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.active}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.published\", \"Published\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.published}\n </p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.draft\", \"Draft\")}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums\">\n {appStats.draft}\n </p>\n </GlassCard>\n </div>\n </EmphasisPanel>\n\n {feedback && (\n <div\n className={`rounded-lg border px-4 py-3 text-sm ${\n feedback.type === \"success\"\n ? \"border-border-subtle bg-status-success-bg-subtle text-status-success-text\"\n : \"border-status-error-border/40 bg-status-error-bg/10 text-status-error-text\"\n }`}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <span>{feedback.message}</span>\n <button\n onClick={() => setFeedback(null)}\n className=\"text-xs font-medium underline underline-offset-2\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n </div>\n )}\n\n {filteredApps.length > 0 && (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-pop)]\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisible}\n ariaLabel={tr(\n \"devportal.selectAllVisibleApplications\",\n \"Select all visible applications\",\n )}\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedAppIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n {tr(\"devportal.clearSelection\", \"Clear selection\")}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedAppIds.size}{\" \"}\n {tr(\"devportal.selectedLower\", \"selected\")}\n </span>\n <button\n onClick={() => setBulkAction(\"deactivate\")}\n disabled={\n deactivatableApps.length === 0 || !updatePerm.allowed\n }\n aria-disabled={\n deactivatableApps.length === 0 || !updatePerm.allowed\n }\n title={\n !updatePerm.allowed\n ? tr(\n \"devportal.common.noPermission\",\n \"You do not have permission to perform this action\",\n )\n : undefined\n }\n className=\"inline-flex items-center gap-2 rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text transition-colors hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <Archive size={16} aria-hidden=\"true\" />\n {tr(\"devportal.deactivateSelected\", \"Deactivate Selected\")}\n {deactivatableApps.length > 0\n ? ` (${deactivatableApps.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Results count */}\n <p className=\"text-sm text-text-secondary\">\n {filteredApps.length}{\" \"}\n {filteredApps.length === 1\n ? tr(\"devportal.applicationFound\", \"application found\")\n : tr(\"devportal.applicationsFound\", \"applications found\")}\n </p>\n\n {/* Applications */}\n {filteredApps.length > 0 ? (\n viewMode === \"grid\" ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {filteredApps.map((app: Application) => (\n <AppCard\n key={app.id}\n app={app}\n selected={selectedAppIds.has(app.id)}\n onToggleSelect={(checked) =>\n toggleAppSelection(app.id, checked)\n }\n />\n ))}\n </div>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border border-border-seam\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam bg-bg-sunken text-left\">\n <th className=\"w-10 px-4 py-3\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisible}\n ariaLabel={tr(\"devportal.selectAll\", \"Select all\")}\n />\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.name\", \"Name\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.status\", \"Status\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.type\", \"Type\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\">\n {tr(\"devportal.created\", \"Created\")}\n </th>\n <th className=\"px-4 py-3 font-medium text-text-secondary\"></th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-seam\">\n {filteredApps.map((app: Application) => (\n <tr\n key={app.id}\n className={`hover:bg-bg-surface-hover transition-colors ${selectedAppIds.has(app.id) ? \"bg-action-primary-bg/5\" : \"\"}`}\n >\n <td className=\"w-10 px-4 py-3\">\n <SelectionCheckbox\n checked={selectedAppIds.has(app.id)}\n onChange={(checked) =>\n toggleAppSelection(app.id, checked)\n }\n ariaLabel={tr(\n \"devportal.selectNamed\",\n \"Select {{name}}\",\n { name: app.name },\n )}\n />\n </td>\n <td className=\"px-4 py-3\">\n <button\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n className=\"group flex items-center gap-3 text-left\"\n >\n <StoreImage\n src={app.iconUrl}\n alt=\"\"\n className=\"h-8 w-8 rounded object-cover\"\n fallback={\n <div className=\"flex h-8 w-8 items-center justify-center rounded bg-bg-sunken text-text-secondary\">\n <AppWindow size={14} />\n </div>\n }\n />\n <div>\n <p className=\"font-medium text-text-primary group-hover:text-action-primary-text transition-colors\">\n {app.name}\n </p>\n {app.shortDescription && (\n <p className=\"text-xs text-text-secondary line-clamp-1\">\n {app.shortDescription}\n </p>\n )}\n </div>\n </button>\n </td>\n <td className=\"px-4 py-3\">\n <StatusBadge status={app.status} tr={tr} />\n </td>\n <td className=\"px-4 py-3 text-text-secondary\">\n {APP_TYPE_LABELS[app.appType] || app.appType}\n </td>\n <td className=\"px-4 py-3 text-text-secondary tabular-nums\">\n {new Date(app.createdAt).toLocaleDateString()}\n </td>\n <td className=\"px-4 py-3 text-right\">\n <button\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n className=\"rounded-md px-3 py-1.5 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.view\", \"View\")}\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )\n ) : (\n <EmptyState\n illustration={\n searchQuery || statusFilter || typeFilter\n ? \"empty-search\"\n : \"empty-data\"\n }\n icon={<AppWindow size={32} />}\n title={tr(\"devportal.noApplicationsFound\", \"No applications found\")}\n description={\n searchQuery || statusFilter || typeFilter\n ? tr(\n \"devportal.tryAdjustingFilters\",\n \"Try adjusting your filters\",\n )\n : tr(\n \"devportal.createFirstApp\",\n \"Create your first application to get started\",\n )\n }\n action={\n !searchQuery && !statusFilter && !typeFilter ? (\n <button\n onClick={() => handleNavigate(\"/apps/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.createApp\", \"Create App\")}\n </button>\n ) : undefined\n }\n />\n )}\n\n <ConfirmDialog\n open={bulkAction === \"deactivate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={runBulkDeactivate}\n title={tr(\n \"devportal.deactivateSelectedApplications\",\n \"Deactivate selected applications\",\n )}\n description={tr(\n \"devportal.deactivateSelectedApplicationsDescription\",\n \"Deactivate {{count}} selected application(s)? Users and integrations may lose access until these apps are re-enabled.\",\n { count: deactivatableApps.length },\n )}\n confirmLabel={tr(\"devportal.deactivate\", \"Deactivate\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"warning\"\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { CheckboxField as n, InputField as r, SelectField as i, TextareaField as
|
|
|
3
3
|
import { ImageUploadField as o, ScreenshotGallery as s } from "../components/ImageUploadField.js";
|
|
4
4
|
import { useDevportal as c } from "../providers/DevportalProvider.js";
|
|
5
5
|
import { useSellerOrganization as ee } from "../hooks/useSellerOrganization.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useAssetUpload as l } from "../hooks/usePackageUpload.js";
|
|
7
7
|
import { cn as te } from "../utils/cn.js";
|
|
8
8
|
import { resolveGlobalGraphqlEndpoint as ne } from "../utils/graphqlEndpoint.js";
|
|
9
9
|
import { useState as u } from "react";
|
|
@@ -165,7 +165,7 @@ var b = () => {
|
|
|
165
165
|
countryOfOrigin: "",
|
|
166
166
|
warrantyInfo: "",
|
|
167
167
|
returnPolicy: ""
|
|
168
|
-
}), [P, F] = u(""), [I, L] = u(""), [R, z] = u([]), { upload: B, uploading: V, progress: H, error: U } = l(w, C, T, E
|
|
168
|
+
}), [P, F] = u(""), [I, L] = u(""), [R, z] = u([]), { upload: B, uploading: V, progress: H, error: U } = l(w, C, T, E), { upload: de, uploading: W, progress: fe, error: pe } = l(w, C, T, E), { upload: me, uploading: G, progress: he, error: ge } = l(w, C, T, E), [K, q] = u(!1), [J, Y] = u(null), [_e, ve] = u(!1), ye = V || W || G, X = y(M) && !!D && !ye, be = ne(S), Z = (e, t) => {
|
|
169
169
|
N((n) => ({
|
|
170
170
|
...n,
|
|
171
171
|
[e]: t
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreatePhysicalItemPage.js","names":["_Fragment","PRODUCT_CATEGORIES","STORE_CATEGORIES","CURRENCIES","WEIGHT_UNITS","DIMENSION_UNITS","REQUIRED_FIELDS","MY_PUBLISHER_QUERY","REGISTER_PUBLISHER_MUTATION","CREATE_PHYSICAL_PRODUCT_MUTATION","SUBMIT_PRODUCT_MUTATION","deriveSlug","name","isFormValid","value","form","field","CreatePhysicalItemPage","useDevportal","sellerOrganization","sellerOrganizations","sellerOrgLoading","sellerOrgError","useSellerOrganization","setForm","useState","icon","setIcon","bannerUrl","setBannerUrl","screenshots","setScreenshots","uploadIcon","iconUploading","iconProgress","iconUploadError","usePackageUpload","workspacesGatewayUrl","authToken","workspaceId","getWorkspaceToken","uploadBanner","bannerUploading","bannerProgress","bannerUploadError","uploadScreenshot","screenshotUploading","screenshotProgress","screenshotUploadError","submitting","setSubmitting","submitError","setSubmitError","submitSuccess","setSubmitSuccess","imagesUploading","isValid","endpoint","resolveGlobalGraphqlEndpoint","apiGatewayUrl","setField","prev","key","handleNameChange","handleCheckboxChange","e","gqlFetch","json","PageLayout","PageSection","SelectField","selectOrganization","org","InputField","TextareaField","ImageUploadField","ScreenshotGallery","CheckboxField","Fragment","Info","navigate","basePath","publisherId","parsedTags","t","hasDimensions","newProductId","productId","err","cn","Loader2"],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent } from \"react\";\nimport { useState } from \"react\";\nimport { Loader2, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport {\n ImageUploadField,\n ScreenshotGallery,\n} from \"../components/ImageUploadField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useSellerOrganization } from \"../hooks/useSellerOrganization\";\nimport { usePackageUpload } from \"../hooks/usePackageUpload\";\nimport { cn } from \"../utils/cn\";\nimport { resolveGlobalGraphqlEndpoint } from \"../utils/graphqlEndpoint\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n category: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n allowBackorder: boolean;\n lowStockThreshold: string;\n requiresShipping: boolean;\n weightKg: string;\n weightUnit: string;\n length: string;\n width: string;\n height: string;\n dimensionUnit: string;\n brand: string;\n manufacturer: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\n// Store category (the product `category` string, distinct from the ProductType above).\n// The five `agri-` slugs are the HealthyBowl Agri-Inputs Store discriminator: picking one lists\n// the item in that farmer-facing storefront (roadmap #26). Leave blank for general listings.\nconst STORE_CATEGORIES = [\n { value: \"agri-seeds\", label: \"Agri-Inputs — Seeds\" },\n { value: \"agri-fertilizer\", label: \"Agri-Inputs — Organic Fertilizer\" },\n { value: \"agri-biopesticide\", label: \"Agri-Inputs — Bio-Pesticides\" },\n { value: \"agri-tools\", label: \"Agri-Inputs — Tools\" },\n { value: \"agri-irrigation\", label: \"Agri-Inputs — Irrigation\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst WEIGHT_UNITS = [\n { value: \"GRAM\", label: \"Grams (g)\" },\n { value: \"KILOGRAM\", label: \"Kilograms (kg)\" },\n { value: \"OUNCE\", label: \"Ounces (oz)\" },\n { value: \"POUND\", label: \"Pounds (lb)\" },\n];\n\nconst DIMENSION_UNITS = [\n { value: \"CENTIMETER\", label: \"Centimeters (cm)\" },\n { value: \"INCH\", label: \"Inches (in)\" },\n { value: \"METER\", label: \"Meters (m)\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n icon\n bannerUrl\n screenshots\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const {\n navigate,\n basePath,\n productId,\n apiGatewayUrl,\n authToken,\n workspacesGatewayUrl,\n workspaceId,\n getWorkspaceToken,\n } = useDevportal();\n const {\n organization: sellerOrganization,\n organizations: sellerOrganizations,\n loading: sellerOrgLoading,\n error: sellerOrgError,\n selectOrganization,\n } = useSellerOrganization();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n category: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n allowBackorder: false,\n lowStockThreshold: \"\",\n requiresShipping: true,\n weightKg: \"\",\n weightUnit: \"KILOGRAM\",\n length: \"\",\n width: \"\",\n height: \"\",\n dimensionUnit: \"CENTIMETER\",\n brand: \"\",\n manufacturer: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [icon, setIcon] = useState(\"\");\n const [bannerUrl, setBannerUrl] = useState(\"\");\n const [screenshots, setScreenshots] = useState<string[]>([]);\n const {\n upload: uploadIcon,\n uploading: iconUploading,\n progress: iconProgress,\n error: iconUploadError,\n } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n \"/DevPortal/Assets\",\n );\n const {\n upload: uploadBanner,\n uploading: bannerUploading,\n progress: bannerProgress,\n error: bannerUploadError,\n } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n \"/DevPortal/Assets\",\n );\n const {\n upload: uploadScreenshot,\n uploading: screenshotUploading,\n progress: screenshotProgress,\n error: screenshotUploadError,\n } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n \"/DevPortal/Assets\",\n );\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const imagesUploading =\n iconUploading || bannerUploading || screenshotUploading;\n const isValid = isFormValid(form) && !!sellerOrganization && !imagesUploading;\n const endpoint = resolveGlobalGraphqlEndpoint(apiGatewayUrl);\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\" | \"allowBackorder\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting || !sellerOrganization) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n if (!publisherId) {\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: sellerOrganization.legalEntityName ?? sellerOrganization.name,\n email: sellerOrganization.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: sellerOrganization.websiteUrl ?? null,\n legalName:\n sellerOrganization.legalEntityName ?? sellerOrganization.name,\n country: sellerOrganization.taxCountry ?? null,\n contactEmail: sellerOrganization.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 2: create the product, linked to the seller's PHYSICAL org —\n // validated server-side (createPhysicalProduct rejects any org that\n // isn't PHYSICAL-kind, see DeveloperOrgKind / BOFF-7285).\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const hasDimensions =\n form.length.trim() || form.width.trim() || form.height.trim();\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n category: form.category.trim() || null,\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n allowBackorder: form.allowBackorder,\n lowStockThreshold: form.lowStockThreshold.trim()\n ? parseInt(form.lowStockThreshold, 10)\n : null,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n weightUnit:\n form.requiresShipping && form.weightKg.trim()\n ? form.weightUnit\n : null,\n length:\n form.requiresShipping && form.length.trim()\n ? parseFloat(form.length)\n : null,\n width:\n form.requiresShipping && form.width.trim()\n ? parseFloat(form.width)\n : null,\n height:\n form.requiresShipping && form.height.trim()\n ? parseFloat(form.height)\n : null,\n dimensionUnit:\n form.requiresShipping && hasDimensions ? form.dimensionUnit : null,\n brand: form.brand.trim() || null,\n manufacturer: form.manufacturer.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n icon: icon || null,\n bannerUrl: bannerUrl || null,\n screenshots: screenshots.length > 0 ? screenshots : null,\n // Tags this listing with the host product (e.g. \"healthybowl\") so\n // it can be filtered to that product's marketplace. Only set when\n // the host shell actually passes a productId — stays exactly as\n // before (no tag) for any shell that doesn't opt in.\n compatibleProducts: productId ? [productId] : null,\n organizationId: sellerOrganization.id,\n },\n });\n\n const newProductId = productData.createPhysicalProduct.id;\n\n // Step 3: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: newProductId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 4: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* Seller org resolution error — the form can't be submitted without one */}\n {sellerOrgError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n Couldn't set up your seller organization: {sellerOrgError}\n </div>\n )}\n\n {/* ── Section 0: Seller Organization ── */}\n <PageSection\n title=\"Seller Organization\"\n description=\"The seller organization this item will be listed under.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-3\">\n <SelectField\n label=\"Organization\"\n required\n value={sellerOrganization?.id ?? \"\"}\n onChange={(e) => selectOrganization(e.target.value)}\n options={sellerOrganizations.map((org) => ({\n value: org.id,\n label: org.name,\n }))}\n placeholder={\n sellerOrgLoading\n ? \"Resolving your seller organization…\"\n : \"Select an organization\"\n }\n disabled={sellerOrgLoading || sellerOrganizations.length <= 1}\n hint={\n sellerOrganizations.length > 1\n ? \"You have more than one seller organization — pick the one this item belongs to.\"\n : sellerOrganization\n ? `Automatically set to your only seller organization, \"${sellerOrganization.name}\".`\n : undefined\n }\n />\n </div>\n </PageSection>\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Type\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a type\"\n />\n\n <SelectField\n label=\"Store Category\"\n value={form.category}\n onChange={(e) => setField(\"category\", e.target.value)}\n options={STORE_CATEGORIES}\n placeholder=\"General / uncategorised\"\n hint=\"Pick an Agri-Inputs category to list this item in the HealthyBowl Agri-Inputs Store. Leave blank for a general store listing.\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"Visual assets shown to buyers browsing the store.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid gap-5 sm:grid-cols-2\">\n <ImageUploadField\n label=\"Icon\"\n value={icon}\n onChange={setIcon}\n hint=\"Shown in marketplace cards and search — square proportions (1:1)\"\n aspectClass=\"aspect-square\"\n upload={uploadIcon}\n uploading={iconUploading}\n progress={iconProgress}\n error={iconUploadError}\n />\n <ImageUploadField\n label=\"Banner\"\n value={bannerUrl}\n onChange={setBannerUrl}\n hint=\"Fills the right panel on the product page — square or portrait works best on all screen sizes (1:1)\"\n aspectClass=\"aspect-square\"\n upload={uploadBanner}\n uploading={bannerUploading}\n progress={bannerProgress}\n error={bannerUploadError}\n />\n </div>\n <ScreenshotGallery\n urls={screenshots}\n onChange={setScreenshots}\n upload={uploadScreenshot}\n uploading={screenshotUploading}\n progress={screenshotProgress}\n error={screenshotUploadError}\n />\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <InputField\n label=\"Low Stock Threshold\"\n type=\"number\"\n value={form.lowStockThreshold}\n onChange={(e) => setField(\"lowStockThreshold\", e.target.value)}\n placeholder=\"e.g. 5\"\n min={0}\n step={1}\n hint=\"Get flagged when stock falls to or below this number.\"\n />\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n\n <CheckboxField\n label=\"Allow Backorder\"\n description=\"Let buyers order even when stock quantity is zero.\"\n checked={form.allowBackorder}\n onChange={handleCheckboxChange(\"allowBackorder\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <>\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Weight\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n\n <SelectField\n label=\"Weight Unit\"\n value={form.weightUnit}\n onChange={(e) => setField(\"weightUnit\", e.target.value)}\n options={WEIGHT_UNITS}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <InputField\n label=\"Length\"\n type=\"number\"\n value={form.length}\n onChange={(e) => setField(\"length\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Width\"\n type=\"number\"\n value={form.width}\n onChange={(e) => setField(\"width\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Height\"\n type=\"number\"\n value={form.height}\n onChange={(e) => setField(\"height\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n </div>\n\n <SelectField\n label=\"Dimension Unit\"\n value={form.dimensionUnit}\n onChange={(e) => setField(\"dimensionUnit\", e.target.value)}\n options={DIMENSION_UNITS}\n hint=\"Applies to length, width, and height above.\"\n />\n </>\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Manufacturer\"\n value={form.manufacturer}\n onChange={(e) => setField(\"manufacturer\", e.target.value)}\n placeholder=\"e.g. Logitech Manufacturing Ltd.\"\n />\n </div>\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n sellerOrgLoading\n ? \"Setting up your seller organization...\"\n : !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : sellerOrgLoading ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Setting up your store...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreatePhysicalItemPage.js","names":["_Fragment","PRODUCT_CATEGORIES","STORE_CATEGORIES","CURRENCIES","WEIGHT_UNITS","DIMENSION_UNITS","REQUIRED_FIELDS","MY_PUBLISHER_QUERY","REGISTER_PUBLISHER_MUTATION","CREATE_PHYSICAL_PRODUCT_MUTATION","SUBMIT_PRODUCT_MUTATION","deriveSlug","name","isFormValid","value","form","field","CreatePhysicalItemPage","useDevportal","sellerOrganization","sellerOrganizations","sellerOrgLoading","sellerOrgError","useSellerOrganization","setForm","useState","icon","setIcon","bannerUrl","setBannerUrl","screenshots","setScreenshots","uploadIcon","iconUploading","iconProgress","iconUploadError","useAssetUpload","workspacesGatewayUrl","authToken","workspaceId","getWorkspaceToken","uploadBanner","bannerUploading","bannerProgress","bannerUploadError","uploadScreenshot","screenshotUploading","screenshotProgress","screenshotUploadError","submitting","setSubmitting","submitError","setSubmitError","submitSuccess","setSubmitSuccess","imagesUploading","isValid","endpoint","resolveGlobalGraphqlEndpoint","apiGatewayUrl","setField","prev","key","handleNameChange","handleCheckboxChange","e","gqlFetch","json","PageLayout","PageSection","SelectField","selectOrganization","org","InputField","TextareaField","ImageUploadField","ScreenshotGallery","CheckboxField","Fragment","Info","navigate","basePath","publisherId","parsedTags","t","hasDimensions","newProductId","productId","err","cn","Loader2"],"sources":["../../../src/devportal/pages/CreatePhysicalItemPage.tsx"],"sourcesContent":["import type { FC, ChangeEvent } from \"react\";\nimport { useState } from \"react\";\nimport { Loader2, Info } from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n CheckboxField,\n} from \"../components/FormField\";\nimport {\n ImageUploadField,\n ScreenshotGallery,\n} from \"../components/ImageUploadField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useSellerOrganization } from \"../hooks/useSellerOrganization\";\nimport { useAssetUpload } from \"../hooks/usePackageUpload\";\nimport { cn } from \"../utils/cn\";\nimport { resolveGlobalGraphqlEndpoint } from \"../utils/graphqlEndpoint\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ninterface FormState {\n name: string;\n slug: string;\n type: string;\n category: string;\n shortDescription: string;\n longDescription: string;\n price: string;\n currency: string;\n tags: string;\n stockQuantity: string;\n sku: string;\n trackInventory: boolean;\n allowBackorder: boolean;\n lowStockThreshold: string;\n requiresShipping: boolean;\n weightKg: string;\n weightUnit: string;\n length: string;\n width: string;\n height: string;\n dimensionUnit: string;\n brand: string;\n manufacturer: string;\n countryOfOrigin: string;\n warrantyInfo: string;\n returnPolicy: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst PRODUCT_CATEGORIES = [\n { value: \"MERCHANDISE\", label: \"Merchandise\" },\n { value: \"HARDWARE\", label: \"Hardware\" },\n { value: \"ACCESSORY\", label: \"Accessory\" },\n { value: \"BOOK\", label: \"Book\" },\n { value: \"EQUIPMENT\", label: \"Equipment\" },\n { value: \"APPAREL\", label: \"Apparel\" },\n { value: \"COLLECTIBLE\", label: \"Collectible\" },\n { value: \"CONSUMABLE\", label: \"Consumable\" },\n];\n\n// Store category (the product `category` string, distinct from the ProductType above).\n// The five `agri-` slugs are the HealthyBowl Agri-Inputs Store discriminator: picking one lists\n// the item in that farmer-facing storefront (roadmap #26). Leave blank for general listings.\nconst STORE_CATEGORIES = [\n { value: \"agri-seeds\", label: \"Agri-Inputs — Seeds\" },\n { value: \"agri-fertilizer\", label: \"Agri-Inputs — Organic Fertilizer\" },\n { value: \"agri-biopesticide\", label: \"Agri-Inputs — Bio-Pesticides\" },\n { value: \"agri-tools\", label: \"Agri-Inputs — Tools\" },\n { value: \"agri-irrigation\", label: \"Agri-Inputs — Irrigation\" },\n];\n\nconst CURRENCIES = [\n { value: \"USD\", label: \"USD — US Dollar\" },\n { value: \"INR\", label: \"INR — Indian Rupee\" },\n { value: \"EUR\", label: \"EUR — Euro\" },\n { value: \"GBP\", label: \"GBP — British Pound\" },\n { value: \"SGD\", label: \"SGD — Singapore Dollar\" },\n { value: \"AED\", label: \"AED — UAE Dirham\" },\n];\n\nconst WEIGHT_UNITS = [\n { value: \"GRAM\", label: \"Grams (g)\" },\n { value: \"KILOGRAM\", label: \"Kilograms (kg)\" },\n { value: \"OUNCE\", label: \"Ounces (oz)\" },\n { value: \"POUND\", label: \"Pounds (lb)\" },\n];\n\nconst DIMENSION_UNITS = [\n { value: \"CENTIMETER\", label: \"Centimeters (cm)\" },\n { value: \"INCH\", label: \"Inches (in)\" },\n { value: \"METER\", label: \"Meters (m)\" },\n];\n\nconst REQUIRED_FIELDS: (keyof FormState)[] = [\n \"name\",\n \"slug\",\n \"type\",\n \"shortDescription\",\n \"price\",\n \"currency\",\n \"stockQuantity\",\n];\n\n// ─── GraphQL strings ──────────────────────────────────────────────────────────\n\nconst MY_PUBLISHER_QUERY = /* GraphQL */ `\n query SellerMyPublisher {\n myPublisher {\n id\n }\n }\n`;\n\nconst REGISTER_PUBLISHER_MUTATION = /* GraphQL */ `\n mutation RegisterPublisher($input: RegisterPublisherInput!) {\n registerPublisher(input: $input) {\n id\n name\n email\n }\n }\n`;\n\nconst CREATE_PHYSICAL_PRODUCT_MUTATION = /* GraphQL */ `\n mutation CreatePhysicalProduct($input: CreatePhysicalProductInput!) {\n createPhysicalProduct(input: $input) {\n id\n name\n slug\n status\n nature\n type\n price\n currency\n icon\n bannerUrl\n screenshots\n }\n }\n`;\n\nconst SUBMIT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation SubmitProduct($id: ID!, $notes: String) {\n submitProduct(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction deriveSlug(name: string): string {\n return name.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction isFormValid(form: FormState): boolean {\n return REQUIRED_FIELDS.every((field) => {\n const value = form[field];\n if (typeof value === \"boolean\") return true;\n return value.trim() !== \"\";\n });\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport const CreatePhysicalItemPage: FC = () => {\n const {\n navigate,\n basePath,\n productId,\n apiGatewayUrl,\n authToken,\n workspacesGatewayUrl,\n workspaceId,\n getWorkspaceToken,\n } = useDevportal();\n const {\n organization: sellerOrganization,\n organizations: sellerOrganizations,\n loading: sellerOrgLoading,\n error: sellerOrgError,\n selectOrganization,\n } = useSellerOrganization();\n\n const [form, setForm] = useState<FormState>({\n name: \"\",\n slug: \"\",\n type: \"\",\n category: \"\",\n shortDescription: \"\",\n longDescription: \"\",\n price: \"\",\n currency: \"\",\n tags: \"\",\n stockQuantity: \"1\",\n sku: \"\",\n trackInventory: true,\n allowBackorder: false,\n lowStockThreshold: \"\",\n requiresShipping: true,\n weightKg: \"\",\n weightUnit: \"KILOGRAM\",\n length: \"\",\n width: \"\",\n height: \"\",\n dimensionUnit: \"CENTIMETER\",\n brand: \"\",\n manufacturer: \"\",\n countryOfOrigin: \"\",\n warrantyInfo: \"\",\n returnPolicy: \"\",\n });\n\n const [icon, setIcon] = useState(\"\");\n const [bannerUrl, setBannerUrl] = useState(\"\");\n const [screenshots, setScreenshots] = useState<string[]>([]);\n const {\n upload: uploadIcon,\n uploading: iconUploading,\n progress: iconProgress,\n error: iconUploadError,\n } = useAssetUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n );\n const {\n upload: uploadBanner,\n uploading: bannerUploading,\n progress: bannerProgress,\n error: bannerUploadError,\n } = useAssetUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n );\n const {\n upload: uploadScreenshot,\n uploading: screenshotUploading,\n progress: screenshotProgress,\n error: screenshotUploadError,\n } = useAssetUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n );\n\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n const imagesUploading =\n iconUploading || bannerUploading || screenshotUploading;\n const isValid = isFormValid(form) && !!sellerOrganization && !imagesUploading;\n const endpoint = resolveGlobalGraphqlEndpoint(apiGatewayUrl);\n\n // ─── Field helpers ────────────────────────────────────────────────────────\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) => {\n setForm((prev) => ({ ...prev, [key]: value }));\n };\n\n const handleNameChange = (e: ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setForm((prev) => ({\n ...prev,\n name,\n slug: deriveSlug(name),\n }));\n };\n\n const handleCheckboxChange =\n (key: \"trackInventory\" | \"requiresShipping\" | \"allowBackorder\") =>\n (e: ChangeEvent<HTMLInputElement>) => {\n setField(key, e.target.checked);\n };\n\n // ─── GraphQL fetch helper ─────────────────────────────────────────────────\n\n async function gqlFetch<T = unknown>(\n query: string,\n variables?: Record<string, unknown>,\n ): Promise<T> {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),\n },\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n const json = (await res.json()) as {\n data?: T;\n errors?: { message: string }[];\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data as T;\n }\n\n // ─── Submit flow ──────────────────────────────────────────────────────────\n\n const handleSubmit = async () => {\n if (!isValid || submitting || !sellerOrganization) return;\n setSubmitting(true);\n setSubmitError(null);\n\n try {\n // Step 1: check if publisher exists\n const publisherData = await gqlFetch<{\n myPublisher: { id: string } | null;\n }>(MY_PUBLISHER_QUERY);\n\n let publisherId = publisherData.myPublisher?.id ?? null;\n\n if (!publisherId) {\n const registerData = await gqlFetch<{\n registerPublisher: { id: string };\n }>(REGISTER_PUBLISHER_MUTATION, {\n input: {\n name: sellerOrganization.legalEntityName ?? sellerOrganization.name,\n email: sellerOrganization.contactEmail ?? \"seller@burdenoff.com\",\n websiteUrl: sellerOrganization.websiteUrl ?? null,\n legalName:\n sellerOrganization.legalEntityName ?? sellerOrganization.name,\n country: sellerOrganization.taxCountry ?? null,\n contactEmail: sellerOrganization.contactEmail ?? null,\n },\n });\n\n publisherId = registerData.registerPublisher.id;\n }\n\n // Step 2: create the product, linked to the seller's PHYSICAL org —\n // validated server-side (createPhysicalProduct rejects any org that\n // isn't PHYSICAL-kind, see DeveloperOrgKind / BOFF-7285).\n const parsedTags = form.tags\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n\n const hasDimensions =\n form.length.trim() || form.width.trim() || form.height.trim();\n\n const productData = await gqlFetch<{\n createPhysicalProduct: { id: string };\n }>(CREATE_PHYSICAL_PRODUCT_MUTATION, {\n input: {\n name: form.name.trim(),\n slug: form.slug.trim(),\n type: form.type,\n category: form.category.trim() || null,\n description: form.shortDescription.trim(),\n longDescription: form.longDescription.trim() || null,\n price: parseFloat(form.price),\n currency: form.currency,\n tags: parsedTags.length > 0 ? parsedTags : null,\n stockQuantity: parseInt(form.stockQuantity, 10),\n sku: form.sku.trim() || null,\n trackInventory: form.trackInventory,\n allowBackorder: form.allowBackorder,\n lowStockThreshold: form.lowStockThreshold.trim()\n ? parseInt(form.lowStockThreshold, 10)\n : null,\n requiresShipping: form.requiresShipping,\n weight:\n form.requiresShipping && form.weightKg.trim()\n ? parseFloat(form.weightKg)\n : null,\n weightUnit:\n form.requiresShipping && form.weightKg.trim()\n ? form.weightUnit\n : null,\n length:\n form.requiresShipping && form.length.trim()\n ? parseFloat(form.length)\n : null,\n width:\n form.requiresShipping && form.width.trim()\n ? parseFloat(form.width)\n : null,\n height:\n form.requiresShipping && form.height.trim()\n ? parseFloat(form.height)\n : null,\n dimensionUnit:\n form.requiresShipping && hasDimensions ? form.dimensionUnit : null,\n brand: form.brand.trim() || null,\n manufacturer: form.manufacturer.trim() || null,\n countryOfOrigin: form.countryOfOrigin.trim() || null,\n warrantyInfo: form.warrantyInfo.trim() || null,\n returnPolicy: form.returnPolicy.trim() || null,\n icon: icon || null,\n bannerUrl: bannerUrl || null,\n screenshots: screenshots.length > 0 ? screenshots : null,\n // Tags this listing with the host product (e.g. \"healthybowl\") so\n // it can be filtered to that product's marketplace. Only set when\n // the host shell actually passes a productId — stays exactly as\n // before (no tag) for any shell that doesn't opt in.\n compatibleProducts: productId ? [productId] : null,\n organizationId: sellerOrganization.id,\n },\n });\n\n const newProductId = productData.createPhysicalProduct.id;\n\n // Step 3: submit for review\n await gqlFetch(SUBMIT_PRODUCT_MUTATION, {\n id: newProductId,\n notes: \"Submitted from seller portal\",\n });\n\n // Step 4: success\n setSubmitSuccess(true);\n setTimeout(() => {\n navigate?.(`${basePath}/seller/items`);\n }, 2000);\n } catch (err) {\n setSubmitError(\n err instanceof Error\n ? err.message\n : \"Submission failed. Please try again.\",\n );\n } finally {\n setSubmitting(false);\n }\n };\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <PageLayout\n title=\"List a Physical Item\"\n description=\"Fill in all required details. Your listing will be reviewed before it appears in the store.\"\n backLink=\"/seller/items\"\n backLabel=\"Back to My Items\"\n >\n <div className=\"space-y-8 max-w-3xl\">\n {/* Success banner */}\n {submitSuccess && (\n <div className=\"rounded-card border border-status-success-text/30 bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text\">\n Your listing has been submitted for review! Redirecting...\n </div>\n )}\n\n {/* Error banner */}\n {submitError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n {submitError}\n </div>\n )}\n\n {/* Seller org resolution error — the form can't be submitted without one */}\n {sellerOrgError && (\n <div className=\"rounded-card border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text\">\n Couldn't set up your seller organization: {sellerOrgError}\n </div>\n )}\n\n {/* ── Section 0: Seller Organization ── */}\n <PageSection\n title=\"Seller Organization\"\n description=\"The seller organization this item will be listed under.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-3\">\n <SelectField\n label=\"Organization\"\n required\n value={sellerOrganization?.id ?? \"\"}\n onChange={(e) => selectOrganization(e.target.value)}\n options={sellerOrganizations.map((org) => ({\n value: org.id,\n label: org.name,\n }))}\n placeholder={\n sellerOrgLoading\n ? \"Resolving your seller organization…\"\n : \"Select an organization\"\n }\n disabled={sellerOrgLoading || sellerOrganizations.length <= 1}\n hint={\n sellerOrganizations.length > 1\n ? \"You have more than one seller organization — pick the one this item belongs to.\"\n : sellerOrganization\n ? `Automatically set to your only seller organization, \"${sellerOrganization.name}\".`\n : undefined\n }\n />\n </div>\n </PageSection>\n\n {/* ── Section 1: Basic Information ── */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details shown in the store listing.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <InputField\n label=\"Name\"\n required\n value={form.name}\n onChange={handleNameChange}\n placeholder=\"e.g. Wireless Ergonomic Keyboard\"\n maxLength={120}\n />\n\n <InputField\n label=\"Slug\"\n required\n value={form.slug}\n onChange={(e) => setField(\"slug\", e.target.value)}\n placeholder=\"auto-derived-from-name\"\n hint=\"URL-friendly identifier. Auto-derived from name; you can edit it.\"\n />\n\n <SelectField\n label=\"Product Type\"\n required\n value={form.type}\n onChange={(e) => setField(\"type\", e.target.value)}\n options={PRODUCT_CATEGORIES}\n placeholder=\"Select a type\"\n />\n\n <SelectField\n label=\"Store Category\"\n value={form.category}\n onChange={(e) => setField(\"category\", e.target.value)}\n options={STORE_CATEGORIES}\n placeholder=\"General / uncategorised\"\n hint=\"Pick an Agri-Inputs category to list this item in the HealthyBowl Agri-Inputs Store. Leave blank for a general store listing.\"\n />\n\n <InputField\n label=\"Short Description\"\n required\n value={form.shortDescription}\n onChange={(e) => setField(\"shortDescription\", e.target.value)}\n placeholder=\"One-line summary shown on the store card\"\n maxLength={160}\n hint={`${form.shortDescription.length}/160`}\n />\n\n <TextareaField\n label=\"Long Description\"\n value={form.longDescription}\n onChange={(e) => setField(\"longDescription\", e.target.value)}\n rows={5}\n placeholder=\"Detailed product description, features, specs…\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 2: Product Images ── */}\n <PageSection\n title=\"Product Images\"\n description=\"Visual assets shown to buyers browsing the store.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid gap-5 sm:grid-cols-2\">\n <ImageUploadField\n label=\"Icon\"\n value={icon}\n onChange={setIcon}\n hint=\"Shown in marketplace cards and search — square proportions (1:1)\"\n aspectClass=\"aspect-square\"\n upload={uploadIcon}\n uploading={iconUploading}\n progress={iconProgress}\n error={iconUploadError}\n />\n <ImageUploadField\n label=\"Banner\"\n value={bannerUrl}\n onChange={setBannerUrl}\n hint=\"Fills the right panel on the product page — square or portrait works best on all screen sizes (1:1)\"\n aspectClass=\"aspect-square\"\n upload={uploadBanner}\n uploading={bannerUploading}\n progress={bannerProgress}\n error={bannerUploadError}\n />\n </div>\n <ScreenshotGallery\n urls={screenshots}\n onChange={setScreenshots}\n upload={uploadScreenshot}\n uploading={screenshotUploading}\n progress={screenshotProgress}\n error={screenshotUploadError}\n />\n </div>\n </PageSection>\n\n {/* ── Section 3: Pricing ── */}\n <PageSection title=\"Pricing\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Price\"\n required\n type=\"number\"\n value={form.price}\n onChange={(e) => setField(\"price\", e.target.value)}\n placeholder=\"0.00\"\n min={0.01}\n step=\"0.01\"\n />\n\n <SelectField\n label=\"Currency\"\n required\n value={form.currency}\n onChange={(e) => setField(\"currency\", e.target.value)}\n options={CURRENCIES}\n placeholder=\"Select currency\"\n />\n </div>\n\n <InputField\n label=\"Tags\"\n value={form.tags}\n onChange={(e) => setField(\"tags\", e.target.value)}\n placeholder=\"mechanical, wireless, ergonomic\"\n hint=\"Comma-separated keywords to help buyers find your item.\"\n />\n </div>\n </PageSection>\n\n {/* ── Section 4: Inventory ── */}\n <PageSection title=\"Inventory\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Stock Quantity\"\n required\n type=\"number\"\n value={form.stockQuantity}\n onChange={(e) => setField(\"stockQuantity\", e.target.value)}\n min={0}\n step={1}\n />\n\n <InputField\n label=\"SKU\"\n value={form.sku}\n onChange={(e) => setField(\"sku\", e.target.value)}\n placeholder=\"e.g. KB-WL-001\"\n hint=\"Your internal stock-keeping unit reference.\"\n />\n </div>\n\n <InputField\n label=\"Low Stock Threshold\"\n type=\"number\"\n value={form.lowStockThreshold}\n onChange={(e) => setField(\"lowStockThreshold\", e.target.value)}\n placeholder=\"e.g. 5\"\n min={0}\n step={1}\n hint=\"Get flagged when stock falls to or below this number.\"\n />\n\n <CheckboxField\n label=\"Track Inventory\"\n description=\"Automatically reduce stock when orders are placed.\"\n checked={form.trackInventory}\n onChange={handleCheckboxChange(\"trackInventory\")}\n />\n\n <CheckboxField\n label=\"Allow Backorder\"\n description=\"Let buyers order even when stock quantity is zero.\"\n checked={form.allowBackorder}\n onChange={handleCheckboxChange(\"allowBackorder\")}\n />\n </div>\n </PageSection>\n\n {/* ── Section 5: Shipping ── */}\n <PageSection title=\"Shipping\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <CheckboxField\n label=\"Requires Shipping\"\n description=\"This item will be physically shipped to the buyer.\"\n checked={form.requiresShipping}\n onChange={handleCheckboxChange(\"requiresShipping\")}\n />\n\n {form.requiresShipping && (\n <>\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Weight\"\n type=\"number\"\n value={form.weightKg}\n onChange={(e) => setField(\"weightKg\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n hint=\"Used to calculate shipping costs.\"\n />\n\n <SelectField\n label=\"Weight Unit\"\n value={form.weightUnit}\n onChange={(e) => setField(\"weightUnit\", e.target.value)}\n options={WEIGHT_UNITS}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <InputField\n label=\"Length\"\n type=\"number\"\n value={form.length}\n onChange={(e) => setField(\"length\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Width\"\n type=\"number\"\n value={form.width}\n onChange={(e) => setField(\"width\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n\n <InputField\n label=\"Height\"\n type=\"number\"\n value={form.height}\n onChange={(e) => setField(\"height\", e.target.value)}\n placeholder=\"0.0\"\n min={0}\n step=\"0.01\"\n />\n </div>\n\n <SelectField\n label=\"Dimension Unit\"\n value={form.dimensionUnit}\n onChange={(e) => setField(\"dimensionUnit\", e.target.value)}\n options={DIMENSION_UNITS}\n hint=\"Applies to length, width, and height above.\"\n />\n </>\n )}\n\n <p className=\"flex items-start gap-2 rounded-input border border-border-subtle bg-bg-sunken px-3 py-2 text-xs text-text-secondary\">\n <Info size={13} className=\"mt-0.5 shrink-0 text-text-muted\" />\n Delivery fees are configured at the organization level in store\n settings.\n </p>\n </div>\n </PageSection>\n\n {/* ── Section 6: Product Details ── */}\n <PageSection\n title=\"Product Details\"\n description=\"Optional supplemental information for buyers.\"\n >\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6 space-y-5\">\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-2\">\n <InputField\n label=\"Brand\"\n value={form.brand}\n onChange={(e) => setField(\"brand\", e.target.value)}\n placeholder=\"e.g. Logitech\"\n />\n\n <InputField\n label=\"Manufacturer\"\n value={form.manufacturer}\n onChange={(e) => setField(\"manufacturer\", e.target.value)}\n placeholder=\"e.g. Logitech Manufacturing Ltd.\"\n />\n </div>\n\n <InputField\n label=\"Country of Origin\"\n value={form.countryOfOrigin}\n onChange={(e) => setField(\"countryOfOrigin\", e.target.value)}\n placeholder=\"e.g. China\"\n />\n\n <InputField\n label=\"Warranty Info\"\n value={form.warrantyInfo}\n onChange={(e) => setField(\"warrantyInfo\", e.target.value)}\n placeholder=\"e.g. 1-year limited manufacturer warranty\"\n />\n\n <TextareaField\n label=\"Return Policy\"\n value={form.returnPolicy}\n onChange={(e) => setField(\"returnPolicy\", e.target.value)}\n rows={3}\n placeholder=\"e.g. 30-day returns accepted\"\n />\n </div>\n </PageSection>\n\n {/* ── Actions ── */}\n <div className=\"flex items-center gap-3 pb-8\">\n <button\n type=\"button\"\n onClick={() => navigate?.(`${basePath}/seller/items`)}\n className=\"rounded-button border border-border-strong px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!isValid || submitting}\n aria-disabled={!isValid || submitting}\n title={\n sellerOrgLoading\n ? \"Setting up your seller organization...\"\n : !isValid\n ? \"Fill in all required fields to continue\"\n : undefined\n }\n className={cn(\n \"inline-flex items-center gap-2 rounded-button px-5 py-2 text-sm font-medium transition-colors\",\n \"bg-action-primary-bg text-action-primary-text\",\n \"hover:bg-action-primary-bg/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n \"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2\",\n )}\n >\n {submitting ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Submitting...\n </>\n ) : sellerOrgLoading ? (\n <>\n <Loader2 size={15} className=\"animate-spin\" />\n Setting up your store...\n </>\n ) : (\n \"Create & Submit for Review\"\n )}\n </button>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|