@burdenoff/microfe-devportal 2026.629.1 → 2026.629.3
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/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +101 -98
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +10 -6
- package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/generated/global-operations.js +4 -3
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationVersionsPage.js","names":[],"sources":["../../../src/devportal/pages/ApplicationVersionsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n Package,\n CheckCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n Upload,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { format } from \"date-fns\";\nimport {\n useGetApplicationQuery,\n useGetApplicationVersionsQuery,\n useCreateApplicationVersionMutation,\n usePublishApplicationVersionMutation,\n useDeprecateApplicationVersionMutation,\n type ApplicationVersionFieldsFragment,\n} from \"../hooks\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { usePackageUpload } from \"../hooks/usePackageUpload\";\n\nexport const ApplicationVersionsPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canCreate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_CREATE,\n );\n const { allowed: canPublish } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_PUBLISH,\n );\n const { allowed: canDeprecate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_DEPRECATE,\n );\n const { appId } = useParams<{ appId: string }>();\n const { workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken } =\n useDevportal();\n const {\n upload: uploadZip,\n uploading: zipUploading,\n progress: zipProgress,\n error: zipUploadError,\n } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n );\n const zipInputRef = useRef<HTMLInputElement>(null);\n const [showCreateDialog, setShowCreateDialog] = useState(false);\n const [newVersion, setNewVersion] = useState({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n });\n const [actionError, setActionError] = useState<string | null>(null);\n const [actionSuccess, setActionSuccess] = useState<string | null>(null);\n const [selectedVersionIds, setSelectedVersionIds] = useState<Set<string>>(\n new Set(),\n );\n const [bulkAction, setBulkAction] = useState<\"publish\" | \"deprecate\" | null>(\n null,\n );\n\n const { data: appData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n const isWorkflowApp = appData?.application?.appType === \"WORKFLOW\";\n useEffect(() => {\n const name = appData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, appData?.application?.name]);\n\n // Query\n const {\n data: versionsData,\n loading: versionsLoading,\n error: versionsError,\n } = useGetApplicationVersionsQuery({\n variables: { applicationId: appId! },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Mutations\n const [createVersion, { loading: creating }] =\n useCreateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [publishVersion] = usePublishApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [deprecateVersion] = useDeprecateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const versions: ApplicationVersionFieldsFragment[] = useMemo(\n () =>\n versionsData?.application?.versions?.edges?.map(\n (edge: { node: ApplicationVersionFieldsFragment }) => edge.node,\n ) ?? [],\n [versionsData?.application?.versions],\n );\n\n const selectedVersions = useMemo(\n () => versions.filter((version) => selectedVersionIds.has(version.id)),\n [versions, selectedVersionIds],\n );\n const publishableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) => version.status?.toUpperCase() === \"DRAFT\",\n ),\n [selectedVersions],\n );\n const deprecatableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) =>\n version.status?.toUpperCase() === \"PUBLISHED\" && !version.isLatest,\n ),\n [selectedVersions],\n );\n const allVisibleSelected =\n versions.length > 0 &&\n versions.every((version) => selectedVersionIds.has(version.id));\n const partiallySelected =\n versions.some((version) => selectedVersionIds.has(version.id)) &&\n !allVisibleSelected;\n\n const handleCreate = async () => {\n if (!appId || !newVersion.version.trim()) return;\n setActionError(null);\n\n let parsedManifest: Record<string, unknown> | undefined;\n if (isWorkflowApp) {\n if (!newVersion.manifestJson.trim()) {\n setActionError(\n \"Workflow Definition (JSON) is required — paste the {nodes, edges} object.\",\n );\n return;\n }\n try {\n parsedManifest = JSON.parse(newVersion.manifestJson) as Record<\n string,\n unknown\n >;\n } catch {\n setActionError(\n \"Invalid JSON in Workflow Definition — please check your input.\",\n );\n return;\n }\n } else if (newVersion.manifestJson.trim()) {\n try {\n parsedManifest = JSON.parse(newVersion.manifestJson) as Record<\n string,\n unknown\n >;\n } catch {\n setActionError(\"Invalid JSON in manifest — please check your input.\");\n return;\n }\n }\n\n try {\n await createVersion({\n variables: {\n applicationId: appId,\n input: {\n version: newVersion.version,\n releaseNotes: newVersion.releaseNotes || undefined,\n changelog: newVersion.changelog || undefined,\n packageUrl: newVersion.packageUrl || undefined,\n packageSize: newVersion.packageSize || undefined,\n manifest: parsedManifest,\n },\n },\n });\n setShowCreateDialog(false);\n setNewVersion({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n });\n setActionSuccess(\n tr(\"devportal.versionCreatedSuccess\", \"Version created successfully\"),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionCreateFailed\", \"Failed to create version\"),\n );\n }\n };\n\n const handleZipUpload = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n e.target.value = \"\";\n try {\n const result = await uploadZip(file);\n setNewVersion((prev) => ({\n ...prev,\n packageUrl: result.downloadUrl,\n packageSize: String(file.size),\n }));\n } catch {\n // error shown via zipUploadError prop\n }\n },\n [uploadZip],\n );\n\n const handlePublish = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await publishVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionPublishedSuccess\",\n \"Version published successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionPublishFailed\", \"Failed to publish version\"),\n );\n }\n };\n\n const handleDeprecate = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await deprecateVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionDeprecatedSuccess\",\n \"Version deprecated successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionDeprecateFailed\",\n \"Failed to deprecate version\",\n ),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n const statusUp = version.status?.toUpperCase();\n if (statusUp === \"PUBLISHED\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (statusUp === \"DRAFT\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (statusUp === \"DEPRECATED\") {\n return <Archive size={16} className=\"text-text-secondary\" />;\n }\n return <Package size={16} className=\"text-accent-blue\" />;\n };\n\n const toggleVersionSelection = (versionId: string, checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(versionId);\n } else {\n next.delete(versionId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVersions = (checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n versions.forEach((version) => {\n if (checked) {\n next.add(version.id);\n } else {\n next.delete(version.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVersionIds(new Set());\n\n const handleBulkPublish = async () => {\n if (publishableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n publishableVersions.map((version) =>\n publishVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Published ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n const handleBulkDeprecate = async () => {\n if (deprecatableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n deprecatableVersions.map((version) =>\n deprecateVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Deprecated ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n // Loading state\n if (versionsLoading && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (versionsError && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.failedToLoadVersions\",\n \"Failed to load versions\",\n )}\n description={\n versionsError.message ||\n tr(\n \"devportal.versionsLoadError\",\n \"An error occurred while loading versions\",\n )\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n purpose={isWorkflowApp\n ? tr(\"devportal.versionManagementPurposeWorkflow\", \"Versions track releases of your workflow. Create a version for each significant change, add release notes, and publish it. The actual workflow definition is configured in the Store Listing tab.\")\n : tr(\"devportal.versionManagementPurpose\", \"Versions are the releases of your app that users install. Cut a new version when you ship changes, upload its package, and publish it — the one marked Latest is what new installs receive, so promote carefully.\")}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => setShowCreateDialog(true)}\n disabled={!canCreate}\n aria-disabled={!canCreate}\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 disabled:opacity-50\"\n >\n <Plus size={16} />\n {tr(\"devportal.newVersion\", \"New Version\")}\n </button>\n }\n >\n {/* Action Error Banner */}\n {actionError && (\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-3 flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} className=\"flex-shrink-0\" />\n {actionError}\n <button\n onClick={() => setActionError(null)}\n className=\"ml-auto text-status-error-text hover:text-status-error-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {actionSuccess && (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-3 flex items-center gap-2 text-sm text-status-success-text\">\n <CheckCircle size={16} className=\"flex-shrink-0\" />\n {actionSuccess}\n <button\n onClick={() => setActionSuccess(null)}\n className=\"ml-auto text-status-success-text hover:text-status-success-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {versions.length > 0 && (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4\">\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={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedVersionIds.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 {selectedVersionIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction(\"publish\")}\n disabled={publishableVersions.length === 0 || !canPublish}\n aria-disabled={!canPublish}\n className=\"rounded-md border border-status-success-bg px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.publishSelected\", \"Publish Selected\")}\n {publishableVersions.length > 0\n ? ` (${publishableVersions.length})`\n : \"\"}\n </button>\n <button\n onClick={() => setBulkAction(\"deprecate\")}\n disabled={deprecatableVersions.length === 0 || !canDeprecate}\n aria-disabled={!canDeprecate}\n className=\"rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.deprecateSelected\", \"Deprecate Selected\")}\n {deprecatableVersions.length > 0\n ? ` (${deprecatableVersions.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {versions.length > 0 ? (\n <DataTable\n data={versions}\n keyField=\"id\"\n columns={[\n {\n key: \"select\",\n header: (\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n ),\n className: \"w-12\",\n render: (v) => (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedVersionIds.has(v.id)}\n onChange={(checked) =>\n toggleVersionSelection(v.id, checked)\n }\n ariaLabel={`Select version ${v.version}`}\n />\n </div>\n ),\n },\n {\n key: \"version\",\n header: tr(\"devportal.version\", \"Version\"),\n render: (v) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(v)}\n <span className=\"font-mono font-medium\">{v.version}</span>\n {v.isLatest && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs text-status-success-text\">\n {tr(\"devportal.latest\", \"Latest\")}\n </span>\n )}\n {!v.isStable && (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs text-status-warning-text\">\n {tr(\"devportal.beta\", \"Beta\")}\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"releaseNotes\",\n header: tr(\"devportal.releaseNotes\", \"Release Notes\"),\n render: (v) => (\n <p className=\"max-w-md truncate text-sm text-text-secondary\">\n {v.releaseNotes ||\n tr(\"devportal.noReleaseNotes\", \"No release notes\")}\n </p>\n ),\n },\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (v) => <StatusBadge status={v.status} />,\n },\n {\n key: \"publishedAt\",\n header: tr(\"devportal.published\", \"Published\"),\n render: (v) =>\n v.publishedAt\n ? format(new Date(v.publishedAt), \"MMM d, yyyy\")\n : \"-\",\n },\n {\n key: \"actions\",\n header: \"\",\n className: \"w-32\",\n render: (v) => (\n <div className=\"flex gap-2\">\n {v.status?.toUpperCase() === \"DRAFT\" && (\n <button\n className=\"text-xs text-primary hover:underline disabled:opacity-50\"\n disabled={!canPublish}\n aria-disabled={!canPublish}\n onClick={() => handlePublish(v.id)}\n >\n {tr(\"devportal.publish\", \"Publish\")}\n </button>\n )}\n {v.status?.toUpperCase() === \"PUBLISHED\" && !v.isLatest && (\n <button\n className=\"text-xs text-status-warning-text hover:underline disabled:opacity-50\"\n disabled={!canDeprecate}\n aria-disabled={!canDeprecate}\n onClick={() => handleDeprecate(v.id)}\n >\n {tr(\"devportal.deprecate\", \"Deprecate\")}\n </button>\n )}\n </div>\n ),\n },\n ]}\n />\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title={tr(\"devportal.noVersionsYet\", \"No versions yet\")}\n description={tr(\n \"devportal.noVersionsYetDescription\",\n \"Create your first version to start distributing your app\",\n )}\n action={\n <button\n onClick={() => setShowCreateDialog(true)}\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.createVersion\", \"Create Version\")}\n </button>\n }\n />\n )}\n\n {/* Create Version Dialog */}\n {showCreateDialog && (\n <ConfirmDialog\n open={showCreateDialog}\n onOpenChange={setShowCreateDialog}\n onConfirm={handleCreate}\n title={tr(\"devportal.createNewVersion\", \"Create New Version\")}\n confirmLabel={\n creating\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createVersion\", \"Create Version\")\n }\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.versionNumber\", \"Version Number\")}\n value={newVersion.version}\n onChange={(e) =>\n setNewVersion({ ...newVersion, version: e.target.value })\n }\n placeholder=\"e.g., 2.2.0\"\n hint={tr(\n \"devportal.versionHint\",\n \"Use semantic versioning (major.minor.patch)\",\n )}\n required\n />\n {isWorkflowApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Workflow Definition (JSON){\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={10}\n required\n placeholder={`{\\n \"nodes\": [],\\n \"edges\": []\\n}`}\n value={newVersion.manifestJson}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n manifestJson: e.target.value,\n })\n }\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-xs font-mono resize-y placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-secondary\">\n Paste the full <code>{`{nodes, edges}`}</code> JSON. Saved\n as this version's manifest — no zip needed.\n </p>\n </div>\n )}\n {/* Zip upload — node apps only */}\n {!isWorkflowApp && <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">Package (.zip)</label>\n <div className=\"flex gap-2 items-center\">\n <input\n ref={zipInputRef}\n type=\"file\"\n accept=\".zip\"\n className=\"hidden\"\n onChange={handleZipUpload}\n />\n <button\n type=\"button\"\n disabled={zipUploading}\n onClick={() => zipInputRef.current?.click()}\n className=\"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {zipUploading ? (\n <>\n <Loader2 size={14} className=\"animate-spin\" />{\" \"}\n Uploading… {zipProgress}%\n </>\n ) : (\n <>\n <Upload size={14} />{\" \"}\n {newVersion.packageUrl ? \"Replace zip\" : \"Upload zip\"}\n </>\n )}\n </button>\n {newVersion.packageUrl && !zipUploading && (\n <span className=\"text-xs text-status-success-text\">\n ✓ Uploaded\n </span>\n )}\n </div>\n {zipUploadError && (\n <p className=\"text-xs text-status-error-text\">\n {zipUploadError}\n </p>\n )}\n </div>}\n\n {/* Manifest JSON — optional alongside zip, node apps only */}\n {!isWorkflowApp && <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n manifest.json{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional — paste if not bundled inside the zip)\n </span>\n </label>\n <textarea\n rows={5}\n placeholder={`{\\n \"manifestVersion\": \"1.0\",\\n \"itemType\": \"node\",\\n \"identity\": { \"version\": \"1.0.0\", \"displayName\": \"My Node\" }\\n}`}\n value={newVersion.manifestJson}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n manifestJson: e.target.value,\n })\n }\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-xs font-mono resize-y placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>}\n <TextareaField\n label={tr(\"devportal.releaseNotes\", \"Release Notes\")}\n value={newVersion.releaseNotes}\n onChange={(e) =>\n setNewVersion({ ...newVersion, releaseNotes: e.target.value })\n }\n placeholder={tr(\n \"devportal.releaseNotesPlaceholder\",\n \"What changed in this release?\",\n )}\n rows={2}\n />\n </div>\n </ConfirmDialog>\n )}\n\n <ConfirmDialog\n open={bulkAction === \"publish\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkPublish}\n title={tr(\n \"devportal.publishSelectedVersions\",\n \"Publish selected versions\",\n )}\n description={`${tr(\"devportal.publish\", \"Publish\")} ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"}?`}\n confirmLabel={tr(\"devportal.publish\", \"Publish\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"success\"\n />\n\n <ConfirmDialog\n open={bulkAction === \"deprecate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkDeprecate}\n title={tr(\n \"devportal.deprecateSelectedVersions\",\n \"Deprecate selected versions\",\n )}\n description={`${tr(\"devportal.deprecate\", \"Deprecate\")} ${deprecatableVersions.length} selected version${deprecatableVersions.length === 1 ? \"\" : \"s\"}? ${tr(\"devportal.deprecateVersionsWarning\", \"Deprecated versions remain visible but should no longer be promoted.\")}`}\n confirmLabel={tr(\"devportal.deprecate\", \"Deprecate\")}\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":"ApplicationVersionsPage.js","names":[],"sources":["../../../src/devportal/pages/ApplicationVersionsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n Package,\n CheckCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n Upload,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { format } from \"date-fns\";\nimport {\n useGetApplicationQuery,\n useGetApplicationVersionsQuery,\n useCreateApplicationVersionMutation,\n usePublishApplicationVersionMutation,\n useDeprecateApplicationVersionMutation,\n type ApplicationVersionFieldsFragment,\n} from \"../hooks\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { usePackageUpload } from \"../hooks/usePackageUpload\";\n\nexport const ApplicationVersionsPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canCreate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_CREATE,\n );\n const { allowed: canPublish } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_PUBLISH,\n );\n const { allowed: canDeprecate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_DEPRECATE,\n );\n const { appId } = useParams<{ appId: string }>();\n const { workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken } =\n useDevportal();\n const {\n upload: uploadZip,\n uploading: zipUploading,\n progress: zipProgress,\n error: zipUploadError,\n } = usePackageUpload(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n getWorkspaceToken,\n );\n const zipInputRef = useRef<HTMLInputElement>(null);\n const [showCreateDialog, setShowCreateDialog] = useState(false);\n const [newVersion, setNewVersion] = useState({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n });\n const [actionError, setActionError] = useState<string | null>(null);\n const [actionSuccess, setActionSuccess] = useState<string | null>(null);\n const [selectedVersionIds, setSelectedVersionIds] = useState<Set<string>>(\n new Set(),\n );\n const [bulkAction, setBulkAction] = useState<\"publish\" | \"deprecate\" | null>(\n null,\n );\n\n const { data: appData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n const isWorkflowApp = appData?.application?.appType === \"WORKFLOW\";\n useEffect(() => {\n const name = appData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, appData?.application?.name]);\n\n // Query\n const {\n data: versionsData,\n loading: versionsLoading,\n error: versionsError,\n } = useGetApplicationVersionsQuery({\n variables: { applicationId: appId! },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Mutations\n const [createVersion, { loading: creating }] =\n useCreateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [publishVersion] = usePublishApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [deprecateVersion] = useDeprecateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const versions: ApplicationVersionFieldsFragment[] = useMemo(\n () =>\n versionsData?.application?.versions?.edges?.map(\n (edge: { node: ApplicationVersionFieldsFragment }) => edge.node,\n ) ?? [],\n [versionsData?.application?.versions],\n );\n\n const selectedVersions = useMemo(\n () => versions.filter((version) => selectedVersionIds.has(version.id)),\n [versions, selectedVersionIds],\n );\n const publishableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) => version.status?.toUpperCase() === \"DRAFT\",\n ),\n [selectedVersions],\n );\n const deprecatableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) =>\n version.status?.toUpperCase() === \"PUBLISHED\" && !version.isLatest,\n ),\n [selectedVersions],\n );\n const allVisibleSelected =\n versions.length > 0 &&\n versions.every((version) => selectedVersionIds.has(version.id));\n const partiallySelected =\n versions.some((version) => selectedVersionIds.has(version.id)) &&\n !allVisibleSelected;\n\n const handleCreate = async () => {\n if (!appId || !newVersion.version.trim()) return;\n setActionError(null);\n\n let parsedManifest: Record<string, unknown> | undefined;\n if (isWorkflowApp) {\n if (!newVersion.manifestJson.trim()) {\n setActionError(\n \"Workflow Definition (JSON) is required — paste the {nodes, edges} object.\",\n );\n return;\n }\n try {\n parsedManifest = JSON.parse(newVersion.manifestJson) as Record<\n string,\n unknown\n >;\n } catch {\n setActionError(\n \"Invalid JSON in Workflow Definition — please check your input.\",\n );\n return;\n }\n } else if (newVersion.manifestJson.trim()) {\n try {\n parsedManifest = JSON.parse(newVersion.manifestJson) as Record<\n string,\n unknown\n >;\n } catch {\n setActionError(\"Invalid JSON in manifest — please check your input.\");\n return;\n }\n }\n\n try {\n await createVersion({\n variables: {\n applicationId: appId,\n input: {\n version: newVersion.version,\n releaseNotes: newVersion.releaseNotes || undefined,\n changelog: newVersion.changelog || undefined,\n packageUrl: newVersion.packageUrl || undefined,\n packageSize: newVersion.packageSize || undefined,\n manifest: parsedManifest,\n },\n },\n });\n setShowCreateDialog(false);\n setNewVersion({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n });\n setActionSuccess(\n tr(\"devportal.versionCreatedSuccess\", \"Version created successfully\"),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionCreateFailed\", \"Failed to create version\"),\n );\n }\n };\n\n const handleZipUpload = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n e.target.value = \"\";\n try {\n const result = await uploadZip(file);\n setNewVersion((prev) => ({\n ...prev,\n packageUrl: result.downloadUrl,\n packageSize: String(file.size),\n }));\n } catch {\n // error shown via zipUploadError prop\n }\n },\n [uploadZip],\n );\n\n const handlePublish = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await publishVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionPublishedSuccess\",\n \"Version published successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionPublishFailed\", \"Failed to publish version\"),\n );\n }\n };\n\n const handleDeprecate = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await deprecateVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionDeprecatedSuccess\",\n \"Version deprecated successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionDeprecateFailed\",\n \"Failed to deprecate version\",\n ),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n const statusUp = version.status?.toUpperCase();\n if (statusUp === \"PUBLISHED\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (statusUp === \"DRAFT\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (statusUp === \"DEPRECATED\") {\n return <Archive size={16} className=\"text-text-secondary\" />;\n }\n return <Package size={16} className=\"text-accent-blue\" />;\n };\n\n const toggleVersionSelection = (versionId: string, checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(versionId);\n } else {\n next.delete(versionId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVersions = (checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n versions.forEach((version) => {\n if (checked) {\n next.add(version.id);\n } else {\n next.delete(version.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVersionIds(new Set());\n\n const handleBulkPublish = async () => {\n if (publishableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n publishableVersions.map((version) =>\n publishVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Published ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n const handleBulkDeprecate = async () => {\n if (deprecatableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n deprecatableVersions.map((version) =>\n deprecateVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Deprecated ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n // Loading state\n if (versionsLoading && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (versionsError && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.failedToLoadVersions\",\n \"Failed to load versions\",\n )}\n description={\n versionsError.message ||\n tr(\n \"devportal.versionsLoadError\",\n \"An error occurred while loading versions\",\n )\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n purpose={\n isWorkflowApp\n ? tr(\n \"devportal.versionManagementPurposeWorkflow\",\n \"Versions track releases of your workflow. Create a version for each significant change, add release notes, and publish it. The actual workflow definition is configured in the Store Listing tab.\",\n )\n : tr(\n \"devportal.versionManagementPurpose\",\n \"Versions are the releases of your app that users install. Cut a new version when you ship changes, upload its package, and publish it — the one marked Latest is what new installs receive, so promote carefully.\",\n )\n }\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => setShowCreateDialog(true)}\n disabled={!canCreate}\n aria-disabled={!canCreate}\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 disabled:opacity-50\"\n >\n <Plus size={16} />\n {tr(\"devportal.newVersion\", \"New Version\")}\n </button>\n }\n >\n {/* Action Error Banner */}\n {actionError && (\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-3 flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} className=\"flex-shrink-0\" />\n {actionError}\n <button\n onClick={() => setActionError(null)}\n className=\"ml-auto text-status-error-text hover:text-status-error-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {actionSuccess && (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-3 flex items-center gap-2 text-sm text-status-success-text\">\n <CheckCircle size={16} className=\"flex-shrink-0\" />\n {actionSuccess}\n <button\n onClick={() => setActionSuccess(null)}\n className=\"ml-auto text-status-success-text hover:text-status-success-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {versions.length > 0 && (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4\">\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={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedVersionIds.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 {selectedVersionIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction(\"publish\")}\n disabled={publishableVersions.length === 0 || !canPublish}\n aria-disabled={!canPublish}\n className=\"rounded-md border border-status-success-bg px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.publishSelected\", \"Publish Selected\")}\n {publishableVersions.length > 0\n ? ` (${publishableVersions.length})`\n : \"\"}\n </button>\n <button\n onClick={() => setBulkAction(\"deprecate\")}\n disabled={deprecatableVersions.length === 0 || !canDeprecate}\n aria-disabled={!canDeprecate}\n className=\"rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.deprecateSelected\", \"Deprecate Selected\")}\n {deprecatableVersions.length > 0\n ? ` (${deprecatableVersions.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {versions.length > 0 ? (\n <DataTable\n data={versions}\n keyField=\"id\"\n columns={[\n {\n key: \"select\",\n header: (\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n ),\n className: \"w-12\",\n render: (v) => (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedVersionIds.has(v.id)}\n onChange={(checked) =>\n toggleVersionSelection(v.id, checked)\n }\n ariaLabel={`Select version ${v.version}`}\n />\n </div>\n ),\n },\n {\n key: \"version\",\n header: tr(\"devportal.version\", \"Version\"),\n render: (v) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(v)}\n <span className=\"font-mono font-medium\">{v.version}</span>\n {v.isLatest && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs text-status-success-text\">\n {tr(\"devportal.latest\", \"Latest\")}\n </span>\n )}\n {!v.isStable && (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs text-status-warning-text\">\n {tr(\"devportal.beta\", \"Beta\")}\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"releaseNotes\",\n header: tr(\"devportal.releaseNotes\", \"Release Notes\"),\n render: (v) => (\n <p className=\"max-w-md truncate text-sm text-text-secondary\">\n {v.releaseNotes ||\n tr(\"devportal.noReleaseNotes\", \"No release notes\")}\n </p>\n ),\n },\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (v) => <StatusBadge status={v.status} />,\n },\n {\n key: \"publishedAt\",\n header: tr(\"devportal.published\", \"Published\"),\n render: (v) =>\n v.publishedAt\n ? format(new Date(v.publishedAt), \"MMM d, yyyy\")\n : \"-\",\n },\n {\n key: \"actions\",\n header: \"\",\n className: \"w-32\",\n render: (v) => (\n <div className=\"flex gap-2\">\n {v.status?.toUpperCase() === \"DRAFT\" && (\n <button\n className=\"text-xs text-primary hover:underline disabled:opacity-50\"\n disabled={!canPublish}\n aria-disabled={!canPublish}\n onClick={() => handlePublish(v.id)}\n >\n {tr(\"devportal.publish\", \"Publish\")}\n </button>\n )}\n {v.status?.toUpperCase() === \"PUBLISHED\" && !v.isLatest && (\n <button\n className=\"text-xs text-status-warning-text hover:underline disabled:opacity-50\"\n disabled={!canDeprecate}\n aria-disabled={!canDeprecate}\n onClick={() => handleDeprecate(v.id)}\n >\n {tr(\"devportal.deprecate\", \"Deprecate\")}\n </button>\n )}\n </div>\n ),\n },\n ]}\n />\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title={tr(\"devportal.noVersionsYet\", \"No versions yet\")}\n description={tr(\n \"devportal.noVersionsYetDescription\",\n \"Create your first version to start distributing your app\",\n )}\n action={\n <button\n onClick={() => setShowCreateDialog(true)}\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.createVersion\", \"Create Version\")}\n </button>\n }\n />\n )}\n\n {/* Create Version Dialog */}\n {showCreateDialog && (\n <ConfirmDialog\n open={showCreateDialog}\n onOpenChange={setShowCreateDialog}\n onConfirm={handleCreate}\n title={tr(\"devportal.createNewVersion\", \"Create New Version\")}\n confirmLabel={\n creating\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createVersion\", \"Create Version\")\n }\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.versionNumber\", \"Version Number\")}\n value={newVersion.version}\n onChange={(e) =>\n setNewVersion({ ...newVersion, version: e.target.value })\n }\n placeholder=\"e.g., 2.2.0\"\n hint={tr(\n \"devportal.versionHint\",\n \"Use semantic versioning (major.minor.patch)\",\n )}\n required\n />\n {isWorkflowApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Workflow Definition (JSON){\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={10}\n required\n placeholder={`{\\n \"nodes\": [],\\n \"edges\": []\\n}`}\n value={newVersion.manifestJson}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n manifestJson: e.target.value,\n })\n }\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-xs font-mono resize-y placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-secondary\">\n Paste the full <code>{`{nodes, edges}`}</code> JSON. Saved\n as this version's manifest — no zip needed.\n </p>\n </div>\n )}\n {/* Zip upload — node apps only */}\n {!isWorkflowApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">Package (.zip)</label>\n <div className=\"flex gap-2 items-center\">\n <input\n ref={zipInputRef}\n type=\"file\"\n accept=\".zip\"\n className=\"hidden\"\n onChange={handleZipUpload}\n />\n <button\n type=\"button\"\n disabled={zipUploading}\n onClick={() => zipInputRef.current?.click()}\n className=\"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {zipUploading ? (\n <>\n <Loader2 size={14} className=\"animate-spin\" />{\" \"}\n Uploading… {zipProgress}%\n </>\n ) : (\n <>\n <Upload size={14} />{\" \"}\n {newVersion.packageUrl ? \"Replace zip\" : \"Upload zip\"}\n </>\n )}\n </button>\n {newVersion.packageUrl && !zipUploading && (\n <span className=\"text-xs text-status-success-text\">\n ✓ Uploaded\n </span>\n )}\n </div>\n {zipUploadError && (\n <p className=\"text-xs text-status-error-text\">\n {zipUploadError}\n </p>\n )}\n </div>\n )}\n\n {/* Manifest JSON — optional alongside zip, node apps only */}\n {!isWorkflowApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n manifest.json{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional — paste if not bundled inside the zip)\n </span>\n </label>\n <textarea\n rows={5}\n placeholder={`{\\n \"manifestVersion\": \"1.0\",\\n \"itemType\": \"node\",\\n \"identity\": { \"version\": \"1.0.0\", \"displayName\": \"My Node\" }\\n}`}\n value={newVersion.manifestJson}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n manifestJson: e.target.value,\n })\n }\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-xs font-mono resize-y placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n )}\n <TextareaField\n label={tr(\"devportal.releaseNotes\", \"Release Notes\")}\n value={newVersion.releaseNotes}\n onChange={(e) =>\n setNewVersion({ ...newVersion, releaseNotes: e.target.value })\n }\n placeholder={tr(\n \"devportal.releaseNotesPlaceholder\",\n \"What changed in this release?\",\n )}\n rows={2}\n />\n </div>\n </ConfirmDialog>\n )}\n\n <ConfirmDialog\n open={bulkAction === \"publish\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkPublish}\n title={tr(\n \"devportal.publishSelectedVersions\",\n \"Publish selected versions\",\n )}\n description={`${tr(\"devportal.publish\", \"Publish\")} ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"}?`}\n confirmLabel={tr(\"devportal.publish\", \"Publish\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"success\"\n />\n\n <ConfirmDialog\n open={bulkAction === \"deprecate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkDeprecate}\n title={tr(\n \"devportal.deprecateSelectedVersions\",\n \"Deprecate selected versions\",\n )}\n description={`${tr(\"devportal.deprecate\", \"Deprecate\")} ${deprecatableVersions.length} selected version${deprecatableVersions.length === 1 ? \"\" : \"s\"}? ${tr(\"devportal.deprecateVersionsWarning\", \"Deprecated versions remain visible but should no longer be promoted.\")}`}\n confirmLabel={tr(\"devportal.deprecate\", \"Deprecate\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"warning\"\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -15,12 +15,15 @@ import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
|
15
15
|
import { EmphasisPanel as D, GradientText as O, NextSteps as k } from "@burdenoff/fe-libs/ui";
|
|
16
16
|
//#region src/devportal/pages/DashboardPage.tsx
|
|
17
17
|
var A = () => {
|
|
18
|
-
let { navigate: A, basePath: N } = e(), { t:
|
|
19
|
-
let n =
|
|
18
|
+
let { navigate: A, basePath: N, activeOrgId: P } = e(), { t: F } = d(), I = (e, t) => {
|
|
19
|
+
let n = F(e);
|
|
20
20
|
return n === e ? t : n;
|
|
21
|
-
}, { data:
|
|
21
|
+
}, { data: L, loading: R } = n({
|
|
22
|
+
variables: { organizationId: P ?? void 0 },
|
|
23
|
+
fetchPolicy: "cache-and-network"
|
|
24
|
+
}), z = L?.myDeveloperOrganizations ?? [];
|
|
22
25
|
u(() => {
|
|
23
|
-
if (!(
|
|
26
|
+
if (!(R || !L) && !(z.length > 0)) {
|
|
24
27
|
try {
|
|
25
28
|
if (localStorage.getItem("devportal_setup_dismissed")) return;
|
|
26
29
|
} catch {
|
|
@@ -29,45 +32,45 @@ var A = () => {
|
|
|
29
32
|
A?.(`${N}/setup`);
|
|
30
33
|
}
|
|
31
34
|
}, [
|
|
35
|
+
R,
|
|
32
36
|
L,
|
|
33
|
-
|
|
34
|
-
R.length,
|
|
37
|
+
z.length,
|
|
35
38
|
A,
|
|
36
39
|
N
|
|
37
40
|
]);
|
|
38
|
-
let
|
|
39
|
-
totalApps:
|
|
40
|
-
totalOrgs:
|
|
41
|
+
let B = L?.applications?.edges?.map((e) => e.node) ?? [], V = {
|
|
42
|
+
totalApps: L?.applications?.totalCount ?? 0,
|
|
43
|
+
totalOrgs: z.length,
|
|
41
44
|
totalApiKeys: 0,
|
|
42
45
|
totalWebhooks: 0,
|
|
43
|
-
totalApiCalls:
|
|
44
|
-
activeInstalls:
|
|
45
|
-
monthlyRevenue:
|
|
46
|
+
totalApiCalls: z.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
|
|
47
|
+
activeInstalls: z.reduce((e, t) => e + (t.activeInstalls || 0), 0),
|
|
48
|
+
monthlyRevenue: z.reduce((e, t) => e + (t.totalRevenue || 0), 0),
|
|
46
49
|
errorRate: 0
|
|
47
|
-
},
|
|
50
|
+
}, H = (e) => {
|
|
48
51
|
A?.(`${N}${e}`);
|
|
49
|
-
},
|
|
52
|
+
}, U = R, W = !U && z.length > 0;
|
|
50
53
|
return /* @__PURE__ */ T(c, {
|
|
51
54
|
permission: l.APP_READ,
|
|
52
55
|
children: /* @__PURE__ */ E(r, {
|
|
53
56
|
heroBackdrop: !0,
|
|
54
57
|
title: /* @__PURE__ */ E(w, { children: [
|
|
55
|
-
|
|
58
|
+
I("devportal.developer", "Developer"),
|
|
56
59
|
" ",
|
|
57
|
-
/* @__PURE__ */ T(O, { children:
|
|
60
|
+
/* @__PURE__ */ T(O, { children: I("devportal.portal", "Portal") })
|
|
58
61
|
] }),
|
|
59
|
-
description:
|
|
60
|
-
purpose:
|
|
61
|
-
actions:
|
|
62
|
+
description: I("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
|
|
63
|
+
purpose: I("devportal.dashboardPurpose", "This is your developer home base — a single view of the apps you ship, the organizations behind them, and how they are performing (installs, API usage, revenue). Start an organization, then create and publish apps from here."),
|
|
64
|
+
actions: W ? /* @__PURE__ */ E("button", {
|
|
62
65
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
63
|
-
onClick: () =>
|
|
66
|
+
onClick: () => H("/apps/new"),
|
|
64
67
|
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",
|
|
65
|
-
children: [/* @__PURE__ */ T(y, { size: 16 }),
|
|
66
|
-
}) :
|
|
68
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createApp", "Create App")]
|
|
69
|
+
}) : U ? null : /* @__PURE__ */ E("button", {
|
|
67
70
|
"data-tour": "devportal-dashboard-create-org-button",
|
|
68
|
-
onClick: () =>
|
|
71
|
+
onClick: () => H("/organizations/new"),
|
|
69
72
|
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",
|
|
70
|
-
children: [/* @__PURE__ */ T(y, { size: 16 }),
|
|
73
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createOrganization", "Create Organization")]
|
|
71
74
|
}),
|
|
72
75
|
children: [
|
|
73
76
|
/* @__PURE__ */ T(D, {
|
|
@@ -77,93 +80,93 @@ var A = () => {
|
|
|
77
80
|
children: [
|
|
78
81
|
/* @__PURE__ */ T(a, {
|
|
79
82
|
featured: !0,
|
|
80
|
-
title:
|
|
81
|
-
value:
|
|
83
|
+
title: I("devportal.totalApps", "Total Apps"),
|
|
84
|
+
value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalApps,
|
|
82
85
|
icon: /* @__PURE__ */ T(p, { size: 20 }),
|
|
83
86
|
trend: void 0
|
|
84
87
|
}),
|
|
85
88
|
/* @__PURE__ */ T(a, {
|
|
86
|
-
title:
|
|
87
|
-
value:
|
|
89
|
+
title: I("devportal.organizations", "Organizations"),
|
|
90
|
+
value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalOrgs,
|
|
88
91
|
icon: /* @__PURE__ */ T(h, { size: 20 })
|
|
89
92
|
}),
|
|
90
93
|
/* @__PURE__ */ T(a, {
|
|
91
|
-
title:
|
|
92
|
-
value:
|
|
94
|
+
title: I("devportal.apiKeys", "API Keys"),
|
|
95
|
+
value: V.totalApiKeys,
|
|
93
96
|
icon: /* @__PURE__ */ T(_, { size: 20 }),
|
|
94
|
-
description:
|
|
97
|
+
description: I("devportal.activeKeys", "Active keys")
|
|
95
98
|
}),
|
|
96
99
|
/* @__PURE__ */ T(a, {
|
|
97
|
-
title:
|
|
98
|
-
value:
|
|
100
|
+
title: I("devportal.webhooks", "Webhooks"),
|
|
101
|
+
value: V.totalWebhooks,
|
|
99
102
|
icon: /* @__PURE__ */ T(C, { size: 20 }),
|
|
100
|
-
description:
|
|
103
|
+
description: I("devportal.configured", "Configured")
|
|
101
104
|
})
|
|
102
105
|
]
|
|
103
106
|
})
|
|
104
107
|
}),
|
|
105
|
-
!
|
|
108
|
+
!U && /* @__PURE__ */ T(k, {
|
|
106
109
|
storageKey: "devportal-dashboard",
|
|
107
110
|
steps: [
|
|
108
111
|
{
|
|
109
112
|
id: "create-org",
|
|
110
|
-
label:
|
|
111
|
-
description:
|
|
113
|
+
label: I("devportal.createOrganization", "Create Organization"),
|
|
114
|
+
description: I("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
|
|
112
115
|
icon: /* @__PURE__ */ T(h, { size: 16 }),
|
|
113
|
-
onClick: () =>
|
|
114
|
-
done:
|
|
116
|
+
onClick: () => H("/organizations/new"),
|
|
117
|
+
done: W
|
|
115
118
|
},
|
|
116
119
|
{
|
|
117
120
|
id: "create-app",
|
|
118
|
-
label:
|
|
119
|
-
description:
|
|
121
|
+
label: I("devportal.createApp", "Create App"),
|
|
122
|
+
description: I("devportal.startBuildingANewApplication", "Start building a new application"),
|
|
120
123
|
icon: /* @__PURE__ */ T(p, { size: 16 }),
|
|
121
|
-
onClick: () =>
|
|
122
|
-
done:
|
|
124
|
+
onClick: () => H("/apps/new"),
|
|
125
|
+
done: V.totalApps > 0
|
|
123
126
|
},
|
|
124
127
|
{
|
|
125
128
|
id: "api-key",
|
|
126
|
-
label:
|
|
127
|
-
description:
|
|
129
|
+
label: I("devportal.generateApiKey", "Generate API Key"),
|
|
130
|
+
description: I("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
|
|
128
131
|
icon: /* @__PURE__ */ T(_, { size: 16 }),
|
|
129
|
-
onClick: () =>
|
|
132
|
+
onClick: () => H("/api-keys")
|
|
130
133
|
},
|
|
131
134
|
{
|
|
132
135
|
id: "webhooks",
|
|
133
|
-
label:
|
|
134
|
-
description:
|
|
136
|
+
label: I("devportal.configureWebhooks", "Configure Webhooks"),
|
|
137
|
+
description: I("devportal.setUpEventNotifications", "Set up event notifications"),
|
|
135
138
|
icon: /* @__PURE__ */ T(C, { size: 16 }),
|
|
136
|
-
onClick: () =>
|
|
139
|
+
onClick: () => H("/webhooks")
|
|
137
140
|
}
|
|
138
141
|
]
|
|
139
142
|
}),
|
|
140
143
|
/* @__PURE__ */ T(i, {
|
|
141
|
-
title:
|
|
144
|
+
title: I("devportal.usageOverview", "Usage Overview"),
|
|
142
145
|
"data-tour": "devportal-dashboard-usage-overview",
|
|
143
146
|
children: /* @__PURE__ */ E(o, {
|
|
144
147
|
columns: 4,
|
|
145
148
|
children: [
|
|
146
149
|
/* @__PURE__ */ T(a, {
|
|
147
|
-
title:
|
|
148
|
-
value:
|
|
150
|
+
title: I("devportal.apiCalls", "API Calls"),
|
|
151
|
+
value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalApiCalls > 1e6 ? `${(V.totalApiCalls / 1e6).toFixed(1)}M` : V.totalApiCalls > 1e3 ? `${(V.totalApiCalls / 1e3).toFixed(1)}K` : V.totalApiCalls.toString(),
|
|
149
152
|
icon: /* @__PURE__ */ T(f, { size: 20 }),
|
|
150
153
|
trend: void 0
|
|
151
154
|
}),
|
|
152
155
|
/* @__PURE__ */ T(a, {
|
|
153
|
-
title:
|
|
154
|
-
value:
|
|
156
|
+
title: I("devportal.activeInstalls", "Active Installs"),
|
|
157
|
+
value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.activeInstalls > 1e3 ? `${(V.activeInstalls / 1e3).toFixed(1)}K` : V.activeInstalls.toString(),
|
|
155
158
|
icon: /* @__PURE__ */ T(S, { size: 20 }),
|
|
156
159
|
trend: void 0
|
|
157
160
|
}),
|
|
158
161
|
/* @__PURE__ */ T(a, {
|
|
159
|
-
title:
|
|
160
|
-
value:
|
|
162
|
+
title: I("devportal.monthlyRevenue", "Monthly Revenue"),
|
|
163
|
+
value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : `$${V.monthlyRevenue.toLocaleString()}`,
|
|
161
164
|
icon: /* @__PURE__ */ T(g, { size: 20 }),
|
|
162
165
|
trend: void 0
|
|
163
166
|
}),
|
|
164
167
|
/* @__PURE__ */ T(a, {
|
|
165
|
-
title:
|
|
166
|
-
value: `${
|
|
168
|
+
title: I("devportal.errorRate", "Error Rate"),
|
|
169
|
+
value: `${V.errorRate}%`,
|
|
167
170
|
icon: /* @__PURE__ */ T(x, { size: 20 }),
|
|
168
171
|
trend: void 0
|
|
169
172
|
})
|
|
@@ -174,102 +177,102 @@ var A = () => {
|
|
|
174
177
|
className: "grid gap-6 lg:grid-cols-2",
|
|
175
178
|
children: [/* @__PURE__ */ T(i, {
|
|
176
179
|
"data-tour": "devportal-dashboard-recent-apps",
|
|
177
|
-
title:
|
|
180
|
+
title: I("devportal.recentApplications", "Recent Applications"),
|
|
178
181
|
actions: /* @__PURE__ */ E("button", {
|
|
179
|
-
onClick: () =>
|
|
182
|
+
onClick: () => H("/apps"),
|
|
180
183
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
181
184
|
children: [
|
|
182
|
-
|
|
185
|
+
I("devportal.viewAll", "View all"),
|
|
183
186
|
" ",
|
|
184
187
|
/* @__PURE__ */ T(m, { size: 14 })
|
|
185
188
|
]
|
|
186
189
|
}),
|
|
187
|
-
children:
|
|
190
|
+
children: U ? /* @__PURE__ */ T("div", {
|
|
188
191
|
className: "flex items-center justify-center py-8",
|
|
189
192
|
children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
190
|
-
}) :
|
|
193
|
+
}) : B.length > 0 ? /* @__PURE__ */ T("div", {
|
|
191
194
|
className: "space-y-2",
|
|
192
|
-
children:
|
|
195
|
+
children: B.slice(0, 3).map((e) => /* @__PURE__ */ T(s, {
|
|
193
196
|
app: e,
|
|
194
|
-
onClick: () =>
|
|
197
|
+
onClick: () => H(`/apps/${e.id}`)
|
|
195
198
|
}, e.id))
|
|
196
199
|
}) : /* @__PURE__ */ T(t, {
|
|
197
200
|
illustration: "empty-generic",
|
|
198
|
-
title:
|
|
199
|
-
description:
|
|
200
|
-
action:
|
|
201
|
-
onClick: () =>
|
|
201
|
+
title: I("devportal.noApplicationsYet", "No applications yet"),
|
|
202
|
+
description: W ? I("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : I("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
|
|
203
|
+
action: W ? /* @__PURE__ */ E("button", {
|
|
204
|
+
onClick: () => H("/apps/new"),
|
|
202
205
|
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",
|
|
203
|
-
children: [/* @__PURE__ */ T(y, { size: 16 }),
|
|
206
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createApp", "Create App")]
|
|
204
207
|
}) : /* @__PURE__ */ E("button", {
|
|
205
|
-
onClick: () =>
|
|
208
|
+
onClick: () => H("/organizations/new"),
|
|
206
209
|
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",
|
|
207
|
-
children: [/* @__PURE__ */ T(y, { size: 16 }),
|
|
210
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createOrganization", "Create Organization")]
|
|
208
211
|
})
|
|
209
212
|
})
|
|
210
213
|
}), /* @__PURE__ */ T(i, {
|
|
211
|
-
title:
|
|
214
|
+
title: I("devportal.quickActions", "Quick Actions"),
|
|
212
215
|
"data-tour": "devportal-dashboard-quick-actions",
|
|
213
216
|
children: /* @__PURE__ */ E("div", {
|
|
214
217
|
className: "grid gap-3",
|
|
215
218
|
children: [
|
|
216
|
-
|
|
217
|
-
title:
|
|
218
|
-
description:
|
|
219
|
+
W ? /* @__PURE__ */ T(j, {
|
|
220
|
+
title: I("devportal.createNewApp", "Create New App"),
|
|
221
|
+
description: I("devportal.startBuildingANewApplication", "Start building a new application"),
|
|
219
222
|
icon: /* @__PURE__ */ T(p, { size: 20 }),
|
|
220
|
-
onClick: () =>
|
|
223
|
+
onClick: () => H("/apps/new")
|
|
221
224
|
}) : /* @__PURE__ */ T(j, {
|
|
222
|
-
title:
|
|
223
|
-
description:
|
|
225
|
+
title: I("devportal.createOrganization", "Create Organization"),
|
|
226
|
+
description: I("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
|
|
224
227
|
icon: /* @__PURE__ */ T(h, { size: 20 }),
|
|
225
|
-
onClick: () =>
|
|
228
|
+
onClick: () => H("/organizations/new"),
|
|
226
229
|
primary: !0
|
|
227
230
|
}),
|
|
228
|
-
|
|
229
|
-
title:
|
|
230
|
-
description:
|
|
231
|
+
W && /* @__PURE__ */ T(j, {
|
|
232
|
+
title: I("devportal.publisherProfile", "Publisher Profile"),
|
|
233
|
+
description: I("devportal.manageYourStorePresence", "Manage your store publisher presence"),
|
|
231
234
|
icon: /* @__PURE__ */ T(b, { size: 20 }),
|
|
232
|
-
onClick: () =>
|
|
235
|
+
onClick: () => H("/publisher/profile")
|
|
233
236
|
}),
|
|
234
237
|
/* @__PURE__ */ T(j, {
|
|
235
|
-
title:
|
|
236
|
-
description:
|
|
238
|
+
title: I("devportal.generateApiKey", "Generate API Key"),
|
|
239
|
+
description: I("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
|
|
237
240
|
icon: /* @__PURE__ */ T(_, { size: 20 }),
|
|
238
|
-
onClick: () =>
|
|
241
|
+
onClick: () => H("/api-keys")
|
|
239
242
|
}),
|
|
240
243
|
/* @__PURE__ */ T(j, {
|
|
241
|
-
title:
|
|
242
|
-
description:
|
|
244
|
+
title: I("devportal.configureWebhooks", "Configure Webhooks"),
|
|
245
|
+
description: I("devportal.setUpEventNotifications", "Set up event notifications"),
|
|
243
246
|
icon: /* @__PURE__ */ T(C, { size: 20 }),
|
|
244
|
-
onClick: () =>
|
|
247
|
+
onClick: () => H("/webhooks")
|
|
245
248
|
})
|
|
246
249
|
]
|
|
247
250
|
})
|
|
248
251
|
})]
|
|
249
252
|
}),
|
|
250
|
-
|
|
253
|
+
U ? /* @__PURE__ */ T(i, {
|
|
251
254
|
"data-tour": "devportal-dashboard-organizations",
|
|
252
|
-
title:
|
|
255
|
+
title: I("devportal.yourOrganizations", "Your Organizations"),
|
|
253
256
|
children: /* @__PURE__ */ T("div", {
|
|
254
257
|
className: "flex items-center justify-center py-8",
|
|
255
258
|
children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
256
259
|
})
|
|
257
|
-
}) :
|
|
258
|
-
title:
|
|
260
|
+
}) : z.length > 0 ? /* @__PURE__ */ T(i, {
|
|
261
|
+
title: I("devportal.yourOrganizations", "Your Organizations"),
|
|
259
262
|
actions: /* @__PURE__ */ E("button", {
|
|
260
|
-
onClick: () =>
|
|
263
|
+
onClick: () => H("/organizations"),
|
|
261
264
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
262
265
|
children: [
|
|
263
|
-
|
|
266
|
+
I("devportal.viewAll", "View all"),
|
|
264
267
|
" ",
|
|
265
268
|
/* @__PURE__ */ T(m, { size: 14 })
|
|
266
269
|
]
|
|
267
270
|
}),
|
|
268
271
|
children: /* @__PURE__ */ T("div", {
|
|
269
272
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
270
|
-
children:
|
|
273
|
+
children: z.slice(0, 3).map((e) => /* @__PURE__ */ T(M, {
|
|
271
274
|
org: e,
|
|
272
|
-
onClick: () =>
|
|
275
|
+
onClick: () => H(`/organizations/${e.id}`)
|
|
273
276
|
}, e.id))
|
|
274
277
|
})
|
|
275
278
|
}) : null
|