@burdenoff/microfe-devportal 2026.703.5 → 2026.705.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/pages/AdminReviewPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +370 -291
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +17 -28
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +445 -321
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/types/index.js.map +1 -1
- package/dist/generated/global-operations.js +200 -185
- 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 XCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n Upload,\n Send,\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 useApproveApplicationVersionMutation,\n useRejectApplicationVersionMutation,\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 manifestInputRef = useRef<HTMLInputElement>(null);\n const [manifestFileName, setManifestFileName] = useState<string | null>(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 requiredScopes: \"\",\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 [rejectDialog, setRejectDialog] = useState<{\n versionId: string;\n version: string;\n } | null>(null);\n const [rejectReason, setRejectReason] = useState(\"\");\n\n const handleManifestFileUpload = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n const reader = new FileReader();\n reader.onload = (evt) => {\n const text = evt.target?.result;\n if (typeof text === \"string\") {\n setNewVersion((prev) => ({ ...prev, manifestJson: text }));\n setManifestFileName(file.name);\n }\n };\n reader.readAsText(file);\n // reset so re-uploading same file triggers onChange\n e.target.value = \"\";\n },\n [],\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 const isNodeApp = appData?.application?.appType === \"NODE\";\n const isIntegrationApp = !isWorkflowApp && !isNodeApp;\n\n // App-level readiness checks — version submission is blocked until the app is ready\n const app = appData?.application;\n const pricingDetailsRaw = (app?.pricingDetails ?? {}) as Record<string, unknown>;\n const pricingDone =\n app?.pricingType === \"FREE\" ||\n (app?.pricingType === \"PAID\" &&\n typeof pricingDetailsRaw.price === \"number\" &&\n (pricingDetailsRaw.price as number) > 0);\n const storeDone =\n !!(app?.shortDescription?.trim()) &&\n !!(app?.privacyPolicyUrl?.trim()) &&\n !!(app?.termsOfServiceUrl?.trim());\n const appReadyForSubmission = pricingDone && storeDone;\n const appReadinessBlockers: string[] = [\n ...(!storeDone ? [\"Complete your store listing (short description, privacy policy, terms of service)\"] : []),\n ...(!pricingDone ? [\"Set pricing in the Pricing tab\"] : []),\n ];\n\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 [approveVersion] = useApproveApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [rejectVersion] = useRejectApplicationVersionMutation({\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) =>\n version.status === \"draft\" || version.status === \"rejected\",\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 const requiredScopes =\n isIntegrationApp && newVersion.requiredScopes.trim()\n ? newVersion.requiredScopes\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\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 requiredScopes,\n },\n },\n });\n setShowCreateDialog(false);\n setManifestFileName(null);\n setNewVersion({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n requiredScopes: \"\",\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.versionSubmittedSuccess\",\n \"Version submitted for review — you'll be notified when approved\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionPublishFailed\",\n \"Failed to submit version for review\",\n ),\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 handleApprove = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await approveVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionApprovedSuccess\",\n \"Version approved and published to store\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionApproveFailed\", \"Failed to approve version\"),\n );\n }\n };\n\n const handleRejectConfirm = async () => {\n if (!rejectDialog || !rejectReason.trim()) return;\n setActionError(null);\n setActionSuccess(null);\n try {\n await rejectVersion({\n variables: { id: rejectDialog.versionId, reason: rejectReason },\n });\n setActionSuccess(\n tr(\n \"devportal.versionRejectedSuccess\",\n \"Version rejected — developer has been notified\",\n ),\n );\n setRejectDialog(null);\n setRejectReason(\"\");\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionRejectFailed\", \"Failed to reject version\"),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n if (version.status === \"published\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (version.status === \"pending_review\") {\n return <Send size={16} className=\"text-status-info-text\" />;\n }\n if (version.status === \"rejected\") {\n return <XCircle size={16} className=\"text-status-error-text\" />;\n }\n if (version.status === \"draft\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (version.status === \"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 and paste the workflow definition JSON. No zip is needed — the manifest is the workflow.\",\n )\n : isNodeApp\n ? tr(\n \"devportal.versionManagementPurposeNode\",\n \"Versions are the releases of your node that users install. Upload a new zip whenever the node implementation changes, and update the manifest if the node's inputs, outputs, or operations change. Bump the version for any change users will see.\",\n )\n : tr(\n \"devportal.versionManagementPurposeIntegration\",\n \"Versions track permission and webhook changes for your integration. No zip or manifest is needed — create a new version whenever you add or change required scopes so users can review and accept the updated permissions.\",\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 {/* App setup incomplete — block version submission with a clear warning */}\n {app && !appReadyForSubmission && (\n <div className=\"flex items-start gap-3 rounded-lg border border-status-warning-bg bg-status-warning-bg-subtle px-4 py-3 text-sm text-status-warning-text\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" />\n <div>\n <p className=\"font-medium\">Complete your app setup before submitting for review</p>\n <ul className=\"mt-1 list-disc pl-4 text-xs opacity-80\">\n {appReadinessBlockers.map((b) => (\n <li key={b}>{b}</li>\n ))}\n </ul>\n </div>\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 || !appReadyForSubmission}\n aria-disabled={!canPublish || !appReadyForSubmission}\n title={!appReadyForSubmission ? appReadinessBlockers.join(\" · \") : undefined}\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.submitForReview\", \"Submit for Review\")}\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) => (\n <div className=\"space-y-1\">\n <StatusBadge\n status={\n v.status === \"pending_review\" ? \"In Review\" : v.status\n }\n />\n {v.status === \"pending_review\" && (\n <p className=\"text-xs text-status-info-text\">\n Awaiting platform review\n </p>\n )}\n {v.status === \"rejected\" && v.reviewNotes && (\n <p className=\"text-xs text-status-error-text max-w-xs\">\n {v.reviewNotes}\n </p>\n )}\n </div>\n ),\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-48\",\n render: (v) => (\n <div className=\"flex flex-wrap gap-2\">\n {/* Only show \"Submit for Review\" on a version when the app is already\n published — for a first-time app the whole submission goes through\n the app-level \"Submit for Review\" (store listing + pricing + version). */}\n {(v.status === \"draft\" || v.status === \"rejected\") &&\n app?.status === \"PUBLISHED\" && (\n <button\n className=\"inline-flex items-center gap-1 text-xs text-primary hover:underline disabled:opacity-50 disabled:cursor-not-allowed\"\n disabled={!canPublish || !appReadyForSubmission}\n aria-disabled={!canPublish || !appReadyForSubmission}\n title={!appReadyForSubmission ? appReadinessBlockers.join(\" · \") : undefined}\n onClick={() => handlePublish(v.id)}\n >\n <Send size={11} />\n {tr(\"devportal.submitForReview\", \"Submit for Review\")}\n </button>\n )}\n {v.status === \"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 {isNodeApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Package (.zip){\" \"}\n <span className=\"text-status-error-text\">*</span>\n </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 <p className=\"text-xs text-text-tertiary\">\n The zip is the node executable. Upload it before publishing\n — users install exactly this artifact.\n </p>\n </div>\n )}\n\n {/* Manifest JSON — node apps only */}\n {isNodeApp && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between gap-2 flex-wrap\">\n <label className=\"text-sm font-medium\">\n manifest.json{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <div className=\"flex items-center gap-2\">\n {manifestFileName && (\n <span className=\"text-xs text-status-success-text\">\n ✓ {manifestFileName}\n </span>\n )}\n <input\n ref={manifestInputRef}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"hidden\"\n onChange={handleManifestFileUpload}\n />\n <button\n type=\"button\"\n onClick={() => manifestInputRef.current?.click()}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-2.5 py-1 text-xs font-medium hover:bg-bg-sunken transition-colors\"\n >\n <Upload size={12} />\n Upload manifest.json\n </button>\n </div>\n </div>\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 setManifestFileName(null);\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-tertiary\">\n Upload or paste your <code>manifest.json</code> — update it\n whenever inputs, outputs, or operations change. The platform\n does not extract it from the zip automatically.\n </p>\n </div>\n )}\n\n {/* Required scopes — integration/app types only */}\n {isIntegrationApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Required scopes{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <input\n type=\"text\"\n value={newVersion.requiredScopes}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n requiredScopes: e.target.value,\n })\n }\n placeholder=\"e.g. read:contacts, write:messages\"\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-sm placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-tertiary\">\n Comma-separated. List every permission this version needs —\n users will be prompted to accept any new or changed scopes\n on update.\n </p>\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 {/* Reject version dialog */}\n {rejectDialog && (\n <ConfirmDialog\n open={!!rejectDialog}\n onOpenChange={(open) => {\n if (!open) {\n setRejectDialog(null);\n setRejectReason(\"\");\n }\n }}\n onConfirm={handleRejectConfirm}\n title={`Reject version ${rejectDialog.version}`}\n confirmLabel=\"Reject Version\"\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"danger\"\n >\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n The developer will be notified and can fix and resubmit. Please\n provide a clear reason.\n </p>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Rejection reason{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={3}\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n placeholder=\"e.g. Manifest contains disallowed network calls to external domains...\"\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-sm placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\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.submitSelectedVersions\",\n \"Submit selected versions for review\",\n )}\n description={`Submit ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"} for platform review?`}\n confirmLabel={tr(\"devportal.submitForReview\", \"Submit for Review\")}\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 XCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n Upload,\n Send,\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 useApproveApplicationVersionMutation,\n useRejectApplicationVersionMutation,\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 manifestInputRef = useRef<HTMLInputElement>(null);\n const [manifestFileName, setManifestFileName] = useState<string | null>(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 requiredScopes: \"\",\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 [rejectDialog, setRejectDialog] = useState<{\n versionId: string;\n version: string;\n } | null>(null);\n const [rejectReason, setRejectReason] = useState(\"\");\n\n const handleManifestFileUpload = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n const reader = new FileReader();\n reader.onload = (evt) => {\n const text = evt.target?.result;\n if (typeof text === \"string\") {\n setNewVersion((prev) => ({ ...prev, manifestJson: text }));\n setManifestFileName(file.name);\n }\n };\n reader.readAsText(file);\n // reset so re-uploading same file triggers onChange\n e.target.value = \"\";\n },\n [],\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 const isNodeApp = appData?.application?.appType === \"NODE\";\n const isIntegrationApp = !isWorkflowApp && !isNodeApp;\n\n // App-level readiness checks — version submission is blocked until the app is ready\n const app = appData?.application;\n const pricingDetailsRaw = (app?.pricingDetails ?? {}) as Record<\n string,\n unknown\n >;\n const pricingDone =\n app?.pricingType === \"FREE\" ||\n (app?.pricingType === \"PAID\" &&\n typeof pricingDetailsRaw.price === \"number\" &&\n (pricingDetailsRaw.price as number) > 0);\n const storeDone =\n !!app?.shortDescription?.trim() &&\n !!app?.privacyPolicyUrl?.trim() &&\n !!app?.termsOfServiceUrl?.trim();\n const appReadyForSubmission = pricingDone && storeDone;\n const appReadinessBlockers: string[] = [\n ...(!storeDone\n ? [\n \"Complete your store listing (short description, privacy policy, terms of service)\",\n ]\n : []),\n ...(!pricingDone ? [\"Set pricing in the Pricing tab\"] : []),\n ];\n\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 [approveVersion] = useApproveApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [rejectVersion] = useRejectApplicationVersionMutation({\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) =>\n version.status === \"draft\" || version.status === \"rejected\",\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 const requiredScopes =\n isIntegrationApp && newVersion.requiredScopes.trim()\n ? newVersion.requiredScopes\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n : undefined;\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 requiredScopes,\n },\n },\n });\n setShowCreateDialog(false);\n setManifestFileName(null);\n setNewVersion({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n packageUrl: \"\",\n packageSize: \"\",\n manifestJson: \"\",\n requiredScopes: \"\",\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.versionSubmittedSuccess\",\n \"Version submitted for review — you'll be notified when approved\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionPublishFailed\",\n \"Failed to submit version for review\",\n ),\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 handleApprove = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await approveVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionApprovedSuccess\",\n \"Version approved and published to store\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionApproveFailed\", \"Failed to approve version\"),\n );\n }\n };\n\n const handleRejectConfirm = async () => {\n if (!rejectDialog || !rejectReason.trim()) return;\n setActionError(null);\n setActionSuccess(null);\n try {\n await rejectVersion({\n variables: { id: rejectDialog.versionId, reason: rejectReason },\n });\n setActionSuccess(\n tr(\n \"devportal.versionRejectedSuccess\",\n \"Version rejected — developer has been notified\",\n ),\n );\n setRejectDialog(null);\n setRejectReason(\"\");\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionRejectFailed\", \"Failed to reject version\"),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n if (version.status === \"published\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (version.status === \"pending_review\") {\n return <Send size={16} className=\"text-status-info-text\" />;\n }\n if (version.status === \"rejected\") {\n return <XCircle size={16} className=\"text-status-error-text\" />;\n }\n if (version.status === \"draft\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (version.status === \"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 and paste the workflow definition JSON. No zip is needed — the manifest is the workflow.\",\n )\n : isNodeApp\n ? tr(\n \"devportal.versionManagementPurposeNode\",\n \"Versions are the releases of your node that users install. Upload a new zip whenever the node implementation changes, and update the manifest if the node's inputs, outputs, or operations change. Bump the version for any change users will see.\",\n )\n : tr(\n \"devportal.versionManagementPurposeIntegration\",\n \"Versions track permission and webhook changes for your integration. No zip or manifest is needed — create a new version whenever you add or change required scopes so users can review and accept the updated permissions.\",\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 {/* App setup incomplete — block version submission with a clear warning */}\n {app && !appReadyForSubmission && (\n <div className=\"flex items-start gap-3 rounded-lg border border-status-warning-bg bg-status-warning-bg-subtle px-4 py-3 text-sm text-status-warning-text\">\n <AlertCircle size={16} className=\"mt-0.5 shrink-0\" />\n <div>\n <p className=\"font-medium\">\n Complete your app setup before submitting for review\n </p>\n <ul className=\"mt-1 list-disc pl-4 text-xs opacity-80\">\n {appReadinessBlockers.map((b) => (\n <li key={b}>{b}</li>\n ))}\n </ul>\n </div>\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={\n publishableVersions.length === 0 ||\n !canPublish ||\n !appReadyForSubmission\n }\n aria-disabled={!canPublish || !appReadyForSubmission}\n title={\n !appReadyForSubmission\n ? appReadinessBlockers.join(\" · \")\n : undefined\n }\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.submitForReview\", \"Submit for Review\")}\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) => (\n <div className=\"space-y-1\">\n <StatusBadge\n status={\n v.status === \"pending_review\" ? \"In Review\" : v.status\n }\n />\n {v.status === \"pending_review\" && (\n <p className=\"text-xs text-status-info-text\">\n Awaiting platform review\n </p>\n )}\n {v.status === \"rejected\" && v.reviewNotes && (\n <p className=\"text-xs text-status-error-text max-w-xs\">\n {v.reviewNotes}\n </p>\n )}\n </div>\n ),\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-48\",\n render: (v) => (\n <div className=\"flex flex-wrap gap-2\">\n {/* Only show \"Submit for Review\" on a version when the app is already\n published — for a first-time app the whole submission goes through\n the app-level \"Submit for Review\" (store listing + pricing + version). */}\n {(v.status === \"draft\" || v.status === \"rejected\") &&\n app?.status === \"PUBLISHED\" && (\n <button\n className=\"inline-flex items-center gap-1 text-xs text-primary hover:underline disabled:opacity-50 disabled:cursor-not-allowed\"\n disabled={!canPublish || !appReadyForSubmission}\n aria-disabled={!canPublish || !appReadyForSubmission}\n title={\n !appReadyForSubmission\n ? appReadinessBlockers.join(\" · \")\n : undefined\n }\n onClick={() => handlePublish(v.id)}\n >\n <Send size={11} />\n {tr(\"devportal.submitForReview\", \"Submit for Review\")}\n </button>\n )}\n {v.status === \"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 {isNodeApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Package (.zip){\" \"}\n <span className=\"text-status-error-text\">*</span>\n </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 <p className=\"text-xs text-text-tertiary\">\n The zip is the node executable. Upload it before publishing\n — users install exactly this artifact.\n </p>\n </div>\n )}\n\n {/* Manifest JSON — node apps only */}\n {isNodeApp && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between gap-2 flex-wrap\">\n <label className=\"text-sm font-medium\">\n manifest.json{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <div className=\"flex items-center gap-2\">\n {manifestFileName && (\n <span className=\"text-xs text-status-success-text\">\n ✓ {manifestFileName}\n </span>\n )}\n <input\n ref={manifestInputRef}\n type=\"file\"\n accept=\".json,application/json\"\n className=\"hidden\"\n onChange={handleManifestFileUpload}\n />\n <button\n type=\"button\"\n onClick={() => manifestInputRef.current?.click()}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-2.5 py-1 text-xs font-medium hover:bg-bg-sunken transition-colors\"\n >\n <Upload size={12} />\n Upload manifest.json\n </button>\n </div>\n </div>\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 setManifestFileName(null);\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-tertiary\">\n Upload or paste your <code>manifest.json</code> — update it\n whenever inputs, outputs, or operations change. The platform\n does not extract it from the zip automatically.\n </p>\n </div>\n )}\n\n {/* Required scopes — integration/app types only */}\n {isIntegrationApp && (\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Required scopes{\" \"}\n <span className=\"text-xs font-normal text-text-secondary\">\n (optional)\n </span>\n </label>\n <input\n type=\"text\"\n value={newVersion.requiredScopes}\n onChange={(e) =>\n setNewVersion({\n ...newVersion,\n requiredScopes: e.target.value,\n })\n }\n placeholder=\"e.g. read:contacts, write:messages\"\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-sm placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-tertiary\">\n Comma-separated. List every permission this version needs —\n users will be prompted to accept any new or changed scopes\n on update.\n </p>\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 {/* Reject version dialog */}\n {rejectDialog && (\n <ConfirmDialog\n open={!!rejectDialog}\n onOpenChange={(open) => {\n if (!open) {\n setRejectDialog(null);\n setRejectReason(\"\");\n }\n }}\n onConfirm={handleRejectConfirm}\n title={`Reject version ${rejectDialog.version}`}\n confirmLabel=\"Reject Version\"\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"danger\"\n >\n <div className=\"space-y-3\">\n <p className=\"text-sm text-text-secondary\">\n The developer will be notified and can fix and resubmit. Please\n provide a clear reason.\n </p>\n <div className=\"space-y-1\">\n <label className=\"text-sm font-medium\">\n Rejection reason{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={3}\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n placeholder=\"e.g. Manifest contains disallowed network calls to external domains...\"\n className=\"w-full rounded-md border bg-transparent px-3 py-2 text-sm placeholder:text-text-secondary focus:outline-none focus:ring-1 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\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.submitSelectedVersions\",\n \"Submit selected versions for review\",\n )}\n description={`Submit ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"} for platform review?`}\n confirmLabel={tr(\"devportal.submitForReview\", \"Submit for Review\")}\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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"}
|
|
@@ -566,36 +566,25 @@ var k = [{
|
|
|
566
566
|
})]
|
|
567
567
|
}),
|
|
568
568
|
P === "app" && /* @__PURE__ */ w(o, {
|
|
569
|
-
title: "
|
|
570
|
-
description: "Configure the
|
|
569
|
+
title: "Integration Setup",
|
|
570
|
+
description: "Configure the endpoints for your integration. After a user installs, we redirect them to your callback URL so your server can complete the connection.",
|
|
571
571
|
children: /* @__PURE__ */ T("div", {
|
|
572
572
|
className: "space-y-4",
|
|
573
|
-
children: [
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
hint: "Endpoint to exchange auth code for access token"
|
|
589
|
-
}),
|
|
590
|
-
/* @__PURE__ */ w(s, {
|
|
591
|
-
label: "Revoke URL",
|
|
592
|
-
type: "url",
|
|
593
|
-
value: V.oauthRevokeUrl,
|
|
594
|
-
onChange: (e) => X("oauthRevokeUrl", e.target.value),
|
|
595
|
-
placeholder: "https://yourapp.com/oauth/revoke",
|
|
596
|
-
hint: "Endpoint to revoke tokens when app is uninstalled"
|
|
597
|
-
})
|
|
598
|
-
]
|
|
573
|
+
children: [/* @__PURE__ */ w(s, {
|
|
574
|
+
label: "Post-install Callback URL",
|
|
575
|
+
type: "url",
|
|
576
|
+
value: V.oauthAuthUrl,
|
|
577
|
+
onChange: (e) => X("oauthAuthUrl", e.target.value),
|
|
578
|
+
placeholder: "https://yourapp.com/callback",
|
|
579
|
+
hint: "Users land here after install with ?installation_id=xxx appended"
|
|
580
|
+
}), /* @__PURE__ */ w(s, {
|
|
581
|
+
label: "Revoke URL",
|
|
582
|
+
type: "url",
|
|
583
|
+
value: V.oauthRevokeUrl,
|
|
584
|
+
onChange: (e) => X("oauthRevokeUrl", e.target.value),
|
|
585
|
+
placeholder: "https://yourapp.com/oauth/revoke",
|
|
586
|
+
hint: "We POST to this URL when a user uninstalls your integration"
|
|
587
|
+
})]
|
|
599
588
|
})
|
|
600
589
|
}),
|
|
601
590
|
P !== "app" && /* @__PURE__ */ T("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateApplicationPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApplicationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useRef } from \"react\";\nimport {\n ArrowLeft,\n Box,\n Check,\n ChevronRight,\n GitFork,\n Info,\n Loader2,\n Puzzle,\n Save,\n X,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalEventBus } from \"../events\";\nimport { cn } from \"../utils/cn\";\nimport {\n useCreateApplicationMutation,\n useGetMyOrganizationsQuery,\n} from \"../../generated/global-operations\";\n\n// ─── Builder types ────────────────────────────────────────────────────────────\n\ntype BuilderType = \"app\" | \"node\" | \"workflow\";\n\n// ─── Permissions (App type) ───────────────────────────────────────────────────\n\nconst RBAC_RESOURCES = [\n {\n id: \"auth\",\n label: \"Authentication\",\n description: \"User identity and session management\",\n },\n {\n id: \"billing\",\n label: \"Billing\",\n description: \"Billing accounts and subscriptions\",\n },\n {\n id: \"files\",\n label: \"Files\",\n description: \"File storage and management\",\n },\n {\n id: \"tags\",\n label: \"Tags\",\n description: \"Tags and labels on resources\",\n },\n {\n id: \"calendar\",\n label: \"Calendar\",\n description: \"Events and scheduling\",\n },\n {\n id: \"workflows\",\n label: \"Workflows\",\n description: \"Automation workflows\",\n },\n {\n id: \"channels\",\n label: \"Channels\",\n description: \"Messaging channels\",\n },\n {\n id: \"notifications\",\n label: \"Notifications\",\n description: \"Platform notifications\",\n },\n {\n id: \"users\",\n label: \"Users & Members\",\n description: \"Workspace members and profiles\",\n },\n {\n id: \"workspaces\",\n label: \"Workspaces\",\n description: \"Workspace settings and metadata\",\n },\n {\n id: \"content\",\n label: \"Content\",\n description: \"Pages, documents, and content\",\n },\n {\n id: \"analytics\",\n label: \"Analytics\",\n description: \"Usage analytics and reporting\",\n },\n {\n id: \"webhooks\",\n label: \"Webhooks\",\n description: \"Webhook subscriptions\",\n },\n {\n id: \"rbac\",\n label: \"Roles & Permissions\",\n description: \"Access control configuration\",\n },\n] as const;\n\ntype ResourceId = (typeof RBAC_RESOURCES)[number][\"id\"];\ntype PermissionLevel = \"none\" | \"read\" | \"write\";\n\ntype PermissionMap = Record<ResourceId, PermissionLevel>;\n\nfunction defaultPermissions(): PermissionMap {\n return Object.fromEntries(\n RBAC_RESOURCES.map((r) => [r.id, \"none\"]),\n ) as PermissionMap;\n}\n\nfunction permissionsToManifest(perms: PermissionMap) {\n const entries = Object.entries(perms).filter(([, level]) => level !== \"none\");\n return entries.map(([resource, level]) => ({ resource, level }));\n}\n\n// ─── Org options ──────────────────────────────────────────────────────────────\n\nconst VISIBILITY_OPTIONS = [\n { value: \"PUBLIC\", label: \"Public — synced to the marketplace\" },\n { value: \"PRIVATE\", label: \"Private — only visible to you and your org\" },\n];\n\nconst CATEGORY_OPTIONS = [\n { value: \"ai\", label: \"AI & Automation\" },\n { value: \"integration\", label: \"Action in an App\" },\n { value: \"data\", label: \"Data & Transform\" },\n { value: \"flow\", label: \"Flow & Logic\" },\n { value: \"core\", label: \"Core\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"trigger\", label: \"Triggers\" },\n];\n\n// ─── Sub-components ───────────────────────────────────────────────────────────\n\ninterface TypeCardProps {\n icon: FC<{ size?: number; className?: string }>;\n title: string;\n description: string;\n bullets: string[];\n selected: boolean;\n onClick: () => void;\n}\n\nconst TypeCard: FC<TypeCardProps> = ({\n icon: Icon,\n title,\n description,\n bullets,\n selected,\n onClick,\n}) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"relative flex flex-col gap-3 rounded-xl border-2 p-6 text-left transition-all hover:border-primary/50 hover:shadow-sm\",\n selected\n ? \"border-primary bg-action-primary-bg/5 shadow-sm\"\n : \"border-border-subtle bg-bg-surface\",\n )}\n >\n {selected && (\n <span className=\"absolute top-4 right-4 flex h-5 w-5 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text\">\n <Check size={12} />\n </span>\n )}\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-bg-sunken\">\n <Icon size={22} className=\"text-text-primary\" />\n </div>\n <div>\n <p className=\"font-semibold text-text-primary\">{title}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>\n </div>\n <ul className=\"mt-1 space-y-1\">\n {bullets.map((b) => (\n <li\n key={b}\n className=\"flex items-center gap-2 text-xs text-text-secondary\"\n >\n <ChevronRight size={12} className=\"flex-shrink-0 text-primary\" />\n {b}\n </li>\n ))}\n </ul>\n </button>\n);\n\n// ─── Permissions matrix ───────────────────────────────────────────────────────\n\ninterface PermissionsMatrixProps {\n permissions: PermissionMap;\n onChange: (resource: ResourceId, level: PermissionLevel) => void;\n}\n\nconst PermissionsMatrix: FC<PermissionsMatrixProps> = ({\n permissions,\n onChange,\n}) => {\n const levels: PermissionLevel[] = [\"none\", \"read\", \"write\"];\n const levelLabels: Record<PermissionLevel, string> = {\n none: \"No access\",\n read: \"Read\",\n write: \"Read & Write\",\n };\n\n return (\n <div className=\"overflow-x-auto rounded-lg border border-border-subtle\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary w-1/2\">\n Resource\n </th>\n {levels.map((l) => (\n <th\n key={l}\n className=\"px-4 py-3 text-center font-medium text-text-secondary\"\n >\n {levelLabels[l]}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {RBAC_RESOURCES.map((resource) => (\n <tr\n key={resource.id}\n className=\"hover:bg-bg-sunken/30 transition-colors\"\n >\n <td className=\"px-4 py-3\">\n <p className=\"font-medium text-text-primary\">\n {resource.label}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {resource.description}\n </p>\n </td>\n {levels.map((level) => (\n <td key={level} className=\"px-4 py-3 text-center\">\n <input\n type=\"radio\"\n name={`permission-${resource.id}`}\n value={level}\n checked={permissions[resource.id] === level}\n onChange={() => onChange(resource.id, level)}\n className=\"h-4 w-4 accent-primary cursor-pointer\"\n />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\n// ─── Compatible Products options ──────────────────────────────────────────────\n\nconst COMPATIBLE_PRODUCTS = [\n { value: \"fluidgrids\", label: \"FluidGrids\" },\n { value: \"workspaces\", label: \"Workspaces\" },\n { value: \"bigconsole\", label: \"BigConsole\" },\n { value: \"botlit\", label: \"Botlit\" },\n];\n\n// ─── Main component ───────────────────────────────────────────────────────────\n\ninterface FormData {\n name: string;\n slug: string;\n shortDescription: string;\n description: string;\n category: string;\n visibility: string;\n developerOrganizationId: string;\n websiteUrl: string;\n githubUrl: string;\n documentationUrl: string;\n supportUrl: string;\n oauthAuthUrl: string;\n oauthTokenUrl: string;\n oauthRevokeUrl: string;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n shortDescription?: string;\n category?: string;\n compatibleProducts?: string;\n _submit?: string;\n}\n\nexport const CreateApplicationPage: FC = () => {\n const { navigate, basePath, activeOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const bus = useDevportalEventBus();\n\n const [builderType, setBuilderType] = useState<BuilderType | null>(null);\n const [compatibleProducts, setCompatibleProducts] = useState<string[]>([]);\n const [permissions, setPermissions] =\n useState<PermissionMap>(defaultPermissions());\n // Track whether the user has manually edited the slug so we stop auto-generating\n const slugUserEdited = useRef(false);\n const [formData, setFormData] = useState<FormData>({\n name: \"\",\n slug: \"\",\n shortDescription: \"\",\n description: \"\",\n category: \"\",\n visibility: \"PUBLIC\",\n developerOrganizationId: activeOrgId ?? \"\",\n websiteUrl: \"\",\n githubUrl: \"\",\n documentationUrl: \"\",\n supportUrl: \"\",\n oauthAuthUrl: \"\",\n oauthTokenUrl: \"\",\n oauthRevokeUrl: \"\",\n });\n const [errors, setErrors] = useState<FormErrors>({});\n\n const [createApplication, { loading: isSubmitting }] =\n useCreateApplicationMutation();\n const { data: orgsData } = useGetMyOrganizationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const orgOptions = [\n { value: \"\", label: \"No organization (personal)\" },\n ...(orgsData?.myDeveloperOrganizations ?? []).map(\n (org: { id: string; name: string }) => ({\n value: org.id,\n label: org.name,\n }),\n ),\n ];\n\n const handleNav = (path: string) => navigate?.(`${basePath}${path}`);\n\n const generateSlug = (name: string) =>\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setFormData((prev) => ({\n ...prev,\n name,\n // Only auto-generate slug while the user hasn't manually touched the field\n slug: slugUserEdited.current ? prev.slug : generateSlug(name),\n }));\n if (errors.name) setErrors((p) => ({ ...p, name: undefined }));\n };\n\n const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n slugUserEdited.current = true;\n const value = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, \"\");\n setFormData((prev) => ({ ...prev, slug: value }));\n if (errors.slug) setErrors((p) => ({ ...p, slug: undefined }));\n };\n\n const handleChange = (field: keyof FormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field as keyof FormErrors] || errors._submit)\n setErrors((p) => ({ ...p, [field]: undefined, _submit: undefined }));\n };\n\n const handlePermissionChange = (\n resource: ResourceId,\n level: PermissionLevel,\n ) => {\n setPermissions((prev) => ({ ...prev, [resource]: level }));\n };\n\n const validate = (): boolean => {\n const newErrors: FormErrors = {};\n if (!formData.name.trim()) {\n newErrors.name = \"Name is required\";\n } else if (formData.name.length < 3) {\n newErrors.name = \"Name must be at least 3 characters\";\n } else if (formData.name.length > 100) {\n newErrors.name = \"Name must be less than 100 characters\";\n }\n if (!formData.slug.trim()) {\n newErrors.slug = \"Slug is required\";\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug =\n \"Slug can only contain lowercase letters, numbers, and hyphens\";\n }\n if (!formData.shortDescription.trim()) {\n newErrors.shortDescription = \"Short description is required\";\n } else if (formData.shortDescription.length > 200) {\n newErrors.shortDescription =\n \"Short description must be less than 200 characters\";\n }\n if (!formData.category) {\n newErrors.category = \"Category is required\";\n }\n if (builderType !== \"app\" && compatibleProducts.length === 0) {\n newErrors.compatibleProducts =\n \"Select at least one compatible product so users can find this in the right store\";\n }\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const appTypeForBuilder = () => {\n if (builderType === \"node\") return \"NODE\" as const;\n if (builderType === \"workflow\") return \"WORKFLOW\" as const;\n return \"API_INTEGRATION\" as const;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!validate() || !builderType) return;\n\n const permManifest =\n builderType === \"app\"\n ? { permissions: permissionsToManifest(permissions) }\n : null;\n\n try {\n const result = await createApplication({\n variables: {\n input: {\n name: formData.name,\n slug: formData.slug || undefined,\n shortDescription: formData.shortDescription || undefined,\n description: formData.description || undefined,\n category: formData.category || undefined,\n appType: appTypeForBuilder(),\n visibility: formData.visibility as\n | \"PUBLIC\"\n | \"PRIVATE\"\n | \"UNLISTED\"\n | undefined,\n developerOrganizationId:\n formData.developerOrganizationId || undefined,\n websiteUrl: formData.websiteUrl || undefined,\n githubUrl: formData.githubUrl || undefined,\n documentationUrl: formData.documentationUrl || undefined,\n supportUrl: formData.supportUrl || undefined,\n manifest: permManifest ?? undefined,\n compatibleProducts:\n compatibleProducts.length > 0 ? compatibleProducts : undefined,\n ...(builderType === \"app\" && {\n oauthAuthUrl: formData.oauthAuthUrl || undefined,\n oauthTokenUrl: formData.oauthTokenUrl || undefined,\n oauthRevokeUrl: formData.oauthRevokeUrl || undefined,\n }),\n },\n },\n });\n if (result.data?.createApplication?.id) {\n bus.emit(\"devportal.app.created\", {\n appId: result.data.createApplication.id,\n name: formData.name,\n appType: appTypeForBuilder(),\n organizationId: formData.developerOrganizationId || null,\n });\n handleNav(`/apps/${result.data.createApplication.id}`);\n }\n } catch (error) {\n // Collect all error messages from GraphQL errors and the top-level message\n const messages: string[] = [];\n if (\n error &&\n typeof error === \"object\" &&\n \"graphQLErrors\" in error &&\n Array.isArray((error as { graphQLErrors: unknown[] }).graphQLErrors)\n ) {\n (\n error as { graphQLErrors: Array<{ message: string }> }\n ).graphQLErrors.forEach((e) => messages.push(e.message));\n }\n if (error instanceof Error) messages.push(error.message);\n\n const combined = messages.join(\" \").toLowerCase();\n const isSlugConflict =\n combined.includes(\"slug\") ||\n combined.includes(\"already exist\") ||\n combined.includes(\"unique\") ||\n combined.includes(\"conflict\") ||\n combined.includes(\"duplicate\");\n\n if (isSlugConflict) {\n setErrors((p) => ({\n ...p,\n slug: \"This slug is already taken — choose a different one\",\n }));\n } else {\n setErrors((p) => ({\n ...p,\n _submit: messages[0] ?? \"Something went wrong. Please try again.\",\n }));\n }\n }\n };\n\n // ── Step 1: type picker ─────────────────────────────────────────────────────\n if (!builderType) {\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title=\"What do you want to build?\"\n description=\"Choose a builder type to get started\"\n purpose=\"Pick the kind of integration you are creating — a standard app, a Node package, or a workflow. Your choice sets up the right defaults (credentials, permissions, and where it can be installed) so you only fill in what that type actually needs.\"\n backLink={`${basePath}/apps`}\n backLabel=\"Back to Applications\"\n >\n <div className=\"max-w-3xl space-y-6\">\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <TypeCard\n icon={Box}\n title=\"App\"\n description=\"Build integrations that connect your service to the platform using OAuth tokens.\"\n bullets={[\n \"GitHub-style permissions\",\n \"OAuth token flow\",\n \"Webhook events\",\n \"Publish to app store\",\n ]}\n selected={builderType === \"app\"}\n onClick={() => setBuilderType(\"app\")}\n />\n <TypeCard\n icon={Puzzle}\n title=\"Node\"\n description=\"Build custom FluidGrids nodes that users can drag into their visual workflows.\"\n bullets={[\n \"ZIP bundle + manifest\",\n \"Custom inputs/outputs\",\n \"Versioned releases\",\n \"Publish to node store\",\n ]}\n selected={builderType === \"node\"}\n onClick={() => setBuilderType(\"node\")}\n />\n <TypeCard\n icon={GitFork}\n title=\"Workflow\"\n description=\"Build reusable automation workflows that users can import and customize.\"\n bullets={[\n \"Manifest + workflow JSON\",\n \"Trigger definitions\",\n \"Versioned releases\",\n \"Publish to workflow store\",\n ]}\n selected={builderType === \"workflow\"}\n onClick={() => setBuilderType(\"workflow\")}\n />\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 flex gap-3 text-sm text-text-secondary\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <p>\n Apps use OAuth to interact with platform data. Nodes and\n Workflows are packaged files you upload after creation.\n </p>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n }\n\n // ── Step 2: form ────────────────────────────────────────────────────────────\n const typeLabel =\n builderType === \"app\"\n ? \"App\"\n : builderType === \"node\"\n ? \"Node\"\n : \"Workflow\";\n const typeIcon =\n builderType === \"app\" ? (\n <Box size={16} />\n ) : builderType === \"node\" ? (\n <Puzzle size={16} />\n ) : (\n <GitFork size={16} />\n );\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title={`Create ${typeLabel}`}\n description={\n builderType === \"app\"\n ? \"Register a new app integration with OAuth permissions\"\n : builderType === \"node\"\n ? \"Register a new FluidGrids node package\"\n : \"Register a new reusable automation workflow\"\n }\n purpose=\"Give your integration a name, description, and the permissions it will request. Once created you will get credentials and can configure webhooks, then publish it to the marketplace when it is ready for users.\"\n backLink={`${basePath}/apps`}\n backLabel=\"Back to Applications\"\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {/* Type badge + back to picker */}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => setBuilderType(null)}\n className=\"flex items-center gap-1.5 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft size={14} />\n Change type\n </button>\n <span className=\"flex items-center gap-1.5 rounded-full border border-border-subtle bg-bg-sunken px-3 py-1 text-xs font-medium\">\n {typeIcon}\n {typeLabel}\n </span>\n </div>\n\n {/* Basic info */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details about your application\"\n >\n <div className=\"space-y-4\">\n <InputField\n label=\"Name\"\n value={formData.name}\n onChange={handleNameChange}\n placeholder={\n builderType === \"app\"\n ? \"My Integration\"\n : builderType === \"node\"\n ? \"HTTP Request\"\n : \"Onboarding Flow\"\n }\n required\n error={errors.name}\n />\n <InputField\n label=\"Slug\"\n value={formData.slug}\n onChange={handleSlugChange}\n placeholder=\"my-integration\"\n required\n error={errors.slug}\n hint={\n slugUserEdited.current\n ? \"Only lowercase letters, numbers, and hyphens\"\n : \"Auto-generated from name — edit to customise\"\n }\n />\n <TextareaField\n label=\"Short Description\"\n value={formData.shortDescription}\n onChange={(e) =>\n handleChange(\"shortDescription\", e.target.value)\n }\n placeholder=\"Briefly describe what this does\"\n required\n error={errors.shortDescription}\n hint={`${formData.shortDescription.length}/200 characters`}\n rows={2}\n />\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <SelectField\n label=\"Category\"\n value={formData.category}\n onChange={(e) => handleChange(\"category\", e.target.value)}\n options={CATEGORY_OPTIONS}\n placeholder=\"Select a category…\"\n required\n error={errors.category}\n hint=\"Helps users discover your item in the store\"\n />\n <SelectField\n label=\"Visibility\"\n value={formData.visibility}\n onChange={(e) => handleChange(\"visibility\", e.target.value)}\n options={VISIBILITY_OPTIONS}\n required\n hint=\"Public apps are synced to the marketplace after review\"\n />\n </div>\n\n {/* Compatible Products — NODE and WORKFLOW only */}\n {builderType !== \"app\" && (\n <div className=\"space-y-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Compatible Products\n <span className=\"text-status-error-text ml-1\">*</span>\n </label>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Select which products this {builderType} works with. Users\n will see it in those products' node stores.\n </p>\n </div>\n <div className=\"flex flex-wrap gap-3\">\n {COMPATIBLE_PRODUCTS.map((product) => {\n const checked = compatibleProducts.includes(\n product.value,\n );\n return (\n <label\n key={product.value}\n className={cn(\n \"flex items-center gap-2 cursor-pointer rounded-md border px-3 py-2 text-sm transition-colors\",\n checked\n ? \"border-primary bg-primary/10 text-text-primary\"\n : \"border-border-subtle hover:border-border text-text-secondary hover:text-text-primary\",\n errors.compatibleProducts &&\n !checked &&\n \"border-status-error-bg/50\",\n )}\n >\n <input\n type=\"checkbox\"\n className=\"sr-only\"\n checked={checked}\n onChange={() => {\n setCompatibleProducts((prev) => {\n const next = checked\n ? prev.filter((v) => v !== product.value)\n : [...prev, product.value];\n if (next.length > 0)\n setErrors((p) => ({\n ...p,\n compatibleProducts: undefined,\n }));\n return next;\n });\n }}\n />\n {checked && (\n <Check\n size={14}\n className=\"text-primary flex-shrink-0\"\n />\n )}\n {product.label}\n </label>\n );\n })}\n </div>\n {errors.compatibleProducts && (\n <p className=\"text-xs text-status-error-text\">\n {errors.compatibleProducts}\n </p>\n )}\n </div>\n )}\n\n <TextareaField\n label=\"Full Description\"\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder=\"Detailed description, features, and use cases...\"\n rows={5}\n hint=\"Markdown supported\"\n />\n </div>\n </PageSection>\n\n {/* Developer Organization — only needed if user has multiple orgs */}\n {orgOptions.length > 2 && (\n <PageSection\n title=\"Organization\"\n description=\"Which developer organization owns this?\"\n >\n <SelectField\n label=\"Developer Organization\"\n value={formData.developerOrganizationId}\n onChange={(e) =>\n handleChange(\"developerOrganizationId\", e.target.value)\n }\n options={orgOptions}\n />\n </PageSection>\n )}\n\n {/* Permissions — App type only */}\n {builderType === \"app\" && (\n <PageSection\n title=\"Permissions\"\n description=\"Choose what data your app can access. Users will see these permissions when authorizing your app — like GitHub Apps.\"\n >\n <PermissionsMatrix\n permissions={permissions}\n onChange={handlePermissionChange}\n />\n <div className=\"mt-3 flex gap-2 rounded-lg border border-border-subtle bg-bg-sunken/30 p-3 text-xs text-text-secondary\">\n <Info size={14} className=\"flex-shrink-0 mt-0.5\" />\n <span>\n <strong>Read & Write</strong> implies Read. Select only\n what your app genuinely needs — fewer permissions build more\n user trust.\n </span>\n </div>\n </PageSection>\n )}\n\n {/* OAuth Endpoints — App type only */}\n {builderType === \"app\" && (\n <PageSection\n title=\"OAuth Endpoints\"\n description=\"Configure the OAuth 2.0 endpoints your app uses to authenticate workspaces. Workspaces will be redirected through these URLs during installation.\"\n >\n <div className=\"space-y-4\">\n <InputField\n label=\"Authorization URL\"\n type=\"url\"\n value={formData.oauthAuthUrl}\n onChange={(e) => handleChange(\"oauthAuthUrl\", e.target.value)}\n placeholder=\"https://yourapp.com/oauth/authorize\"\n hint=\"Where users are redirected to grant access\"\n />\n <InputField\n label=\"Token URL\"\n type=\"url\"\n value={formData.oauthTokenUrl}\n onChange={(e) =>\n handleChange(\"oauthTokenUrl\", e.target.value)\n }\n placeholder=\"https://yourapp.com/oauth/token\"\n hint=\"Endpoint to exchange auth code for access token\"\n />\n <InputField\n label=\"Revoke URL\"\n type=\"url\"\n value={formData.oauthRevokeUrl}\n onChange={(e) =>\n handleChange(\"oauthRevokeUrl\", e.target.value)\n }\n placeholder=\"https://yourapp.com/oauth/revoke\"\n hint=\"Endpoint to revoke tokens when app is uninstalled\"\n />\n </div>\n </PageSection>\n )}\n\n {/* Node/Workflow file upload note */}\n {builderType !== \"app\" && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 flex gap-3 text-sm text-text-secondary\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-text-primary\">File uploads</p>\n <p className=\"mt-1\">\n {builderType === \"node\"\n ? \"After creating your Node, go to the Versions tab to upload your ZIP bundle and manifest.json.\"\n : \"After creating your Workflow, go to the Versions tab to upload your manifest and workflow JSON definition.\"}\n </p>\n </div>\n </div>\n )}\n\n {/* General submit error */}\n {errors._submit && (\n <div className=\"flex items-start gap-3 rounded-lg border border-status-error-bg bg-status-error-bg/10 px-4 py-3 text-sm text-status-error-text\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <span>{errors._submit}</span>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={() => handleNav(\"/apps\")}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={16} />\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\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\"\n >\n {isSubmitting ? (\n <>\n <Loader2 size={16} className=\"animate-spin\" />\n Creating…\n </>\n ) : (\n <>\n <Save size={16} />\n Create {typeLabel}\n </>\n )}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateApplicationPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApplicationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useRef } from \"react\";\nimport {\n ArrowLeft,\n Box,\n Check,\n ChevronRight,\n GitFork,\n Info,\n Loader2,\n Puzzle,\n Save,\n X,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalEventBus } from \"../events\";\nimport { cn } from \"../utils/cn\";\nimport {\n useCreateApplicationMutation,\n useGetMyOrganizationsQuery,\n} from \"../../generated/global-operations\";\n\n// ─── Builder types ────────────────────────────────────────────────────────────\n\ntype BuilderType = \"app\" | \"node\" | \"workflow\";\n\n// ─── Permissions (App type) ───────────────────────────────────────────────────\n\nconst RBAC_RESOURCES = [\n {\n id: \"auth\",\n label: \"Authentication\",\n description: \"User identity and session management\",\n },\n {\n id: \"billing\",\n label: \"Billing\",\n description: \"Billing accounts and subscriptions\",\n },\n {\n id: \"files\",\n label: \"Files\",\n description: \"File storage and management\",\n },\n {\n id: \"tags\",\n label: \"Tags\",\n description: \"Tags and labels on resources\",\n },\n {\n id: \"calendar\",\n label: \"Calendar\",\n description: \"Events and scheduling\",\n },\n {\n id: \"workflows\",\n label: \"Workflows\",\n description: \"Automation workflows\",\n },\n {\n id: \"channels\",\n label: \"Channels\",\n description: \"Messaging channels\",\n },\n {\n id: \"notifications\",\n label: \"Notifications\",\n description: \"Platform notifications\",\n },\n {\n id: \"users\",\n label: \"Users & Members\",\n description: \"Workspace members and profiles\",\n },\n {\n id: \"workspaces\",\n label: \"Workspaces\",\n description: \"Workspace settings and metadata\",\n },\n {\n id: \"content\",\n label: \"Content\",\n description: \"Pages, documents, and content\",\n },\n {\n id: \"analytics\",\n label: \"Analytics\",\n description: \"Usage analytics and reporting\",\n },\n {\n id: \"webhooks\",\n label: \"Webhooks\",\n description: \"Webhook subscriptions\",\n },\n {\n id: \"rbac\",\n label: \"Roles & Permissions\",\n description: \"Access control configuration\",\n },\n] as const;\n\ntype ResourceId = (typeof RBAC_RESOURCES)[number][\"id\"];\ntype PermissionLevel = \"none\" | \"read\" | \"write\";\n\ntype PermissionMap = Record<ResourceId, PermissionLevel>;\n\nfunction defaultPermissions(): PermissionMap {\n return Object.fromEntries(\n RBAC_RESOURCES.map((r) => [r.id, \"none\"]),\n ) as PermissionMap;\n}\n\nfunction permissionsToManifest(perms: PermissionMap) {\n const entries = Object.entries(perms).filter(([, level]) => level !== \"none\");\n return entries.map(([resource, level]) => ({ resource, level }));\n}\n\n// ─── Org options ──────────────────────────────────────────────────────────────\n\nconst VISIBILITY_OPTIONS = [\n { value: \"PUBLIC\", label: \"Public — synced to the marketplace\" },\n { value: \"PRIVATE\", label: \"Private — only visible to you and your org\" },\n];\n\nconst CATEGORY_OPTIONS = [\n { value: \"ai\", label: \"AI & Automation\" },\n { value: \"integration\", label: \"Action in an App\" },\n { value: \"data\", label: \"Data & Transform\" },\n { value: \"flow\", label: \"Flow & Logic\" },\n { value: \"core\", label: \"Core\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"trigger\", label: \"Triggers\" },\n];\n\n// ─── Sub-components ───────────────────────────────────────────────────────────\n\ninterface TypeCardProps {\n icon: FC<{ size?: number; className?: string }>;\n title: string;\n description: string;\n bullets: string[];\n selected: boolean;\n onClick: () => void;\n}\n\nconst TypeCard: FC<TypeCardProps> = ({\n icon: Icon,\n title,\n description,\n bullets,\n selected,\n onClick,\n}) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"relative flex flex-col gap-3 rounded-xl border-2 p-6 text-left transition-all hover:border-primary/50 hover:shadow-sm\",\n selected\n ? \"border-primary bg-action-primary-bg/5 shadow-sm\"\n : \"border-border-subtle bg-bg-surface\",\n )}\n >\n {selected && (\n <span className=\"absolute top-4 right-4 flex h-5 w-5 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text\">\n <Check size={12} />\n </span>\n )}\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-bg-sunken\">\n <Icon size={22} className=\"text-text-primary\" />\n </div>\n <div>\n <p className=\"font-semibold text-text-primary\">{title}</p>\n <p className=\"mt-1 text-sm text-text-secondary\">{description}</p>\n </div>\n <ul className=\"mt-1 space-y-1\">\n {bullets.map((b) => (\n <li\n key={b}\n className=\"flex items-center gap-2 text-xs text-text-secondary\"\n >\n <ChevronRight size={12} className=\"flex-shrink-0 text-primary\" />\n {b}\n </li>\n ))}\n </ul>\n </button>\n);\n\n// ─── Permissions matrix ───────────────────────────────────────────────────────\n\ninterface PermissionsMatrixProps {\n permissions: PermissionMap;\n onChange: (resource: ResourceId, level: PermissionLevel) => void;\n}\n\nconst PermissionsMatrix: FC<PermissionsMatrixProps> = ({\n permissions,\n onChange,\n}) => {\n const levels: PermissionLevel[] = [\"none\", \"read\", \"write\"];\n const levelLabels: Record<PermissionLevel, string> = {\n none: \"No access\",\n read: \"Read\",\n write: \"Read & Write\",\n };\n\n return (\n <div className=\"overflow-x-auto rounded-lg border border-border-subtle\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left font-medium text-text-secondary w-1/2\">\n Resource\n </th>\n {levels.map((l) => (\n <th\n key={l}\n className=\"px-4 py-3 text-center font-medium text-text-secondary\"\n >\n {levelLabels[l]}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {RBAC_RESOURCES.map((resource) => (\n <tr\n key={resource.id}\n className=\"hover:bg-bg-sunken/30 transition-colors\"\n >\n <td className=\"px-4 py-3\">\n <p className=\"font-medium text-text-primary\">\n {resource.label}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {resource.description}\n </p>\n </td>\n {levels.map((level) => (\n <td key={level} className=\"px-4 py-3 text-center\">\n <input\n type=\"radio\"\n name={`permission-${resource.id}`}\n value={level}\n checked={permissions[resource.id] === level}\n onChange={() => onChange(resource.id, level)}\n className=\"h-4 w-4 accent-primary cursor-pointer\"\n />\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n};\n\n// ─── Compatible Products options ──────────────────────────────────────────────\n\nconst COMPATIBLE_PRODUCTS = [\n { value: \"fluidgrids\", label: \"FluidGrids\" },\n { value: \"workspaces\", label: \"Workspaces\" },\n { value: \"bigconsole\", label: \"BigConsole\" },\n { value: \"botlit\", label: \"Botlit\" },\n];\n\n// ─── Main component ───────────────────────────────────────────────────────────\n\ninterface FormData {\n name: string;\n slug: string;\n shortDescription: string;\n description: string;\n category: string;\n visibility: string;\n developerOrganizationId: string;\n websiteUrl: string;\n githubUrl: string;\n documentationUrl: string;\n supportUrl: string;\n oauthAuthUrl: string;\n oauthTokenUrl: string;\n oauthRevokeUrl: string;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n shortDescription?: string;\n category?: string;\n compatibleProducts?: string;\n _submit?: string;\n}\n\nexport const CreateApplicationPage: FC = () => {\n const { navigate, basePath, activeOrgId } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n const bus = useDevportalEventBus();\n\n const [builderType, setBuilderType] = useState<BuilderType | null>(null);\n const [compatibleProducts, setCompatibleProducts] = useState<string[]>([]);\n const [permissions, setPermissions] =\n useState<PermissionMap>(defaultPermissions());\n // Track whether the user has manually edited the slug so we stop auto-generating\n const slugUserEdited = useRef(false);\n const [formData, setFormData] = useState<FormData>({\n name: \"\",\n slug: \"\",\n shortDescription: \"\",\n description: \"\",\n category: \"\",\n visibility: \"PUBLIC\",\n developerOrganizationId: activeOrgId ?? \"\",\n websiteUrl: \"\",\n githubUrl: \"\",\n documentationUrl: \"\",\n supportUrl: \"\",\n oauthAuthUrl: \"\",\n oauthTokenUrl: \"\",\n oauthRevokeUrl: \"\",\n });\n const [errors, setErrors] = useState<FormErrors>({});\n\n const [createApplication, { loading: isSubmitting }] =\n useCreateApplicationMutation();\n const { data: orgsData } = useGetMyOrganizationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const orgOptions = [\n { value: \"\", label: \"No organization (personal)\" },\n ...(orgsData?.myDeveloperOrganizations ?? []).map(\n (org: { id: string; name: string }) => ({\n value: org.id,\n label: org.name,\n }),\n ),\n ];\n\n const handleNav = (path: string) => navigate?.(`${basePath}${path}`);\n\n const generateSlug = (name: string) =>\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const name = e.target.value;\n setFormData((prev) => ({\n ...prev,\n name,\n // Only auto-generate slug while the user hasn't manually touched the field\n slug: slugUserEdited.current ? prev.slug : generateSlug(name),\n }));\n if (errors.name) setErrors((p) => ({ ...p, name: undefined }));\n };\n\n const handleSlugChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n slugUserEdited.current = true;\n const value = e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, \"\");\n setFormData((prev) => ({ ...prev, slug: value }));\n if (errors.slug) setErrors((p) => ({ ...p, slug: undefined }));\n };\n\n const handleChange = (field: keyof FormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field as keyof FormErrors] || errors._submit)\n setErrors((p) => ({ ...p, [field]: undefined, _submit: undefined }));\n };\n\n const handlePermissionChange = (\n resource: ResourceId,\n level: PermissionLevel,\n ) => {\n setPermissions((prev) => ({ ...prev, [resource]: level }));\n };\n\n const validate = (): boolean => {\n const newErrors: FormErrors = {};\n if (!formData.name.trim()) {\n newErrors.name = \"Name is required\";\n } else if (formData.name.length < 3) {\n newErrors.name = \"Name must be at least 3 characters\";\n } else if (formData.name.length > 100) {\n newErrors.name = \"Name must be less than 100 characters\";\n }\n if (!formData.slug.trim()) {\n newErrors.slug = \"Slug is required\";\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug =\n \"Slug can only contain lowercase letters, numbers, and hyphens\";\n }\n if (!formData.shortDescription.trim()) {\n newErrors.shortDescription = \"Short description is required\";\n } else if (formData.shortDescription.length > 200) {\n newErrors.shortDescription =\n \"Short description must be less than 200 characters\";\n }\n if (!formData.category) {\n newErrors.category = \"Category is required\";\n }\n if (builderType !== \"app\" && compatibleProducts.length === 0) {\n newErrors.compatibleProducts =\n \"Select at least one compatible product so users can find this in the right store\";\n }\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const appTypeForBuilder = () => {\n if (builderType === \"node\") return \"NODE\" as const;\n if (builderType === \"workflow\") return \"WORKFLOW\" as const;\n return \"API_INTEGRATION\" as const;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!validate() || !builderType) return;\n\n const permManifest =\n builderType === \"app\"\n ? { permissions: permissionsToManifest(permissions) }\n : null;\n\n try {\n const result = await createApplication({\n variables: {\n input: {\n name: formData.name,\n slug: formData.slug || undefined,\n shortDescription: formData.shortDescription || undefined,\n description: formData.description || undefined,\n category: formData.category || undefined,\n appType: appTypeForBuilder(),\n visibility: formData.visibility as\n | \"PUBLIC\"\n | \"PRIVATE\"\n | \"UNLISTED\"\n | undefined,\n developerOrganizationId:\n formData.developerOrganizationId || undefined,\n websiteUrl: formData.websiteUrl || undefined,\n githubUrl: formData.githubUrl || undefined,\n documentationUrl: formData.documentationUrl || undefined,\n supportUrl: formData.supportUrl || undefined,\n manifest: permManifest ?? undefined,\n compatibleProducts:\n compatibleProducts.length > 0 ? compatibleProducts : undefined,\n ...(builderType === \"app\" && {\n oauthAuthUrl: formData.oauthAuthUrl || undefined,\n oauthTokenUrl: formData.oauthTokenUrl || undefined,\n oauthRevokeUrl: formData.oauthRevokeUrl || undefined,\n }),\n },\n },\n });\n if (result.data?.createApplication?.id) {\n bus.emit(\"devportal.app.created\", {\n appId: result.data.createApplication.id,\n name: formData.name,\n appType: appTypeForBuilder(),\n organizationId: formData.developerOrganizationId || null,\n });\n handleNav(`/apps/${result.data.createApplication.id}`);\n }\n } catch (error) {\n // Collect all error messages from GraphQL errors and the top-level message\n const messages: string[] = [];\n if (\n error &&\n typeof error === \"object\" &&\n \"graphQLErrors\" in error &&\n Array.isArray((error as { graphQLErrors: unknown[] }).graphQLErrors)\n ) {\n (\n error as { graphQLErrors: Array<{ message: string }> }\n ).graphQLErrors.forEach((e) => messages.push(e.message));\n }\n if (error instanceof Error) messages.push(error.message);\n\n const combined = messages.join(\" \").toLowerCase();\n const isSlugConflict =\n combined.includes(\"slug\") ||\n combined.includes(\"already exist\") ||\n combined.includes(\"unique\") ||\n combined.includes(\"conflict\") ||\n combined.includes(\"duplicate\");\n\n if (isSlugConflict) {\n setErrors((p) => ({\n ...p,\n slug: \"This slug is already taken — choose a different one\",\n }));\n } else {\n setErrors((p) => ({\n ...p,\n _submit: messages[0] ?? \"Something went wrong. Please try again.\",\n }));\n }\n }\n };\n\n // ── Step 1: type picker ─────────────────────────────────────────────────────\n if (!builderType) {\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title=\"What do you want to build?\"\n description=\"Choose a builder type to get started\"\n purpose=\"Pick the kind of integration you are creating — a standard app, a Node package, or a workflow. Your choice sets up the right defaults (credentials, permissions, and where it can be installed) so you only fill in what that type actually needs.\"\n backLink={`${basePath}/apps`}\n backLabel=\"Back to Applications\"\n >\n <div className=\"max-w-3xl space-y-6\">\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <TypeCard\n icon={Box}\n title=\"App\"\n description=\"Build integrations that connect your service to the platform using OAuth tokens.\"\n bullets={[\n \"GitHub-style permissions\",\n \"OAuth token flow\",\n \"Webhook events\",\n \"Publish to app store\",\n ]}\n selected={builderType === \"app\"}\n onClick={() => setBuilderType(\"app\")}\n />\n <TypeCard\n icon={Puzzle}\n title=\"Node\"\n description=\"Build custom FluidGrids nodes that users can drag into their visual workflows.\"\n bullets={[\n \"ZIP bundle + manifest\",\n \"Custom inputs/outputs\",\n \"Versioned releases\",\n \"Publish to node store\",\n ]}\n selected={builderType === \"node\"}\n onClick={() => setBuilderType(\"node\")}\n />\n <TypeCard\n icon={GitFork}\n title=\"Workflow\"\n description=\"Build reusable automation workflows that users can import and customize.\"\n bullets={[\n \"Manifest + workflow JSON\",\n \"Trigger definitions\",\n \"Versioned releases\",\n \"Publish to workflow store\",\n ]}\n selected={builderType === \"workflow\"}\n onClick={() => setBuilderType(\"workflow\")}\n />\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 flex gap-3 text-sm text-text-secondary\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <p>\n Apps use OAuth to interact with platform data. Nodes and\n Workflows are packaged files you upload after creation.\n </p>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n }\n\n // ── Step 2: form ────────────────────────────────────────────────────────────\n const typeLabel =\n builderType === \"app\"\n ? \"App\"\n : builderType === \"node\"\n ? \"Node\"\n : \"Workflow\";\n const typeIcon =\n builderType === \"app\" ? (\n <Box size={16} />\n ) : builderType === \"node\" ? (\n <Puzzle size={16} />\n ) : (\n <GitFork size={16} />\n );\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title={`Create ${typeLabel}`}\n description={\n builderType === \"app\"\n ? \"Register a new app integration with OAuth permissions\"\n : builderType === \"node\"\n ? \"Register a new FluidGrids node package\"\n : \"Register a new reusable automation workflow\"\n }\n purpose=\"Give your integration a name, description, and the permissions it will request. Once created you will get credentials and can configure webhooks, then publish it to the marketplace when it is ready for users.\"\n backLink={`${basePath}/apps`}\n backLabel=\"Back to Applications\"\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {/* Type badge + back to picker */}\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => setBuilderType(null)}\n className=\"flex items-center gap-1.5 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft size={14} />\n Change type\n </button>\n <span className=\"flex items-center gap-1.5 rounded-full border border-border-subtle bg-bg-sunken px-3 py-1 text-xs font-medium\">\n {typeIcon}\n {typeLabel}\n </span>\n </div>\n\n {/* Basic info */}\n <PageSection\n title=\"Basic Information\"\n description=\"Core details about your application\"\n >\n <div className=\"space-y-4\">\n <InputField\n label=\"Name\"\n value={formData.name}\n onChange={handleNameChange}\n placeholder={\n builderType === \"app\"\n ? \"My Integration\"\n : builderType === \"node\"\n ? \"HTTP Request\"\n : \"Onboarding Flow\"\n }\n required\n error={errors.name}\n />\n <InputField\n label=\"Slug\"\n value={formData.slug}\n onChange={handleSlugChange}\n placeholder=\"my-integration\"\n required\n error={errors.slug}\n hint={\n slugUserEdited.current\n ? \"Only lowercase letters, numbers, and hyphens\"\n : \"Auto-generated from name — edit to customise\"\n }\n />\n <TextareaField\n label=\"Short Description\"\n value={formData.shortDescription}\n onChange={(e) =>\n handleChange(\"shortDescription\", e.target.value)\n }\n placeholder=\"Briefly describe what this does\"\n required\n error={errors.shortDescription}\n hint={`${formData.shortDescription.length}/200 characters`}\n rows={2}\n />\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <SelectField\n label=\"Category\"\n value={formData.category}\n onChange={(e) => handleChange(\"category\", e.target.value)}\n options={CATEGORY_OPTIONS}\n placeholder=\"Select a category…\"\n required\n error={errors.category}\n hint=\"Helps users discover your item in the store\"\n />\n <SelectField\n label=\"Visibility\"\n value={formData.visibility}\n onChange={(e) => handleChange(\"visibility\", e.target.value)}\n options={VISIBILITY_OPTIONS}\n required\n hint=\"Public apps are synced to the marketplace after review\"\n />\n </div>\n\n {/* Compatible Products — NODE and WORKFLOW only */}\n {builderType !== \"app\" && (\n <div className=\"space-y-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Compatible Products\n <span className=\"text-status-error-text ml-1\">*</span>\n </label>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n Select which products this {builderType} works with. Users\n will see it in those products' node stores.\n </p>\n </div>\n <div className=\"flex flex-wrap gap-3\">\n {COMPATIBLE_PRODUCTS.map((product) => {\n const checked = compatibleProducts.includes(\n product.value,\n );\n return (\n <label\n key={product.value}\n className={cn(\n \"flex items-center gap-2 cursor-pointer rounded-md border px-3 py-2 text-sm transition-colors\",\n checked\n ? \"border-primary bg-primary/10 text-text-primary\"\n : \"border-border-subtle hover:border-border text-text-secondary hover:text-text-primary\",\n errors.compatibleProducts &&\n !checked &&\n \"border-status-error-bg/50\",\n )}\n >\n <input\n type=\"checkbox\"\n className=\"sr-only\"\n checked={checked}\n onChange={() => {\n setCompatibleProducts((prev) => {\n const next = checked\n ? prev.filter((v) => v !== product.value)\n : [...prev, product.value];\n if (next.length > 0)\n setErrors((p) => ({\n ...p,\n compatibleProducts: undefined,\n }));\n return next;\n });\n }}\n />\n {checked && (\n <Check\n size={14}\n className=\"text-primary flex-shrink-0\"\n />\n )}\n {product.label}\n </label>\n );\n })}\n </div>\n {errors.compatibleProducts && (\n <p className=\"text-xs text-status-error-text\">\n {errors.compatibleProducts}\n </p>\n )}\n </div>\n )}\n\n <TextareaField\n label=\"Full Description\"\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder=\"Detailed description, features, and use cases...\"\n rows={5}\n hint=\"Markdown supported\"\n />\n </div>\n </PageSection>\n\n {/* Developer Organization — only needed if user has multiple orgs */}\n {orgOptions.length > 2 && (\n <PageSection\n title=\"Organization\"\n description=\"Which developer organization owns this?\"\n >\n <SelectField\n label=\"Developer Organization\"\n value={formData.developerOrganizationId}\n onChange={(e) =>\n handleChange(\"developerOrganizationId\", e.target.value)\n }\n options={orgOptions}\n />\n </PageSection>\n )}\n\n {/* Permissions — App type only */}\n {builderType === \"app\" && (\n <PageSection\n title=\"Permissions\"\n description=\"Choose what data your app can access. Users will see these permissions when authorizing your app — like GitHub Apps.\"\n >\n <PermissionsMatrix\n permissions={permissions}\n onChange={handlePermissionChange}\n />\n <div className=\"mt-3 flex gap-2 rounded-lg border border-border-subtle bg-bg-sunken/30 p-3 text-xs text-text-secondary\">\n <Info size={14} className=\"flex-shrink-0 mt-0.5\" />\n <span>\n <strong>Read & Write</strong> implies Read. Select only\n what your app genuinely needs — fewer permissions build more\n user trust.\n </span>\n </div>\n </PageSection>\n )}\n\n {/* Integration Setup — App type only */}\n {builderType === \"app\" && (\n <PageSection\n title=\"Integration Setup\"\n description=\"Configure the endpoints for your integration. After a user installs, we redirect them to your callback URL so your server can complete the connection.\"\n >\n <div className=\"space-y-4\">\n <InputField\n label=\"Post-install Callback URL\"\n type=\"url\"\n value={formData.oauthAuthUrl}\n onChange={(e) => handleChange(\"oauthAuthUrl\", e.target.value)}\n placeholder=\"https://yourapp.com/callback\"\n hint=\"Users land here after install with ?installation_id=xxx appended\"\n />\n <InputField\n label=\"Revoke URL\"\n type=\"url\"\n value={formData.oauthRevokeUrl}\n onChange={(e) =>\n handleChange(\"oauthRevokeUrl\", e.target.value)\n }\n placeholder=\"https://yourapp.com/oauth/revoke\"\n hint=\"We POST to this URL when a user uninstalls your integration\"\n />\n </div>\n </PageSection>\n )}\n\n {/* Node/Workflow file upload note */}\n {builderType !== \"app\" && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4 flex gap-3 text-sm text-text-secondary\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <div>\n <p className=\"font-medium text-text-primary\">File uploads</p>\n <p className=\"mt-1\">\n {builderType === \"node\"\n ? \"After creating your Node, go to the Versions tab to upload your ZIP bundle and manifest.json.\"\n : \"After creating your Workflow, go to the Versions tab to upload your manifest and workflow JSON definition.\"}\n </p>\n </div>\n </div>\n )}\n\n {/* General submit error */}\n {errors._submit && (\n <div className=\"flex items-start gap-3 rounded-lg border border-status-error-bg bg-status-error-bg/10 px-4 py-3 text-sm text-status-error-text\">\n <Info size={16} className=\"flex-shrink-0 mt-0.5\" />\n <span>{errors._submit}</span>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={() => handleNav(\"/apps\")}\n disabled={isSubmitting}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n <X size={16} />\n {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting}\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\"\n >\n {isSubmitting ? (\n <>\n <Loader2 size={16} className=\"animate-spin\" />\n Creating…\n </>\n ) : (\n <>\n <Save size={16} />\n Create {typeLabel}\n </>\n )}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|