@burdenoff/microfe-devportal 2026.611.2 → 2026.614.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/components/SectionNav.js +105 -41
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +36 -36
- package/dist/devportal/pages/AnalyticsDashboardPage.js +150 -150
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +184 -172
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +9 -9
- package/dist/devportal/pages/ApplicationDetailPage.js +414 -414
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +44 -44
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +118 -118
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +20 -20
- package/dist/devportal/pages/CreateApplicationPage.js +32 -32
- package/dist/devportal/pages/CreateOAuthClientPage.js +20 -20
- package/dist/devportal/pages/CreateOrganizationPage.js +125 -442
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +25 -25
- package/dist/devportal/pages/DashboardPage.js +160 -145
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +22 -22
- package/dist/devportal/pages/EditApplicationPage.js +283 -283
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +9 -9
- package/dist/devportal/pages/OAuthClientDetailPage.js +17 -17
- package/dist/devportal/pages/OAuthClientsPage.js +16 -16
- package/dist/devportal/pages/OrganizationDetailPage.js +78 -78
- package/dist/devportal/pages/OrganizationMembersPage.js +260 -260
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +18 -18
- package/dist/devportal/pages/PublishPage.js +16 -16
- package/dist/devportal/pages/PublisherProfilePage.js +156 -146
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +512 -362
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +17 -17
- package/dist/devportal/pages/SetupPage.js +99 -99
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +143 -143
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +140 -140
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.js +46 -33
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/generated/global-operations.js +69 -55
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditApplicationPage.js","names":[],"sources":["../../../src/devportal/pages/EditApplicationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Save,\n Trash2,\n AlertTriangle,\n Archive,\n Send,\n AlertCircle,\n AppWindow,\n Power,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n TagInput,\n CheckboxField,\n} from \"../components/FormField\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { LoadingState } from \"../components/LoadingState\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport type { Application, AppType, AppVisibility, AppStatus } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport {\n useGetApplicationQuery,\n useUpdateApplicationMutation,\n useDeleteApplicationMutation,\n useSubmitApplicationMutation,\n useDeactivateAppMutation,\n} from \"../../generated/global-operations\";\n\ninterface EditApplicationFormData {\n name: string;\n slug: string;\n shortDescription: string;\n description: string;\n appType: AppType;\n visibility: AppVisibility;\n category: string;\n tags: string[];\n keywords: string[];\n websiteUrl: string;\n documentationUrl: string;\n supportUrl: string;\n privacyPolicyUrl: string;\n termsOfServiceUrl: string;\n githubUrl: string;\n requiresApproval: boolean;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n shortDescription?: string;\n appType?: string;\n category?: string;\n}\n\nconst APP_TYPE_OPTIONS = Object.entries(APP_TYPE_LABELS).map(\n ([value, label]) => ({\n value,\n label,\n }),\n);\n\nconst VISIBILITY_OPTIONS = [\n { value: \"PUBLIC\", label: \"Public - Visible to everyone\" },\n { value: \"PRIVATE\", label: \"Private - Only visible to you\" },\n { value: \"UNLISTED\", label: \"Unlisted - Accessible via direct link\" },\n { value: \"INTERNAL\", label: \"Internal - Only visible within organization\" },\n];\n\nconst CATEGORY_OPTIONS = [\n { value: \"productivity\", label: \"Productivity\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"automation\", label: \"Automation\" },\n { value: \"developer-tools\", label: \"Developer Tools\" },\n { value: \"finance\", label: \"Finance\" },\n { value: \"hr\", label: \"HR & People\" },\n { value: \"marketing\", label: \"Marketing\" },\n { value: \"project-management\", label: \"Project Management\" },\n { value: \"security\", label: \"Security\" },\n { value: \"support\", label: \"Support\" },\n { value: \"other\", label: \"Other\" },\n];\n\n/**\n * Helper to safely map GraphQL response to Application type\n */\nfunction mapToApplication(\n data: NonNullable<\n ReturnType<typeof useGetApplicationQuery>[\"data\"]\n >[\"application\"],\n): Application | null {\n if (!data) return null;\n return {\n id: data.id,\n name: data.name,\n slug: data.slug,\n shortDescription: data.shortDescription || undefined,\n description: data.description || undefined,\n appType: data.appType,\n status: data.status,\n visibility: data.visibility,\n tenantId: data.tenantId,\n developerId: data.developerId,\n developerOrganizationId: data.developerOrganizationId || undefined,\n category: data.category || undefined,\n tags: data.tags || [],\n keywords: data.keywords || [],\n iconUrl: data.iconUrl || undefined,\n websiteUrl: data.websiteUrl || undefined,\n documentationUrl: data.documentationUrl || undefined,\n supportUrl: data.supportUrl || undefined,\n privacyPolicyUrl: data.privacyPolicyUrl || undefined,\n termsOfServiceUrl: data.termsOfServiceUrl || undefined,\n githubUrl: data.githubUrl || undefined,\n pricingType: data.pricingType || undefined,\n installCount: data.installCount,\n activeInstallCount: data.activeInstallCount,\n averageRating: data.averageRating ?? undefined,\n reviewCount: data.reviewCount,\n submittedAt: data.submittedAt || undefined,\n publishedAt: data.publishedAt || undefined,\n lastReviewedAt: data.lastReviewedAt || undefined,\n reviewStatus: data.reviewStatus,\n reviewNotes: data.reviewNotes || undefined,\n reviewedBy: data.reviewedBy || undefined,\n isFeatured: data.isFeatured,\n isVerified: data.isVerified,\n requiresApproval: data.requiresApproval,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n };\n}\n\n/**\n * Edit application page — wired to real GraphQL mutations.\n */\nexport const EditApplicationPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { appId } = useParams<{ appId: string }>();\n const { t } = useI18n();\n const { allowed: canUpdate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_UPDATE,\n );\n const { allowed: canDelete } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_DELETE,\n );\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errors, setErrors] = useState<FormErrors>({});\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showDeprecateDialog, setShowDeprecateDialog] = useState(false);\n const [showDeactivateDialog, setShowDeactivateDialog] = useState(false);\n const [hasChanges, setHasChanges] = useState(false);\n\n // ---------------------------------------------------------------------------\n // GraphQL queries & mutations\n // ---------------------------------------------------------------------------\n const {\n data,\n loading,\n error: queryError,\n } = useGetApplicationQuery({\n variables: { id: appId! },\n skip: !appId,\n });\n\n const [updateApplication] = useUpdateApplicationMutation();\n const [deleteApplication] = useDeleteApplicationMutation();\n const [submitApplication] = useSubmitApplicationMutation();\n const [deactivateApp] = useDeactivateAppMutation();\n\n // ---------------------------------------------------------------------------\n // Derive application from query data\n // ---------------------------------------------------------------------------\n const app = mapToApplication(data?.application);\n\n useEffect(() => {\n const name = app?.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, app?.name]);\n\n // ---------------------------------------------------------------------------\n // Form state — initialised from fetched app once available\n // ---------------------------------------------------------------------------\n const [formData, setFormData] = useState<EditApplicationFormData>({\n name: \"\",\n slug: \"\",\n shortDescription: \"\",\n description: \"\",\n appType: \"WEB_APP\",\n visibility: \"PUBLIC\",\n category: \"\",\n tags: [],\n keywords: [],\n websiteUrl: \"\",\n documentationUrl: \"\",\n supportUrl: \"\",\n privacyPolicyUrl: \"\",\n termsOfServiceUrl: \"\",\n githubUrl: \"\",\n requiresApproval: false,\n });\n\n useEffect(() => {\n if (app) {\n setFormData({\n name: app.name,\n slug: app.slug,\n shortDescription: app.shortDescription || \"\",\n description: app.description || \"\",\n appType: app.appType,\n visibility: app.visibility,\n category: app.category || \"\",\n tags: app.tags,\n keywords: app.keywords,\n websiteUrl: app.websiteUrl || \"\",\n documentationUrl: app.documentationUrl || \"\",\n supportUrl: app.supportUrl || \"\",\n privacyPolicyUrl: app.privacyPolicyUrl || \"\",\n termsOfServiceUrl: app.termsOfServiceUrl || \"\",\n githubUrl: app.githubUrl || \"\",\n requiresApproval: app.requiresApproval,\n });\n setHasChanges(false);\n }\n // Re-sync form when the underlying application data changes (e.g. after\n // a refetch triggered by a mutation). We intentionally depend on\n // `data?.application` rather than the derived `app` object so that the\n // effect fires when the query cache updates.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data?.application]);\n\n // ---------------------------------------------------------------------------\n // Handlers\n // ---------------------------------------------------------------------------\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleChange = (\n field: keyof EditApplicationFormData,\n value: string | string[] | boolean,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n setHasChanges(true);\n if (errors[field as keyof FormErrors]) {\n setErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: FormErrors = {};\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.appNameRequired\",\n \"Application name is required\",\n );\n } else if (formData.name.length < 3) {\n newErrors.name = tr(\n \"devportal.appNameMinLength\",\n \"Name must be at least 3 characters\",\n );\n } else if (formData.name.length > 100) {\n newErrors.name = tr(\n \"devportal.appNameMaxLength\",\n \"Name must be less than 100 characters\",\n );\n }\n\n if (!formData.slug.trim()) {\n newErrors.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug = tr(\n \"devportal.slugInvalidChars\",\n \"Slug can only contain lowercase letters, numbers, and hyphens\",\n );\n }\n\n if (!formData.shortDescription.trim()) {\n newErrors.shortDescription = tr(\n \"devportal.shortDescriptionRequired\",\n \"Short description is required\",\n );\n } else if (formData.shortDescription.length > 200) {\n newErrors.shortDescription = tr(\n \"devportal.shortDescriptionMaxLength\",\n \"Short description must be less than 200 characters\",\n );\n }\n\n if (!formData.appType) {\n newErrors.appType = tr(\n \"devportal.appTypeRequired\",\n \"Application type is required\",\n );\n }\n\n if (!formData.category) {\n newErrors.category = tr(\n \"devportal.categoryRequired\",\n \"Category is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm() || !app) {\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n await updateApplication({\n variables: {\n id: app.id,\n input: {\n name: formData.name,\n shortDescription: formData.shortDescription,\n description: formData.description,\n appType: formData.appType,\n visibility: formData.visibility,\n category: formData.category,\n tags: formData.tags,\n keywords: formData.keywords,\n websiteUrl: formData.websiteUrl || null,\n documentationUrl: formData.documentationUrl || null,\n supportUrl: formData.supportUrl || null,\n privacyPolicyUrl: formData.privacyPolicyUrl || null,\n termsOfServiceUrl: formData.termsOfServiceUrl || null,\n githubUrl: formData.githubUrl || null,\n },\n },\n });\n setHasChanges(false);\n } catch (err) {\n console.error(\"Failed to update application:\", err);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleDelete = async () => {\n if (!app) return;\n void nativeImpact(\"medium\");\n try {\n await deleteApplication({\n variables: { id: app.id },\n });\n void nativeNotify(\"success\");\n handleNavigate(\"/apps\");\n } catch (err) {\n void nativeNotify(\"error\");\n console.error(\"Failed to delete application:\", err);\n }\n };\n\n const handleDeprecate = async () => {\n if (!app) return;\n try {\n // Deprecate is modelled as a status transition via update. The backend\n // treats a visibility change to PRIVATE + status mutation or a dedicated\n // deprecation endpoint. Here we use submitApplication with a status note\n // as the deprecate action since there is no separate `deprecateApplication`\n // mutation. If a dedicated mutation is added later, swap it in here.\n await updateApplication({\n variables: {\n id: app.id,\n input: {\n visibility: \"PRIVATE\",\n },\n },\n });\n setShowDeprecateDialog(false);\n } catch (err) {\n console.error(\"Failed to deprecate application:\", err);\n }\n };\n\n const handleDeactivate = async () => {\n if (!app) return;\n try {\n await deactivateApp({\n variables: { id: app.id },\n });\n setShowDeactivateDialog(false);\n handleNavigate(\"/apps\");\n } catch (err) {\n console.error(\"Failed to deactivate application:\", err);\n }\n };\n\n const handleSubmitForReview = async () => {\n if (!app) return;\n try {\n setIsSubmitting(true);\n await submitApplication({\n variables: { id: app.id },\n });\n } catch (err) {\n console.error(\"Failed to submit for review:\", err);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n // ---------------------------------------------------------------------------\n // Loading / Error / Not-found states\n // ---------------------------------------------------------------------------\n if (loading) {\n return (\n <LoadingState\n message={tr(\"devportal.loadingApplication\", \"Loading application...\")}\n />\n );\n }\n\n if (queryError) {\n return (\n <PageLayout\n title={tr(\"devportal.error\", \"Error\")}\n backLink={`${basePath}/apps`}\n backLabel={tr(\"devportal.backToApplications\", \"Back to Applications\")}\n >\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle size={48} className=\"text-destructive mb-4\" />\n <h3 className=\"text-lg font-semibold mb-2\">\n {tr(\n \"devportal.failedToLoadApplication\",\n \"Failed to load application\",\n )}\n </h3>\n <p className=\"text-sm text-muted-foreground\">{queryError.message}</p>\n </div>\n </PageLayout>\n );\n }\n\n if (!app) {\n return (\n <PageLayout\n title={tr(\"devportal.notFound\", \"Not Found\")}\n backLink={`${basePath}/apps`}\n backLabel={tr(\"devportal.backToApplications\", \"Back to Applications\")}\n >\n <EmptyState\n icon={<AppWindow size={48} />}\n title={tr(\"devportal.applicationNotFound\", \"Application not found\")}\n description={tr(\n \"devportal.applicationNotFoundDescription\",\n \"The application you're looking for doesn't exist or you don't have access to it.\",\n )}\n />\n </PageLayout>\n );\n }\n\n // ---------------------------------------------------------------------------\n // Derived flags\n // ---------------------------------------------------------------------------\n const canSubmitForReview =\n app.status === \"DRAFT\" || app.status === \"REJECTED\";\n const canDeprecate = app.status === \"PUBLISHED\";\n const canDeactivate = app.status === \"ACTIVE\" || app.status === \"PUBLISHED\";\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_UPDATE}>\n <PageLayout\n title={`${tr(\"devportal.editAppTitlePrefix\", \"Edit:\")} ${app.name}`}\n description={tr(\n \"devportal.editAppDescription\",\n \"Update your application settings\",\n )}\n backLink={`${basePath}/apps/${app.id}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={app.status} type=\"app\" />\n </div>\n }\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {/* Status banner for published apps */}\n {app.status === \"PUBLISHED\" && (\n <div className=\"rounded-lg border border-border-subtle bg-status-warning-bg-subtle p-4 flex gap-3\">\n <AlertTriangle\n size={20}\n className=\"text-status-warning-text flex-shrink-0 mt-0.5\"\n />\n <div className=\"text-sm text-status-warning-text\">\n <p className=\"font-medium\">\n {tr(\n \"devportal.appIsPublished\",\n \"This application is published\",\n )}\n </p>\n <p className=\"mt-1 text-status-warning-text\">\n {tr(\n \"devportal.publishedAppChangesWarning\",\n \"Changes to published apps may require re-review. Major changes will put the app back into review status.\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {/* Basic Information */}\n <PageSection\n title={tr(\"devportal.basicInformation\", \"Basic Information\")}\n description={tr(\n \"devportal.basicInformationDescription\",\n \"Core details about your application\",\n )}\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.applicationName\", \"Application Name\")}\n value={formData.name}\n onChange={(e) => handleChange(\"name\", e.target.value)}\n placeholder={tr(\n \"devportal.applicationNamePlaceholder\",\n \"My Awesome App\",\n )}\n required\n error={errors.name}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={formData.slug}\n onChange={(e) =>\n handleChange(\"slug\", e.target.value.toLowerCase())\n }\n placeholder={tr(\"devportal.slugPlaceholder\", \"my-awesome-app\")}\n required\n error={errors.slug}\n hint={tr(\n \"devportal.slugChangeWarning\",\n \"Changing the slug may break existing integrations\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.shortDescription\", \"Short Description\")}\n value={formData.shortDescription}\n onChange={(e) =>\n handleChange(\"shortDescription\", e.target.value)\n }\n placeholder={tr(\n \"devportal.shortDescriptionPlaceholder\",\n \"A brief description of what your app does\",\n )}\n required\n error={errors.shortDescription}\n hint={`${formData.shortDescription.length}/200 ${tr(\"devportal.characters\", \"characters\")}`}\n rows={2}\n />\n\n <TextareaField\n label={tr(\"devportal.fullDescription\", \"Full Description\")}\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder={tr(\n \"devportal.fullDescriptionPlaceholder\",\n \"Detailed description of your application, features, and use cases...\",\n )}\n rows={6}\n hint={tr(\n \"devportal.markdownSupported\",\n \"Markdown formatting is supported\",\n )}\n />\n </div>\n </PageSection>\n\n {/* Classification */}\n <PageSection\n title={tr(\"devportal.classification\", \"Classification\")}\n description={tr(\n \"devportal.classificationDescription\",\n \"Categorize your application\",\n )}\n >\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <SelectField\n label={tr(\"devportal.applicationType\", \"Application Type\")}\n value={formData.appType}\n onChange={(e) => handleChange(\"appType\", e.target.value)}\n options={APP_TYPE_OPTIONS}\n required\n error={errors.appType}\n />\n\n <SelectField\n label={tr(\"devportal.category\", \"Category\")}\n value={formData.category}\n onChange={(e) => handleChange(\"category\", e.target.value)}\n options={CATEGORY_OPTIONS}\n required\n error={errors.category}\n />\n\n <SelectField\n label={tr(\"devportal.visibility\", \"Visibility\")}\n value={formData.visibility}\n onChange={(e) =>\n handleChange(\"visibility\", e.target.value as AppVisibility)\n }\n options={VISIBILITY_OPTIONS}\n hint={tr(\n \"devportal.visibilityHint\",\n \"Controls who can see this app once published. Public apps still require admin review and approval before appearing in the store.\",\n )}\n />\n </div>\n\n <TagInput\n label={tr(\"devportal.tags\", \"Tags\")}\n value={formData.tags}\n onChange={(tags) => handleChange(\"tags\", tags)}\n placeholder={tr(\n \"devportal.addTagsPlaceholder\",\n \"Add tags (press Enter)\",\n )}\n />\n\n <TagInput\n label={tr(\"devportal.keywords\", \"Keywords\")}\n value={formData.keywords}\n onChange={(keywords) => handleChange(\"keywords\", keywords)}\n placeholder={tr(\n \"devportal.addKeywordsPlaceholder\",\n \"Add keywords (press Enter)\",\n )}\n />\n </PageSection>\n\n {/* URLs */}\n <PageSection\n title={tr(\"devportal.links\", \"Links\")}\n description={tr(\n \"devportal.linksDescription\",\n \"External resources for your application\",\n )}\n >\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <InputField\n label={tr(\"devportal.websiteUrl\", \"Website URL\")}\n type=\"url\"\n value={formData.websiteUrl}\n onChange={(e) => handleChange(\"websiteUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.websiteUrlPlaceholder\",\n \"https://example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.documentationUrl\", \"Documentation URL\")}\n type=\"url\"\n value={formData.documentationUrl}\n onChange={(e) =>\n handleChange(\"documentationUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.documentationUrlPlaceholder\",\n \"https://docs.example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.supportUrl\", \"Support URL\")}\n type=\"url\"\n value={formData.supportUrl}\n onChange={(e) => handleChange(\"supportUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.supportUrlPlaceholder\",\n \"https://support.example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.githubUrl\", \"GitHub URL\")}\n type=\"url\"\n value={formData.githubUrl}\n onChange={(e) => handleChange(\"githubUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.githubUrlPlaceholder\",\n \"https://github.com/org/repo\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.privacyPolicyUrl\", \"Privacy Policy URL\")}\n type=\"url\"\n value={formData.privacyPolicyUrl}\n onChange={(e) =>\n handleChange(\"privacyPolicyUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.privacyPolicyUrlPlaceholder\",\n \"https://example.com/privacy\",\n )}\n />\n\n <InputField\n label={tr(\n \"devportal.termsOfServiceUrl\",\n \"Terms of Service URL\",\n )}\n type=\"url\"\n value={formData.termsOfServiceUrl}\n onChange={(e) =>\n handleChange(\"termsOfServiceUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.termsOfServiceUrlPlaceholder\",\n \"https://example.com/terms\",\n )}\n />\n </div>\n </PageSection>\n\n {/* Settings */}\n <PageSection\n title={tr(\"devportal.settings\", \"Settings\")}\n description={tr(\n \"devportal.settingsDescription\",\n \"Additional configuration options\",\n )}\n >\n <div className=\"space-y-4\">\n <CheckboxField\n label={tr(\n \"devportal.requireWorkspaceAdminApproval\",\n \"Require workspace admin approval\",\n )}\n description={tr(\n \"devportal.requireWorkspaceAdminApprovalDescription\",\n \"When enabled, workspace admins must approve installations before users can access this app\",\n )}\n checked={formData.requiresApproval}\n onChange={(e) =>\n handleChange(\"requiresApproval\", e.target.checked)\n }\n />\n </div>\n </PageSection>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-4 border-t pt-6\">\n {/* Primary actions */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {canSubmitForReview && (\n <button\n type=\"button\"\n onClick={handleSubmitForReview}\n disabled={isSubmitting || hasChanges}\n className=\"inline-flex items-center gap-2 rounded-md border border-primary bg-primary/10 px-4 py-2 text-sm font-medium text-primary hover:bg-primary/20 transition-colors disabled:opacity-50\"\n >\n <Send size={16} />\n {tr(\"devportal.submitForReview\", \"Submit for Review\")}\n </button>\n )}\n </div>\n <button\n type=\"submit\"\n disabled={isSubmitting || !hasChanges || !canUpdate}\n aria-disabled={!canUpdate}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n {tr(\"devportal.saving\", \"Saving...\")}\n </>\n ) : (\n <>\n <Save size={16} />\n {tr(\"devportal.saveChanges\", \"Save Changes\")}\n </>\n )}\n </button>\n </div>\n\n {/* Danger zone */}\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-4\">\n <h3 className=\"text-sm font-medium text-status-error-text\">\n {tr(\"devportal.dangerZone\", \"Danger Zone\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-error-text\">\n {tr(\n \"devportal.dangerZoneWarning\",\n \"These actions are irreversible. Please proceed with caution.\",\n )}\n </p>\n <div className=\"mt-4 flex gap-3\">\n {canDeprecate && (\n <button\n type=\"button\"\n onClick={() => setShowDeprecateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors\"\n >\n <Archive size={14} />\n {tr(\"devportal.deprecateApp\", \"Deprecate App\")}\n </button>\n )}\n {canDeactivate && (\n <button\n type=\"button\"\n onClick={() => setShowDeactivateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors\"\n >\n <Power size={14} />\n {tr(\"devportal.deactivateApp\", \"Deactivate App\")}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setShowDeleteDialog(true)}\n disabled={!canDelete}\n aria-disabled={!canDelete}\n className=\"inline-flex items-center gap-2 rounded-md border border-status-error-bg bg-bg-surface px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n <Trash2 size={14} />\n {tr(\"devportal.deleteApp\", \"Delete App\")}\n </button>\n </div>\n </div>\n </div>\n </form>\n\n {/* Delete confirmation dialog */}\n <ConfirmDialog\n open={showDeleteDialog}\n onOpenChange={setShowDeleteDialog}\n title={tr(\"devportal.deleteApplication\", \"Delete Application\")}\n description={tr(\n \"devportal.deleteApplicationConfirm\",\n `Are you sure you want to delete \"${app.name}\"? This action cannot be undone. All OAuth clients, API keys, webhooks, and analytics data will be permanently deleted.`,\n )}\n confirmLabel={tr(\"devportal.deleteApplication\", \"Delete Application\")}\n variant=\"destructive\"\n onConfirm={handleDelete}\n />\n\n {/* Deprecate confirmation dialog */}\n <ConfirmDialog\n open={showDeprecateDialog}\n onOpenChange={setShowDeprecateDialog}\n title={tr(\"devportal.deprecateApplication\", \"Deprecate Application\")}\n description={tr(\n \"devportal.deprecateApplicationConfirm\",\n `Are you sure you want to deprecate \"${app.name}\"? Existing users will still be able to use the app, but it will no longer be visible in the marketplace and new installations will be disabled.`,\n )}\n confirmLabel={tr(\n \"devportal.deprecateApplication\",\n \"Deprecate Application\",\n )}\n variant=\"warning\"\n onConfirm={handleDeprecate}\n />\n\n {/* Deactivate confirmation dialog */}\n <ConfirmDialog\n open={showDeactivateDialog}\n onOpenChange={setShowDeactivateDialog}\n title={tr(\n \"devportal.deactivateApplication\",\n \"Deactivate Application\",\n )}\n description={tr(\n \"devportal.deactivateApplicationConfirm\",\n `Are you sure you want to deactivate \"${app.name}\"? This will revoke all active tokens and disable the app from the auth-svc layer. The app will no longer be able to authenticate or act on behalf of users.`,\n )}\n confirmLabel={tr(\n \"devportal.deactivateApplication\",\n \"Deactivate Application\",\n )}\n variant=\"destructive\"\n onConfirm={handleDeactivate}\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EditApplicationPage.js","names":[],"sources":["../../../src/devportal/pages/EditApplicationPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Save,\n Trash2,\n AlertTriangle,\n Archive,\n Send,\n AlertCircle,\n AppWindow,\n Power,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n TagInput,\n CheckboxField,\n} from \"../components/FormField\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { LoadingState } from \"../components/LoadingState\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport type { Application, AppType, AppVisibility, AppStatus } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport {\n useGetApplicationQuery,\n useUpdateApplicationMutation,\n useDeleteApplicationMutation,\n useSubmitApplicationMutation,\n useDeactivateAppMutation,\n} from \"../../generated/global-operations\";\n\ninterface EditApplicationFormData {\n name: string;\n slug: string;\n shortDescription: string;\n description: string;\n appType: AppType;\n visibility: AppVisibility;\n category: string;\n tags: string[];\n keywords: string[];\n websiteUrl: string;\n documentationUrl: string;\n supportUrl: string;\n privacyPolicyUrl: string;\n termsOfServiceUrl: string;\n githubUrl: string;\n requiresApproval: boolean;\n}\n\ninterface FormErrors {\n name?: string;\n slug?: string;\n shortDescription?: string;\n appType?: string;\n category?: string;\n}\n\nconst APP_TYPE_OPTIONS = Object.entries(APP_TYPE_LABELS).map(\n ([value, label]) => ({\n value,\n label,\n }),\n);\n\nconst VISIBILITY_OPTIONS = [\n { value: \"PUBLIC\", label: \"Public - Visible to everyone\" },\n { value: \"PRIVATE\", label: \"Private - Only visible to you\" },\n { value: \"UNLISTED\", label: \"Unlisted - Accessible via direct link\" },\n { value: \"INTERNAL\", label: \"Internal - Only visible within organization\" },\n];\n\nconst CATEGORY_OPTIONS = [\n { value: \"productivity\", label: \"Productivity\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"automation\", label: \"Automation\" },\n { value: \"developer-tools\", label: \"Developer Tools\" },\n { value: \"finance\", label: \"Finance\" },\n { value: \"hr\", label: \"HR & People\" },\n { value: \"marketing\", label: \"Marketing\" },\n { value: \"project-management\", label: \"Project Management\" },\n { value: \"security\", label: \"Security\" },\n { value: \"support\", label: \"Support\" },\n { value: \"other\", label: \"Other\" },\n];\n\n/**\n * Helper to safely map GraphQL response to Application type\n */\nfunction mapToApplication(\n data: NonNullable<\n ReturnType<typeof useGetApplicationQuery>[\"data\"]\n >[\"application\"],\n): Application | null {\n if (!data) return null;\n return {\n id: data.id,\n name: data.name,\n slug: data.slug,\n shortDescription: data.shortDescription || undefined,\n description: data.description || undefined,\n appType: data.appType,\n status: data.status,\n visibility: data.visibility,\n tenantId: data.tenantId,\n developerId: data.developerId,\n developerOrganizationId: data.developerOrganizationId || undefined,\n category: data.category || undefined,\n tags: data.tags || [],\n keywords: data.keywords || [],\n iconUrl: data.iconUrl || undefined,\n websiteUrl: data.websiteUrl || undefined,\n documentationUrl: data.documentationUrl || undefined,\n supportUrl: data.supportUrl || undefined,\n privacyPolicyUrl: data.privacyPolicyUrl || undefined,\n termsOfServiceUrl: data.termsOfServiceUrl || undefined,\n githubUrl: data.githubUrl || undefined,\n pricingType: data.pricingType || undefined,\n installCount: data.installCount,\n activeInstallCount: data.activeInstallCount,\n averageRating: data.averageRating ?? undefined,\n reviewCount: data.reviewCount,\n submittedAt: data.submittedAt || undefined,\n publishedAt: data.publishedAt || undefined,\n lastReviewedAt: data.lastReviewedAt || undefined,\n reviewStatus: data.reviewStatus,\n reviewNotes: data.reviewNotes || undefined,\n reviewedBy: data.reviewedBy || undefined,\n isFeatured: data.isFeatured,\n isVerified: data.isVerified,\n requiresApproval: data.requiresApproval,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n };\n}\n\n/**\n * Edit application page — wired to real GraphQL mutations.\n */\nexport const EditApplicationPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { appId } = useParams<{ appId: string }>();\n const { t } = useI18n();\n const { allowed: canUpdate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_UPDATE,\n );\n const { allowed: canDelete } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_DELETE,\n );\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [errors, setErrors] = useState<FormErrors>({});\n const [showDeleteDialog, setShowDeleteDialog] = useState(false);\n const [showDeprecateDialog, setShowDeprecateDialog] = useState(false);\n const [showDeactivateDialog, setShowDeactivateDialog] = useState(false);\n const [hasChanges, setHasChanges] = useState(false);\n\n // ---------------------------------------------------------------------------\n // GraphQL queries & mutations\n // ---------------------------------------------------------------------------\n const {\n data,\n loading,\n error: queryError,\n } = useGetApplicationQuery({\n variables: { id: appId! },\n skip: !appId,\n });\n\n const [updateApplication] = useUpdateApplicationMutation();\n const [deleteApplication] = useDeleteApplicationMutation();\n const [submitApplication] = useSubmitApplicationMutation();\n const [deactivateApp] = useDeactivateAppMutation();\n\n // ---------------------------------------------------------------------------\n // Derive application from query data\n // ---------------------------------------------------------------------------\n const app = mapToApplication(data?.application);\n\n useEffect(() => {\n const name = app?.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, app?.name]);\n\n // ---------------------------------------------------------------------------\n // Form state — initialised from fetched app once available\n // ---------------------------------------------------------------------------\n const [formData, setFormData] = useState<EditApplicationFormData>({\n name: \"\",\n slug: \"\",\n shortDescription: \"\",\n description: \"\",\n appType: \"WEB_APP\",\n visibility: \"PUBLIC\",\n category: \"\",\n tags: [],\n keywords: [],\n websiteUrl: \"\",\n documentationUrl: \"\",\n supportUrl: \"\",\n privacyPolicyUrl: \"\",\n termsOfServiceUrl: \"\",\n githubUrl: \"\",\n requiresApproval: false,\n });\n\n useEffect(() => {\n if (app) {\n setFormData({\n name: app.name,\n slug: app.slug,\n shortDescription: app.shortDescription || \"\",\n description: app.description || \"\",\n appType: app.appType,\n visibility: app.visibility,\n category: app.category || \"\",\n tags: app.tags,\n keywords: app.keywords,\n websiteUrl: app.websiteUrl || \"\",\n documentationUrl: app.documentationUrl || \"\",\n supportUrl: app.supportUrl || \"\",\n privacyPolicyUrl: app.privacyPolicyUrl || \"\",\n termsOfServiceUrl: app.termsOfServiceUrl || \"\",\n githubUrl: app.githubUrl || \"\",\n requiresApproval: app.requiresApproval,\n });\n setHasChanges(false);\n }\n // Re-sync form when the underlying application data changes (e.g. after\n // a refetch triggered by a mutation). We intentionally depend on\n // `data?.application` rather than the derived `app` object so that the\n // effect fires when the query cache updates.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [data?.application]);\n\n // ---------------------------------------------------------------------------\n // Handlers\n // ---------------------------------------------------------------------------\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleChange = (\n field: keyof EditApplicationFormData,\n value: string | string[] | boolean,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n setHasChanges(true);\n if (errors[field as keyof FormErrors]) {\n setErrors((prev) => ({ ...prev, [field]: undefined }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: FormErrors = {};\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.appNameRequired\",\n \"Application name is required\",\n );\n } else if (formData.name.length < 3) {\n newErrors.name = tr(\n \"devportal.appNameMinLength\",\n \"Name must be at least 3 characters\",\n );\n } else if (formData.name.length > 100) {\n newErrors.name = tr(\n \"devportal.appNameMaxLength\",\n \"Name must be less than 100 characters\",\n );\n }\n\n if (!formData.slug.trim()) {\n newErrors.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n } else if (!/^[a-z0-9-]+$/.test(formData.slug)) {\n newErrors.slug = tr(\n \"devportal.slugInvalidChars\",\n \"Slug can only contain lowercase letters, numbers, and hyphens\",\n );\n }\n\n if (!formData.shortDescription.trim()) {\n newErrors.shortDescription = tr(\n \"devportal.shortDescriptionRequired\",\n \"Short description is required\",\n );\n } else if (formData.shortDescription.length > 200) {\n newErrors.shortDescription = tr(\n \"devportal.shortDescriptionMaxLength\",\n \"Short description must be less than 200 characters\",\n );\n }\n\n if (!formData.appType) {\n newErrors.appType = tr(\n \"devportal.appTypeRequired\",\n \"Application type is required\",\n );\n }\n\n if (!formData.category) {\n newErrors.category = tr(\n \"devportal.categoryRequired\",\n \"Category is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm() || !app) {\n return;\n }\n\n setIsSubmitting(true);\n\n try {\n await updateApplication({\n variables: {\n id: app.id,\n input: {\n name: formData.name,\n shortDescription: formData.shortDescription,\n description: formData.description,\n appType: formData.appType,\n visibility: formData.visibility,\n category: formData.category,\n tags: formData.tags,\n keywords: formData.keywords,\n websiteUrl: formData.websiteUrl || null,\n documentationUrl: formData.documentationUrl || null,\n supportUrl: formData.supportUrl || null,\n privacyPolicyUrl: formData.privacyPolicyUrl || null,\n termsOfServiceUrl: formData.termsOfServiceUrl || null,\n githubUrl: formData.githubUrl || null,\n },\n },\n });\n setHasChanges(false);\n } catch (err) {\n console.error(\"Failed to update application:\", err);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n const handleDelete = async () => {\n if (!app) return;\n void nativeImpact(\"medium\");\n try {\n await deleteApplication({\n variables: { id: app.id },\n });\n void nativeNotify(\"success\");\n handleNavigate(\"/apps\");\n } catch (err) {\n void nativeNotify(\"error\");\n console.error(\"Failed to delete application:\", err);\n }\n };\n\n const handleDeprecate = async () => {\n if (!app) return;\n try {\n // Deprecate is modelled as a status transition via update. The backend\n // treats a visibility change to PRIVATE + status mutation or a dedicated\n // deprecation endpoint. Here we use submitApplication with a status note\n // as the deprecate action since there is no separate `deprecateApplication`\n // mutation. If a dedicated mutation is added later, swap it in here.\n await updateApplication({\n variables: {\n id: app.id,\n input: {\n visibility: \"PRIVATE\",\n },\n },\n });\n setShowDeprecateDialog(false);\n } catch (err) {\n console.error(\"Failed to deprecate application:\", err);\n }\n };\n\n const handleDeactivate = async () => {\n if (!app) return;\n try {\n await deactivateApp({\n variables: { id: app.id },\n });\n setShowDeactivateDialog(false);\n handleNavigate(\"/apps\");\n } catch (err) {\n console.error(\"Failed to deactivate application:\", err);\n }\n };\n\n const handleSubmitForReview = async () => {\n if (!app) return;\n try {\n setIsSubmitting(true);\n await submitApplication({\n variables: { id: app.id },\n });\n } catch (err) {\n console.error(\"Failed to submit for review:\", err);\n } finally {\n setIsSubmitting(false);\n }\n };\n\n // ---------------------------------------------------------------------------\n // Loading / Error / Not-found states\n // ---------------------------------------------------------------------------\n if (loading) {\n return (\n <LoadingState\n message={tr(\"devportal.loadingApplication\", \"Loading application...\")}\n />\n );\n }\n\n if (queryError) {\n return (\n <PageLayout\n title={tr(\"devportal.error\", \"Error\")}\n backLink={`${basePath}/apps`}\n backLabel={tr(\"devportal.backToApplications\", \"Back to Applications\")}\n >\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <AlertCircle size={48} className=\"text-destructive mb-4\" />\n <h3 className=\"text-lg font-semibold mb-2\">\n {tr(\n \"devportal.failedToLoadApplication\",\n \"Failed to load application\",\n )}\n </h3>\n <p className=\"text-sm text-muted-foreground\">{queryError.message}</p>\n </div>\n </PageLayout>\n );\n }\n\n if (!app) {\n return (\n <PageLayout\n title={tr(\"devportal.notFound\", \"Not Found\")}\n backLink={`${basePath}/apps`}\n backLabel={tr(\"devportal.backToApplications\", \"Back to Applications\")}\n >\n <EmptyState\n icon={<AppWindow size={48} />}\n title={tr(\"devportal.applicationNotFound\", \"Application not found\")}\n description={tr(\n \"devportal.applicationNotFoundDescription\",\n \"The application you're looking for doesn't exist or you don't have access to it.\",\n )}\n />\n </PageLayout>\n );\n }\n\n // ---------------------------------------------------------------------------\n // Derived flags\n // ---------------------------------------------------------------------------\n const canSubmitForReview =\n app.status === \"DRAFT\" || app.status === \"REJECTED\";\n const canDeprecate = app.status === \"PUBLISHED\";\n const canDeactivate = app.status === \"ACTIVE\" || app.status === \"PUBLISHED\";\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_UPDATE}>\n <PageLayout\n title={`${tr(\"devportal.editAppTitlePrefix\", \"Edit:\")} ${app.name}`}\n description={tr(\n \"devportal.editAppDescription\",\n \"Update your application settings\",\n )}\n backLink={`${basePath}/apps/${app.id}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <div className=\"flex items-center gap-2\">\n <StatusBadge status={app.status} type=\"app\" />\n </div>\n }\n >\n <form onSubmit={handleSubmit} className=\"space-y-8 max-w-3xl\">\n {/* Status banner for published apps */}\n {app.status === \"PUBLISHED\" && (\n <div className=\"rounded-lg border border-border-subtle bg-status-warning-bg-subtle p-4 flex gap-3\">\n <AlertTriangle\n size={20}\n className=\"text-status-warning-text flex-shrink-0 mt-0.5\"\n />\n <div className=\"text-sm text-status-warning-text\">\n <p className=\"font-medium\">\n {tr(\n \"devportal.appIsPublished\",\n \"This application is published\",\n )}\n </p>\n <p className=\"mt-1 text-status-warning-text\">\n {tr(\n \"devportal.publishedAppChangesWarning\",\n \"Changes to published apps may require re-review. Major changes will put the app back into review status.\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {/* Basic Information */}\n <PageSection\n title={tr(\"devportal.basicInformation\", \"Basic Information\")}\n description={tr(\n \"devportal.basicInformationDescription\",\n \"Core details about your application\",\n )}\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.applicationName\", \"Application Name\")}\n value={formData.name}\n onChange={(e) => handleChange(\"name\", e.target.value)}\n placeholder={tr(\n \"devportal.applicationNamePlaceholder\",\n \"My Awesome App\",\n )}\n required\n error={errors.name}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={formData.slug}\n onChange={(e) =>\n handleChange(\"slug\", e.target.value.toLowerCase())\n }\n placeholder={tr(\"devportal.slugPlaceholder\", \"my-awesome-app\")}\n required\n error={errors.slug}\n hint={tr(\n \"devportal.slugChangeWarning\",\n \"Changing the slug may break existing integrations\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.shortDescription\", \"Short Description\")}\n value={formData.shortDescription}\n onChange={(e) =>\n handleChange(\"shortDescription\", e.target.value)\n }\n placeholder={tr(\n \"devportal.shortDescriptionPlaceholder\",\n \"A brief description of what your app does\",\n )}\n required\n error={errors.shortDescription}\n hint={`${formData.shortDescription.length}/200 ${tr(\"devportal.characters\", \"characters\")}`}\n rows={2}\n />\n\n <TextareaField\n label={tr(\"devportal.fullDescription\", \"Full Description\")}\n value={formData.description}\n onChange={(e) => handleChange(\"description\", e.target.value)}\n placeholder={tr(\n \"devportal.fullDescriptionPlaceholder\",\n \"Detailed description of your application, features, and use cases...\",\n )}\n rows={6}\n hint={tr(\n \"devportal.markdownSupported\",\n \"Markdown formatting is supported\",\n )}\n />\n </div>\n </PageSection>\n\n {/* Classification */}\n <PageSection\n title={tr(\"devportal.classification\", \"Classification\")}\n description={tr(\n \"devportal.classificationDescription\",\n \"Categorize your application\",\n )}\n >\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <SelectField\n label={tr(\"devportal.applicationType\", \"Application Type\")}\n value={formData.appType}\n onChange={(e) => handleChange(\"appType\", e.target.value)}\n options={APP_TYPE_OPTIONS}\n required\n error={errors.appType}\n />\n\n <SelectField\n label={tr(\"devportal.category\", \"Category\")}\n value={formData.category}\n onChange={(e) => handleChange(\"category\", e.target.value)}\n options={CATEGORY_OPTIONS}\n required\n error={errors.category}\n />\n\n <SelectField\n label={tr(\"devportal.visibility\", \"Visibility\")}\n value={formData.visibility}\n onChange={(e) =>\n handleChange(\"visibility\", e.target.value as AppVisibility)\n }\n options={VISIBILITY_OPTIONS}\n hint={tr(\n \"devportal.visibilityHint\",\n \"Controls who can see this app once published. Public apps still require admin review and approval before appearing in the store.\",\n )}\n />\n </div>\n\n <TagInput\n label={tr(\"devportal.tags\", \"Tags\")}\n value={formData.tags}\n onChange={(tags) => handleChange(\"tags\", tags)}\n placeholder={tr(\n \"devportal.addTagsPlaceholder\",\n \"Add tags (press Enter)\",\n )}\n />\n\n <TagInput\n label={tr(\"devportal.keywords\", \"Keywords\")}\n value={formData.keywords}\n onChange={(keywords) => handleChange(\"keywords\", keywords)}\n placeholder={tr(\n \"devportal.addKeywordsPlaceholder\",\n \"Add keywords (press Enter)\",\n )}\n />\n </PageSection>\n\n {/* URLs */}\n <PageSection\n title={tr(\"devportal.links\", \"Links\")}\n description={tr(\n \"devportal.linksDescription\",\n \"External resources for your application\",\n )}\n >\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <InputField\n label={tr(\"devportal.websiteUrl\", \"Website URL\")}\n type=\"url\"\n value={formData.websiteUrl}\n onChange={(e) => handleChange(\"websiteUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.websiteUrlPlaceholder\",\n \"https://example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.documentationUrl\", \"Documentation URL\")}\n type=\"url\"\n value={formData.documentationUrl}\n onChange={(e) =>\n handleChange(\"documentationUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.documentationUrlPlaceholder\",\n \"https://docs.example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.supportUrl\", \"Support URL\")}\n type=\"url\"\n value={formData.supportUrl}\n onChange={(e) => handleChange(\"supportUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.supportUrlPlaceholder\",\n \"https://support.example.com\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.githubUrl\", \"GitHub URL\")}\n type=\"url\"\n value={formData.githubUrl}\n onChange={(e) => handleChange(\"githubUrl\", e.target.value)}\n placeholder={tr(\n \"devportal.githubUrlPlaceholder\",\n \"https://github.com/org/repo\",\n )}\n />\n\n <InputField\n label={tr(\"devportal.privacyPolicyUrl\", \"Privacy Policy URL\")}\n type=\"url\"\n value={formData.privacyPolicyUrl}\n onChange={(e) =>\n handleChange(\"privacyPolicyUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.privacyPolicyUrlPlaceholder\",\n \"https://example.com/privacy\",\n )}\n />\n\n <InputField\n label={tr(\n \"devportal.termsOfServiceUrl\",\n \"Terms of Service URL\",\n )}\n type=\"url\"\n value={formData.termsOfServiceUrl}\n onChange={(e) =>\n handleChange(\"termsOfServiceUrl\", e.target.value)\n }\n placeholder={tr(\n \"devportal.termsOfServiceUrlPlaceholder\",\n \"https://example.com/terms\",\n )}\n />\n </div>\n </PageSection>\n\n {/* Settings */}\n <PageSection\n title={tr(\"devportal.settings\", \"Settings\")}\n description={tr(\n \"devportal.settingsDescription\",\n \"Additional configuration options\",\n )}\n >\n <div className=\"space-y-4\">\n <CheckboxField\n label={tr(\n \"devportal.requireWorkspaceAdminApproval\",\n \"Require workspace admin approval\",\n )}\n description={tr(\n \"devportal.requireWorkspaceAdminApprovalDescription\",\n \"When enabled, workspace admins must approve installations before users can access this app\",\n )}\n checked={formData.requiresApproval}\n onChange={(e) =>\n handleChange(\"requiresApproval\", e.target.checked)\n }\n />\n </div>\n </PageSection>\n\n {/* Actions */}\n <div className=\"flex flex-col gap-4 border-t pt-6\">\n {/* Primary actions */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {canSubmitForReview && (\n <button\n type=\"button\"\n onClick={handleSubmitForReview}\n disabled={isSubmitting || hasChanges}\n className=\"inline-flex items-center gap-2 rounded-md border border-primary bg-primary/10 px-4 py-2 text-sm font-medium text-primary hover:bg-primary/20 transition-colors disabled:opacity-50\"\n >\n <Send size={16} />\n {tr(\"devportal.submitForReview\", \"Submit for Review\")}\n </button>\n )}\n </div>\n <button\n type=\"submit\"\n disabled={isSubmitting || !hasChanges || !canUpdate}\n aria-disabled={!canUpdate}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isSubmitting ? (\n <>\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n {tr(\"devportal.saving\", \"Saving...\")}\n </>\n ) : (\n <>\n <Save size={16} />\n {tr(\"devportal.saveChanges\", \"Save Changes\")}\n </>\n )}\n </button>\n </div>\n\n {/* Danger zone */}\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-4\">\n <h3 className=\"text-sm font-medium text-status-error-text\">\n {tr(\"devportal.dangerZone\", \"Danger Zone\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-error-text\">\n {tr(\n \"devportal.dangerZoneWarning\",\n \"These actions are irreversible. Please proceed with caution.\",\n )}\n </p>\n <div className=\"mt-4 flex gap-3\">\n {canDeprecate && (\n <button\n type=\"button\"\n onClick={() => setShowDeprecateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors\"\n >\n <Archive size={14} />\n {tr(\"devportal.deprecateApp\", \"Deprecate App\")}\n </button>\n )}\n {canDeactivate && (\n <button\n type=\"button\"\n onClick={() => setShowDeactivateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md border border-accent-orange bg-bg-surface px-3 py-1.5 text-sm font-medium text-accent-orange hover:bg-accent-orange-subtle transition-colors\"\n >\n <Power size={14} />\n {tr(\"devportal.deactivateApp\", \"Deactivate App\")}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setShowDeleteDialog(true)}\n disabled={!canDelete}\n aria-disabled={!canDelete}\n className=\"inline-flex items-center gap-2 rounded-md border border-status-error-bg bg-bg-surface px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n <Trash2 size={14} />\n {tr(\"devportal.deleteApp\", \"Delete App\")}\n </button>\n </div>\n </div>\n </div>\n </form>\n\n {/* Delete confirmation dialog */}\n <ConfirmDialog\n open={showDeleteDialog}\n onOpenChange={setShowDeleteDialog}\n title={tr(\"devportal.deleteApplication\", \"Delete Application\")}\n description={tr(\n \"devportal.deleteApplicationConfirm\",\n `Are you sure you want to delete \"${app.name}\"? This action cannot be undone. All OAuth clients, API keys, webhooks, and analytics data will be permanently deleted.`,\n )}\n confirmLabel={tr(\"devportal.deleteApplication\", \"Delete Application\")}\n variant=\"destructive\"\n onConfirm={handleDelete}\n />\n\n {/* Deprecate confirmation dialog */}\n <ConfirmDialog\n open={showDeprecateDialog}\n onOpenChange={setShowDeprecateDialog}\n title={tr(\"devportal.deprecateApplication\", \"Deprecate Application\")}\n description={tr(\n \"devportal.deprecateApplicationConfirm\",\n `Are you sure you want to deprecate \"${app.name}\"? Existing users will still be able to use the app, but it will no longer be visible in the marketplace and new installations will be disabled.`,\n )}\n confirmLabel={tr(\n \"devportal.deprecateApplication\",\n \"Deprecate Application\",\n )}\n variant=\"warning\"\n onConfirm={handleDeprecate}\n />\n\n {/* Deactivate confirmation dialog */}\n <ConfirmDialog\n open={showDeactivateDialog}\n onOpenChange={setShowDeactivateDialog}\n title={tr(\n \"devportal.deactivateApplication\",\n \"Deactivate Application\",\n )}\n description={tr(\n \"devportal.deactivateApplicationConfirm\",\n `Are you sure you want to deactivate \"${app.name}\"? This will revoke all active tokens and disable the app from the auth-svc layer. The app will no longer be able to authenticate or act on behalf of users.`,\n )}\n confirmLabel={tr(\n \"devportal.deactivateApplication\",\n \"Deactivate Application\",\n )}\n variant=\"destructive\"\n onConfirm={handleDeactivate}\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useGetApplicationQuery as t } from "../../generated/global-operations.js";
|
|
3
|
+
import { PageLayout as n, PageSection as r } from "../components/PageLayout.js";
|
|
4
|
+
import { Alert as i } from "../components/ConfirmDialog.js";
|
|
5
5
|
import { PermissionGuard as a } from "../components/PermissionGuard.js";
|
|
6
6
|
import "../components/index.js";
|
|
7
7
|
import { DEVPORTAL_PERMISSIONS as o } from "../constants/permissions.js";
|
|
@@ -38,7 +38,7 @@ var S = {
|
|
|
38
38
|
let { appId: w } = y(), { navigate: T, basePath: E } = e(), { t: D } = c(), O = (e, t) => {
|
|
39
39
|
let n = D(e);
|
|
40
40
|
return n === e ? t : n;
|
|
41
|
-
}, { data: k, loading: A, error: j } =
|
|
41
|
+
}, { data: k, loading: A, error: j } = t({
|
|
42
42
|
variables: { id: w },
|
|
43
43
|
skip: !w,
|
|
44
44
|
fetchPolicy: "cache-and-network"
|
|
@@ -48,7 +48,7 @@ var S = {
|
|
|
48
48
|
if (!w || !e) return;
|
|
49
49
|
let t = window.__breadcrumbRegistry;
|
|
50
50
|
return t?.register(w, e), () => t?.unregister(w);
|
|
51
|
-
}, [w, M?.name]), A) return /* @__PURE__ */ b(
|
|
51
|
+
}, [w, M?.name]), A) return /* @__PURE__ */ b(n, {
|
|
52
52
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
53
53
|
description: O("devportal.loading", "Loading..."),
|
|
54
54
|
backLink: `/apps/${w}`,
|
|
@@ -58,12 +58,12 @@ var S = {
|
|
|
58
58
|
children: /* @__PURE__ */ b(h, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
59
59
|
})
|
|
60
60
|
});
|
|
61
|
-
if (j || !M) return /* @__PURE__ */ b(
|
|
61
|
+
if (j || !M) return /* @__PURE__ */ b(n, {
|
|
62
62
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
63
63
|
description: O("devportal.couldNotLoadApplication", "Could not load application"),
|
|
64
64
|
backLink: `/apps/${w}`,
|
|
65
65
|
backLabel: O("devportal.backToApplication", "Back to Application"),
|
|
66
|
-
children: /* @__PURE__ */ b(
|
|
66
|
+
children: /* @__PURE__ */ b(i, {
|
|
67
67
|
variant: "error",
|
|
68
68
|
title: O("devportal.failedToLoadApplication", "Failed to load application"),
|
|
69
69
|
children: j?.message ?? O("devportal.applicationNotFound", "Application not found")
|
|
@@ -72,7 +72,7 @@ var S = {
|
|
|
72
72
|
let N = Array.isArray(M.screenshots) ? M.screenshots : [], P = M.status === "PUBLISHED";
|
|
73
73
|
return /* @__PURE__ */ b(a, {
|
|
74
74
|
permission: o.APP_READ,
|
|
75
|
-
children: /* @__PURE__ */ x(
|
|
75
|
+
children: /* @__PURE__ */ x(n, {
|
|
76
76
|
title: O("devportal.marketplacePreview", "Marketplace Preview"),
|
|
77
77
|
description: O("devportal.marketplacePreviewDescription", "Preview how your app will appear in the marketplace"),
|
|
78
78
|
backLink: `/apps/${w}`,
|
|
@@ -292,7 +292,7 @@ var S = {
|
|
|
292
292
|
})
|
|
293
293
|
]
|
|
294
294
|
}),
|
|
295
|
-
/* @__PURE__ */ x(
|
|
295
|
+
/* @__PURE__ */ x(r, {
|
|
296
296
|
title: O("devportal.listingCompleteness", "Listing Completeness"),
|
|
297
297
|
children: [/* @__PURE__ */ b("div", {
|
|
298
298
|
className: "space-y-2",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { cn as t } from "../utils/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { useDeactivateAppMutation as n, useGetAuthAppQuery as r, useRotateAppSecretMutation as i, useUpdateAuthAppMutation as a } from "../../generated/global-operations.js";
|
|
4
|
+
import { getErrorMessage as o } from "../hooks/useGraphQL.js";
|
|
5
|
+
import { PageLayout as s } from "../components/PageLayout.js";
|
|
6
|
+
import { ActiveIndicator as c } from "../components/StatusBadge.js";
|
|
7
|
+
import { nativeCopyText as l, nativeImpact as u, nativeNotify as d } from "../../utils/nativeBridge.js";
|
|
8
|
+
import { PermissionGuard as f } from "../components/PermissionGuard.js";
|
|
8
9
|
import "../components/index.js";
|
|
9
|
-
import { DEVPORTAL_PERMISSIONS as
|
|
10
|
-
import { getErrorMessage as p } from "../hooks/useGraphQL.js";
|
|
10
|
+
import { DEVPORTAL_PERMISSIONS as p } from "../constants/permissions.js";
|
|
11
11
|
import "../hooks/index.js";
|
|
12
12
|
import { useState as m } from "react";
|
|
13
13
|
import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
@@ -19,11 +19,11 @@ var D = () => {
|
|
|
19
19
|
let { clientId: D } = w(), { navigate: O, basePath: k } = e(), { t: A } = h(), j = (e, t) => {
|
|
20
20
|
let n = A(e);
|
|
21
21
|
return n === e ? t : n;
|
|
22
|
-
}, [M, N] = m(!1), [P, F] = m(null), [I, L] = m(!1), [R, z] = m(!1), { data: B, loading: V, error: H, refetch: U } =
|
|
22
|
+
}, [M, N] = m(!1), [P, F] = m(null), [I, L] = m(!1), [R, z] = m(!1), { data: B, loading: V, error: H, refetch: U } = r({
|
|
23
23
|
variables: { id: D || "" },
|
|
24
24
|
skip: !D,
|
|
25
25
|
fetchPolicy: "cache-and-network"
|
|
26
|
-
}), [W, { loading: G }] =
|
|
26
|
+
}), [W, { loading: G }] = i(), [K, { loading: q }] = n(), [J] = a(), Y = B?.app ? {
|
|
27
27
|
id: B.app.id,
|
|
28
28
|
name: B.app.name,
|
|
29
29
|
description: B.app.description ?? void 0,
|
|
@@ -39,9 +39,9 @@ var D = () => {
|
|
|
39
39
|
} : null, X = (e) => {
|
|
40
40
|
O?.(`${k}${e}`);
|
|
41
41
|
}, Z = async (e) => {
|
|
42
|
-
|
|
42
|
+
u("light"), d(await l(e) ? "success" : "error");
|
|
43
43
|
};
|
|
44
|
-
return V && !B ? /* @__PURE__ */ T(
|
|
44
|
+
return V && !B ? /* @__PURE__ */ T(s, {
|
|
45
45
|
title: j("devportal.appDetails", "App Details"),
|
|
46
46
|
description: j("devportal.loadingDots", "Loading..."),
|
|
47
47
|
backLink: "/oauth",
|
|
@@ -50,7 +50,7 @@ var D = () => {
|
|
|
50
50
|
className: "flex items-center justify-center py-12",
|
|
51
51
|
children: /* @__PURE__ */ T(x, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
52
52
|
})
|
|
53
|
-
}) : H || !Y ? /* @__PURE__ */ T(
|
|
53
|
+
}) : H || !Y ? /* @__PURE__ */ T(s, {
|
|
54
54
|
title: j("devportal.appDetails", "App Details"),
|
|
55
55
|
description: j("devportal.failedToLoad", "Failed to load"),
|
|
56
56
|
backLink: "/oauth",
|
|
@@ -65,7 +65,7 @@ var D = () => {
|
|
|
65
65
|
}),
|
|
66
66
|
H && /* @__PURE__ */ T("p", {
|
|
67
67
|
className: "mt-1 text-sm text-muted-foreground",
|
|
68
|
-
children:
|
|
68
|
+
children: o(H)
|
|
69
69
|
}),
|
|
70
70
|
/* @__PURE__ */ E("button", {
|
|
71
71
|
onClick: () => X("/oauth"),
|
|
@@ -74,9 +74,9 @@ var D = () => {
|
|
|
74
74
|
})
|
|
75
75
|
]
|
|
76
76
|
})
|
|
77
|
-
}) : /* @__PURE__ */ T(
|
|
78
|
-
permission:
|
|
79
|
-
children: /* @__PURE__ */ E(
|
|
77
|
+
}) : /* @__PURE__ */ T(f, {
|
|
78
|
+
permission: p.APP_READ,
|
|
79
|
+
children: /* @__PURE__ */ E(s, {
|
|
80
80
|
title: Y.name,
|
|
81
81
|
description: Y.description || j("devportal.oauthAppDetailsAndCredentials", "OAuth App details and credentials"),
|
|
82
82
|
backLink: "/oauth",
|
|
@@ -120,7 +120,7 @@ var D = () => {
|
|
|
120
120
|
children: j("devportal.status", "Status")
|
|
121
121
|
}), /* @__PURE__ */ E("div", {
|
|
122
122
|
className: "flex items-center gap-2",
|
|
123
|
-
children: [/* @__PURE__ */ T(
|
|
123
|
+
children: [/* @__PURE__ */ T(c, { active: Y.status === "ACTIVE" }), /* @__PURE__ */ T("span", {
|
|
124
124
|
className: "text-sm font-medium",
|
|
125
125
|
children: Y.status
|
|
126
126
|
})]
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { cn as t } from "../utils/cn.js";
|
|
3
3
|
import { EmptyState as n } from "../components/EmptyState.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
4
|
+
import { useCurrentUserAppsQuery as r } from "../../generated/global-operations.js";
|
|
5
|
+
import { getErrorMessage as i } from "../hooks/useGraphQL.js";
|
|
6
|
+
import { PageLayout as a } from "../components/PageLayout.js";
|
|
7
|
+
import { ActiveIndicator as o } from "../components/StatusBadge.js";
|
|
8
|
+
import { DataTable as s } from "../components/DataTable.js";
|
|
9
|
+
import { PermissionGuard as c } from "../components/PermissionGuard.js";
|
|
9
10
|
import "../components/index.js";
|
|
10
|
-
import { DEVPORTAL_PERMISSIONS as
|
|
11
|
-
import { getErrorMessage as l } from "../hooks/useGraphQL.js";
|
|
11
|
+
import { DEVPORTAL_PERMISSIONS as l } from "../constants/permissions.js";
|
|
12
12
|
import "../hooks/index.js";
|
|
13
13
|
import { useState as u } from "react";
|
|
14
14
|
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
@@ -19,7 +19,7 @@ var y = () => {
|
|
|
19
19
|
let { navigate: y, basePath: b } = e(), { t: x } = d(), S = (e, t) => {
|
|
20
20
|
let n = x(e);
|
|
21
21
|
return n === e ? t : n;
|
|
22
|
-
}, [C, w] = u(""), [T, E] = u(""), { data: D, loading: O, error: k, refetch: A } =
|
|
22
|
+
}, [C, w] = u(""), [T, E] = u(""), { data: D, loading: O, error: k, refetch: A } = r({ fetchPolicy: "cache-and-network" }), j = D?.currentUserApps?.apps.map((e) => ({
|
|
23
23
|
id: e.id,
|
|
24
24
|
name: e.name,
|
|
25
25
|
description: e.description ?? void 0,
|
|
@@ -38,7 +38,7 @@ var y = () => {
|
|
|
38
38
|
let t = !C || e.name.toLowerCase().includes(C.toLowerCase()) || e.clientId.toLowerCase().includes(C.toLowerCase()), n = !T || e.type === T;
|
|
39
39
|
return t && n;
|
|
40
40
|
});
|
|
41
|
-
if (O && !D) return /* @__PURE__ */ _(
|
|
41
|
+
if (O && !D) return /* @__PURE__ */ _(a, {
|
|
42
42
|
title: S("devportal.oauthApps", "OAuth Apps"),
|
|
43
43
|
description: S("devportal.manageOAuthApps", "Manage your OAuth applications and credentials"),
|
|
44
44
|
children: /* @__PURE__ */ v("div", {
|
|
@@ -49,7 +49,7 @@ var y = () => {
|
|
|
49
49
|
})]
|
|
50
50
|
})
|
|
51
51
|
});
|
|
52
|
-
if (k) return /* @__PURE__ */ _(
|
|
52
|
+
if (k) return /* @__PURE__ */ _(a, {
|
|
53
53
|
title: S("devportal.oauthApps", "OAuth Apps"),
|
|
54
54
|
description: S("devportal.manageOAuthApps", "Manage your OAuth applications and credentials"),
|
|
55
55
|
children: /* @__PURE__ */ v("div", {
|
|
@@ -62,7 +62,7 @@ var y = () => {
|
|
|
62
62
|
}),
|
|
63
63
|
/* @__PURE__ */ _("p", {
|
|
64
64
|
className: "mt-1 text-sm text-muted-foreground",
|
|
65
|
-
children:
|
|
65
|
+
children: i(k)
|
|
66
66
|
}),
|
|
67
67
|
/* @__PURE__ */ _("button", {
|
|
68
68
|
onClick: () => A(),
|
|
@@ -127,7 +127,7 @@ var y = () => {
|
|
|
127
127
|
header: "Status",
|
|
128
128
|
render: (e) => /* @__PURE__ */ v("div", {
|
|
129
129
|
className: "flex items-center gap-2",
|
|
130
|
-
children: [/* @__PURE__ */ _(
|
|
130
|
+
children: [/* @__PURE__ */ _(o, { active: e.status === "ACTIVE" }), /* @__PURE__ */ _("span", {
|
|
131
131
|
className: "text-sm",
|
|
132
132
|
children: e.status
|
|
133
133
|
})]
|
|
@@ -139,9 +139,9 @@ var y = () => {
|
|
|
139
139
|
integration: j.filter((e) => e.type === "INTEGRATION" || e.type === "API_INTEGRATION").length,
|
|
140
140
|
service: j.filter((e) => e.type === "SERVICE").length
|
|
141
141
|
};
|
|
142
|
-
return /* @__PURE__ */ _(
|
|
143
|
-
permission:
|
|
144
|
-
children: /* @__PURE__ */ v(
|
|
142
|
+
return /* @__PURE__ */ _(c, {
|
|
143
|
+
permission: l.APP_READ,
|
|
144
|
+
children: /* @__PURE__ */ v(a, {
|
|
145
145
|
title: S("devportal.oauthApps", "OAuth Apps"),
|
|
146
146
|
description: S("devportal.manageOAuthApps", "Manage your OAuth applications and credentials"),
|
|
147
147
|
actions: /* @__PURE__ */ v("button", {
|
|
@@ -264,7 +264,7 @@ var y = () => {
|
|
|
264
264
|
N.length === 1 ? S("devportal.appFound", "app found") : S("devportal.appsFound", "apps found")
|
|
265
265
|
]
|
|
266
266
|
}),
|
|
267
|
-
N.length > 0 ? /* @__PURE__ */ _(
|
|
267
|
+
N.length > 0 ? /* @__PURE__ */ _(s, {
|
|
268
268
|
data: N,
|
|
269
269
|
columns: P,
|
|
270
270
|
keyField: "id",
|