@burdenoff/microfe-devportal 2026.609.3 → 2026.611.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.
Files changed (58) hide show
  1. package/dist/devportal/DevportalRoot.js +6 -1
  2. package/dist/devportal/DevportalRoot.js.map +1 -1
  3. package/dist/devportal/DevportalRoutes.js +169 -158
  4. package/dist/devportal/DevportalRoutes.js.map +1 -1
  5. package/dist/devportal/hooks/usePackageUpload.js +120 -0
  6. package/dist/devportal/hooks/usePackageUpload.js.map +1 -0
  7. package/dist/devportal/pages/AnalyticsDashboardPage.js +73 -62
  8. package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
  9. package/dist/devportal/pages/AppLogsPage.js +89 -82
  10. package/dist/devportal/pages/AppLogsPage.js.map +1 -1
  11. package/dist/devportal/pages/ApplicationDetailPage.js +948 -367
  12. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  13. package/dist/devportal/pages/ApplicationVersionsPage.js +56 -45
  14. package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
  15. package/dist/devportal/pages/CreateApiKeyPage.js +30 -32
  16. package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
  17. package/dist/devportal/pages/CreateApplicationPage.js +8 -3
  18. package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
  19. package/dist/devportal/pages/CreateOAuthClientPage.js +4 -4
  20. package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
  21. package/dist/devportal/pages/CreateOrganizationPage.js +233 -228
  22. package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
  23. package/dist/devportal/pages/CreateWebhookPage.js +159 -148
  24. package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
  25. package/dist/devportal/pages/DelegationsPage.js +1 -1
  26. package/dist/devportal/pages/DelegationsPage.js.map +1 -1
  27. package/dist/devportal/pages/EditApplicationPage.js +25 -17
  28. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  29. package/dist/devportal/pages/ListingPreviewPage.js +159 -153
  30. package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
  31. package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
  32. package/dist/devportal/pages/OAuthClientsPage.js +2 -2
  33. package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
  34. package/dist/devportal/pages/OrganizationDetailPage.js +158 -153
  35. package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
  36. package/dist/devportal/pages/OrganizationMembersPage.js +223 -212
  37. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  38. package/dist/devportal/pages/PublishPage.js +172 -165
  39. package/dist/devportal/pages/PublishPage.js.map +1 -1
  40. package/dist/devportal/pages/ResourcesPage.js.map +1 -1
  41. package/dist/devportal/pages/RevenueDashboardPage.js +405 -0
  42. package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -0
  43. package/dist/devportal/pages/SetupPage.js +170 -217
  44. package/dist/devportal/pages/SetupPage.js.map +1 -1
  45. package/dist/devportal/pages/StoreSubmissionPage.js +76 -97
  46. package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
  47. package/dist/devportal/pages/WebhookDetailPage.js +172 -154
  48. package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
  49. package/dist/devportal/pages/WebhooksPage.js +218 -200
  50. package/dist/devportal/pages/WebhooksPage.js.map +1 -1
  51. package/dist/devportal/providers/DevportalProvider.js +16 -13
  52. package/dist/devportal/providers/DevportalProvider.js.map +1 -1
  53. package/dist/devportal/types/index.js.map +1 -1
  54. package/dist/generated/global-operations.js +310 -225
  55. package/dist/generated/global-operations.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/devportal/pages/RevenueRedirectPage.js +0 -14
  58. package/dist/devportal/pages/RevenueRedirectPage.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"DelegationsPage.js","names":[],"sources":["../../../src/devportal/pages/DelegationsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Shield,\n Users,\n Clock,\n XCircle,\n AlertTriangle,\n Loader2,\n AlertCircle,\n Bot,\n Zap,\n Cog,\n} from \"lucide-react\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { LoadingState } from \"../components/LoadingState\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { cn } from \"../utils/cn\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport type { AppUserDelegation, DelegationStatus } from \"../types\";\nimport {\n useCurrentUserDelegationsQuery,\n useCurrentUserGrantedDelegationsQuery,\n useRevokeDelegationMutation,\n} from \"../../generated/global-operations\";\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\ntype ActiveTab = \"acting-as-me\" | \"granted-by-me\";\n\n/**\n * Map a GraphQL delegation fragment to the local AppUserDelegation type\n */\nfunction mapDelegation(d: {\n id: string;\n appId: string;\n userId: string;\n scopes: string[];\n workspaceIds?: string[] | null;\n status: string;\n grantedAt: string;\n grantedBy: string;\n expiresAt?: string | null;\n revokedAt?: string | null;\n revokedBy?: string | null;\n app: {\n id: string;\n name: string;\n description?: string | null;\n type: string;\n status: string;\n };\n}): AppUserDelegation {\n return {\n id: d.id,\n appId: d.appId,\n userId: d.userId,\n scopes: d.scopes,\n workspaceIds: d.workspaceIds ?? [],\n status: d.status as DelegationStatus,\n grantedAt: d.grantedAt,\n grantedBy: d.grantedBy,\n expiresAt: d.expiresAt ?? undefined,\n revokedAt: d.revokedAt ?? undefined,\n revokedBy: d.revokedBy ?? undefined,\n app: {\n id: d.app.id,\n name: d.app.name,\n description: d.app.description ?? undefined,\n type: d.app.type,\n status: d.app.status,\n },\n };\n}\n\n/**\n * Format a date string to relative time (\"2 hours ago\", \"3 days ago\", etc.)\n */\nfunction formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n const diffWeeks = Math.floor(diffDays / 7);\n const diffMonths = Math.floor(diffDays / 30);\n\n if (diffSeconds < 60) return \"just now\";\n if (diffMinutes < 60)\n return `${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"} ago`;\n if (diffHours < 24)\n return `${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"} ago`;\n if (diffDays < 7) return `${diffDays} day${diffDays !== 1 ? \"s\" : \"\"} ago`;\n if (diffWeeks < 5)\n return `${diffWeeks} week${diffWeeks !== 1 ? \"s\" : \"\"} ago`;\n if (diffMonths < 12)\n return `${diffMonths} month${diffMonths !== 1 ? \"s\" : \"\"} ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Format a future date as relative time (\"in 3 days\", \"in 2 hours\", etc.)\n */\nfunction formatExpirationTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = date.getTime() - now.getTime();\n\n if (diffMs <= 0) return \"expired\";\n\n const diffMinutes = Math.floor(diffMs / (1000 * 60));\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffMinutes < 60)\n return `in ${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"}`;\n if (diffHours < 24)\n return `in ${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"}`;\n return `in ${diffDays} day${diffDays !== 1 ? \"s\" : \"\"}`;\n}\n\n// =============================================================================\n// SUB-COMPONENTS\n// =============================================================================\n\n/**\n * Icon for app type (SERVICE, BOT, INTEGRATION, etc.)\n */\nconst AppTypeIcon: FC<{ type: string; className?: string }> = ({\n type,\n className,\n}) => {\n const iconProps = { className: cn(\"h-5 w-5\", className) };\n\n switch (type) {\n case \"SERVICE\":\n return <Cog {...iconProps} />;\n case \"BOT\":\n return <Bot {...iconProps} />;\n case \"INTEGRATION\":\n return <Zap {...iconProps} />;\n default:\n return <Shield {...iconProps} />;\n }\n};\n\n/**\n * Delegation status badge with color coding\n */\nconst DelegationStatusBadge: FC<{ status: DelegationStatus }> = ({\n status,\n}) => {\n const styles: Record<DelegationStatus, string> = {\n ACTIVE: \"bg-status-success-bg-subtle text-status-success-text\",\n REVOKED: \"bg-status-error-bg-subtle text-status-error-text\",\n EXPIRED: \"bg-bg-sunken text-text-primary\",\n };\n\n return (\n <span\n className={cn(\n \"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium\",\n styles[status],\n )}\n >\n {status}\n </span>\n );\n};\n\n/**\n * Success toast that auto-dismisses\n */\nconst SuccessToast: FC<{ message: string; onDismiss: () => void }> = ({\n message,\n onDismiss,\n}) => {\n useEffect(() => {\n const timer = setTimeout(onDismiss, 5000);\n return () => clearTimeout(timer);\n }, [onDismiss]);\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-4\">\n <div className=\"flex items-center gap-3\">\n <Shield className=\"h-5 w-5 text-status-success-text\" />\n <p className=\"text-sm font-medium text-status-success-text\">\n {message}\n </p>\n </div>\n </div>\n );\n};\n\n/**\n * Error banner\n */\nconst ErrorBanner: FC<{ message: string; onRetry?: () => void }> = ({\n message,\n onRetry,\n}) => {\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 return (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-4\">\n <div className=\"flex items-center gap-3\">\n <AlertCircle className=\"h-5 w-5 text-destructive\" />\n <p className=\"flex-1 text-sm text-destructive\">{message}</p>\n {onRetry && (\n <button\n onClick={onRetry}\n className=\"rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted transition-colors\"\n >\n {tr(\"devportal.retry\", \"Retry\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Single delegation card for \"Apps Acting as Me\" tab\n */\nconst DelegationCard: FC<{\n delegation: AppUserDelegation;\n onRevoke: (delegation: AppUserDelegation) => void;\n isRevoking: boolean;\n selected: boolean;\n onToggleSelect: (checked: boolean) => void;\n canRevoke: boolean;\n}> = ({\n delegation,\n onRevoke,\n isRevoking,\n selected,\n onToggleSelect,\n canRevoke,\n}) => {\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 return (\n <div\n className={cn(\n \"rounded-lg border bg-card p-5 transition-colors hover:border-border/80\",\n selected && \"border-primary bg-primary/5\",\n delegation.status !== \"ACTIVE\" && \"opacity-60\",\n )}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Left: App info */}\n <div className=\"flex items-start gap-4 min-w-0\">\n {delegation.status === \"ACTIVE\" && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={tr(\n \"devportal.selectDelegationAriaLabel\",\n `Select delegation ${delegation.app?.name ?? delegation.id}`,\n )}\n className=\"mt-1\"\n />\n </div>\n )}\n <div\n className={cn(\n \"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg\",\n delegation.status === \"ACTIVE\"\n ? \"bg-primary/10 text-primary\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-6 w-6\"\n />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n {/* Name + status */}\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate font-semibold\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n\n {/* Description */}\n {delegation.app?.description && (\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {delegation.app.description}\n </p>\n )}\n\n {/* Metadata row */}\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.granted\", \"Granted\")}{\" \"}\n {formatRelativeTime(delegation.grantedAt)}\n </span>\n {delegation.expiresAt && (\n <span className=\"flex items-center gap-1\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.expires\", \"Expires\")}{\" \"}\n {formatExpirationTime(delegation.expiresAt)}\n </span>\n )}\n <span\n className={cn(\n \"rounded-md px-2 py-0.5 text-xs font-mono\",\n \"bg-muted text-muted-foreground\",\n )}\n >\n {delegation.app?.type ?? \"UNKNOWN\"}\n </span>\n </div>\n\n {/* Scopes */}\n {delegation.scopes.length > 0 && (\n <div>\n <p className=\"mb-1.5 text-xs text-muted-foreground\">\n {tr(\"devportal.permissions\", \"Permissions:\")}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {delegation.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-md bg-status-info-bg-subtle px-2 py-0.5 text-xs text-status-info-text\"\n >\n {scope}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Workspace count */}\n {delegation.workspaceIds.length > 0 && (\n <p className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Users className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.accessToWorkspaces\", \"Access to\")}{\" \"}\n {delegation.workspaceIds.length}{\" \"}\n {tr(\"devportal.workspace\", \"workspace\")}\n {delegation.workspaceIds.length !== 1\n ? tr(\"devportal.workspacePluralSuffix\", \"s\")\n : \"\"}\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Revoke button (only for active delegations) */}\n {delegation.status === \"ACTIVE\" && (\n <button\n onClick={() => onRevoke(delegation)}\n disabled={isRevoking || !canRevoke}\n aria-disabled={!canRevoke}\n className=\"inline-flex shrink-0 items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isRevoking ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n {tr(\"devportal.revoke\", \"Revoke\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Single delegation card for \"Delegations I Granted\" tab\n */\nconst GrantedDelegationCard: FC<{\n delegation: AppUserDelegation;\n onRevoke: (delegation: AppUserDelegation) => void;\n isRevoking: boolean;\n selected: boolean;\n onToggleSelect: (checked: boolean) => void;\n canRevoke: boolean;\n}> = ({\n delegation,\n onRevoke,\n isRevoking,\n selected,\n onToggleSelect,\n canRevoke,\n}) => {\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 return (\n <div\n className={cn(\n \"rounded-lg border bg-card p-5 transition-colors hover:border-border/80\",\n selected && \"border-primary bg-primary/5\",\n )}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Left: App info */}\n <div className=\"flex items-start gap-4 min-w-0\">\n {delegation.status === \"ACTIVE\" && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={tr(\n \"devportal.selectDelegationAriaLabel\",\n `Select delegation ${delegation.app?.name ?? delegation.id}`,\n )}\n className=\"mt-1\"\n />\n </div>\n )}\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-accent-purple\">\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-6 w-6\"\n />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n {/* Name + status */}\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate font-semibold\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n\n {/* Target user */}\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"devportal.grantedToUser\", \"Granted to user:\")}{\" \"}\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">\n {delegation.userId}\n </code>\n </p>\n\n {/* Time */}\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.granted\", \"Granted\")}{\" \"}\n {formatRelativeTime(delegation.grantedAt)}\n </span>\n {delegation.expiresAt && (\n <span className=\"flex items-center gap-1\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.expires\", \"Expires\")}{\" \"}\n {new Date(delegation.expiresAt).toLocaleDateString()}\n </span>\n )}\n </div>\n\n {/* Scopes */}\n {delegation.scopes.length > 0 && (\n <div>\n <p className=\"mb-1.5 text-xs text-muted-foreground\">\n {tr(\"devportal.permissionsGranted\", \"Permissions granted:\")}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {delegation.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-md bg-accent-purple-subtle px-2 py-0.5 text-xs text-accent-purple\"\n >\n {scope}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right: Revoke button */}\n {delegation.status === \"ACTIVE\" && (\n <button\n onClick={() => onRevoke(delegation)}\n disabled={isRevoking || !canRevoke}\n aria-disabled={!canRevoke}\n className=\"inline-flex shrink-0 items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isRevoking ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n {tr(\"devportal.revoke\", \"Revoke\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Revocation history section (dimmed, for revoked/expired delegations)\n */\nconst RevocationHistory: FC<{ delegations: AppUserDelegation[] }> = ({\n delegations,\n}) => {\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\n if (delegations.length === 0) return null;\n\n return (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">\n {tr(\"devportal.revocationHistory\", \"Revocation History\")}\n </h2>\n <div className=\"space-y-3 opacity-60\">\n {delegations.map((delegation) => (\n <div\n key={delegation.id}\n className=\"flex items-center justify-between rounded-lg border bg-muted/30 p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-muted text-muted-foreground\">\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-5 w-5\"\n />\n </div>\n <div>\n <h3 className=\"font-medium\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n {delegation.status === \"REVOKED\"\n ? tr(\"devportal.revoked\", \"Revoked\")\n : tr(\"devportal.expired\", \"Expired\")}{\" \"}\n {delegation.revokedAt\n ? formatRelativeTime(delegation.revokedAt)\n : \"\"}\n </p>\n </div>\n </div>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n ))}\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * Delegations management page for the Developer Portal.\n *\n * Consolidates delegation management previously at /auth/delegations in\n * microfe-auth. Shows two tabs:\n * - \"Apps Acting as Me\": delegations where the current user is the subject\n * - \"Delegations I Granted\": delegations the current user granted to others\n */\nexport const DelegationsPage: 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: canRevoke } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.DELEGATION_REVOKE,\n );\n // ---------------------------------------------------------------------------\n // State\n // ---------------------------------------------------------------------------\n const [activeTab, setActiveTab] = useState<ActiveTab>(\"acting-as-me\");\n const [revokeTargets, setRevokeTargets] = useState<AppUserDelegation[]>([]);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [selectedDelegationIds, setSelectedDelegationIds] = useState<\n Set<string>\n >(new Set());\n\n // ---------------------------------------------------------------------------\n // Queries\n // ---------------------------------------------------------------------------\n const {\n data: delegationsData,\n loading: delegationsLoading,\n error: delegationsError,\n refetch: refetchDelegations,\n } = useCurrentUserDelegationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const {\n data: grantedData,\n loading: grantedLoading,\n error: grantedError,\n refetch: refetchGranted,\n } = useCurrentUserGrantedDelegationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n // ---------------------------------------------------------------------------\n // Mutation\n // ---------------------------------------------------------------------------\n const [revokeDelegationMutation, { loading: revokeLoading }] =\n useRevokeDelegationMutation();\n\n // ---------------------------------------------------------------------------\n // Derived data\n // ---------------------------------------------------------------------------\n const delegations: AppUserDelegation[] = (\n delegationsData?.currentUserDelegations ?? []\n ).map(mapDelegation);\n\n const grantedDelegations: AppUserDelegation[] = (\n grantedData?.currentUserGrantedDelegations ?? []\n ).map(mapDelegation);\n\n const activeDelegations = delegations.filter((d) => d.status === \"ACTIVE\");\n const inactiveDelegations = delegations.filter((d) => d.status !== \"ACTIVE\");\n const activeGrantedDelegations = grantedDelegations.filter(\n (d) => d.status === \"ACTIVE\",\n );\n const visibleActiveDelegations =\n activeTab === \"acting-as-me\" ? activeDelegations : activeGrantedDelegations;\n const selectedActiveDelegations = useMemo(\n () =>\n visibleActiveDelegations.filter((delegation) =>\n selectedDelegationIds.has(delegation.id),\n ),\n [selectedDelegationIds, visibleActiveDelegations],\n );\n const allVisibleSelected =\n visibleActiveDelegations.length > 0 &&\n visibleActiveDelegations.every((delegation) =>\n selectedDelegationIds.has(delegation.id),\n );\n const partiallySelected =\n visibleActiveDelegations.some((delegation) =>\n selectedDelegationIds.has(delegation.id),\n ) && !allVisibleSelected;\n\n const isLoading =\n activeTab === \"acting-as-me\" ? delegationsLoading : grantedLoading;\n const queryError =\n activeTab === \"acting-as-me\" ? delegationsError : grantedError;\n\n // ---------------------------------------------------------------------------\n // Handlers\n // ---------------------------------------------------------------------------\n const handleRevokeClick = useCallback((delegation: AppUserDelegation) => {\n setRevokeTargets([delegation]);\n }, []);\n\n const toggleDelegationSelection = useCallback(\n (delegationId: string, checked: boolean) => {\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(delegationId);\n } else {\n next.delete(delegationId);\n }\n return next;\n });\n },\n [],\n );\n\n const toggleAllVisibleDelegations = useCallback(\n (checked: boolean) => {\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n visibleActiveDelegations.forEach((delegation) => {\n if (checked) {\n next.add(delegation.id);\n } else {\n next.delete(delegation.id);\n }\n });\n return next;\n });\n },\n [visibleActiveDelegations],\n );\n\n const clearSelection = useCallback(() => {\n setSelectedDelegationIds(new Set());\n }, []);\n\n const handleBulkRevokeClick = useCallback(() => {\n if (selectedActiveDelegations.length === 0) return;\n setRevokeTargets(selectedActiveDelegations);\n }, [selectedActiveDelegations]);\n\n const handleRevokeConfirm = useCallback(async () => {\n if (revokeTargets.length === 0) return;\n try {\n const results = await Promise.allSettled(\n revokeTargets.map((delegation) =>\n revokeDelegationMutation({\n variables: {\n id: delegation.id,\n reason: tr(\n \"devportal.userRevokedAccessReason\",\n \"User revoked access via Developer Portal\",\n ),\n },\n }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) =>\n result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setSuccessMessage(\n `Revoked ${succeeded} delegation${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n revokeTargets.forEach((delegation) => next.delete(delegation.id));\n return next;\n });\n setRevokeTargets([]);\n // Refetch both queries to keep data fresh\n void refetchDelegations();\n void refetchGranted();\n } catch {\n // Error is available via the mutation hook's error state;\n // the ConfirmDialog will close and we show the error via queryError fallback\n setRevokeTargets([]);\n }\n }, [\n revokeTargets,\n revokeDelegationMutation,\n refetchDelegations,\n refetchGranted,\n ]);\n\n const handleDismissSuccess = useCallback(() => {\n setSuccessMessage(null);\n }, []);\n\n // ---------------------------------------------------------------------------\n // Summary stats for header\n // ---------------------------------------------------------------------------\n const totalActive = activeDelegations.length;\n const totalGranted = activeGrantedDelegations.length;\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.INSTALLATION_READ}>\n <PageLayout\n title={tr(\"devportal.appDelegationsTitle\", \"App Delegations\")}\n description={tr(\n \"devportal.appDelegationsDescription\",\n \"Manage which apps can act on your behalf. Revoke access at any time.\",\n )}\n >\n {/* Success message */}\n {successMessage && (\n <SuccessToast\n message={successMessage}\n onDismiss={handleDismissSuccess}\n />\n )}\n\n {/* Error message */}\n {queryError && (\n <ErrorBanner\n message={\n queryError.message ||\n tr(\n \"devportal.failedToLoadDelegations\",\n \"Failed to load delegations\",\n )\n }\n onRetry={() => {\n void refetchDelegations();\n void refetchGranted();\n }}\n />\n )}\n\n {/* Summary stats */}\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Shield className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.activeDelegations\", \"Active Delegations\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalActive}</p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Users className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.grantedByMe\", \"Granted by Me\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalGranted}</p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-status-warning-text\">\n <AlertTriangle className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.revokedExpired\", \"Revoked / Expired\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">\n {inactiveDelegations.length}\n </p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Clock className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.totalAllTime\", \"Total All-Time\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{delegations.length}</p>\n </div>\n </div>\n\n {/* Tabs */}\n <div className=\"border-b\">\n <div\n className=\"flex overflow-x-auto\"\n role=\"tablist\"\n aria-label={tr(\n \"devportal.delegationSections\",\n \"Delegation sections\",\n )}\n >\n <button\n id=\"delegations-tab-acting-as-me\"\n role=\"tab\"\n aria-selected={activeTab === \"acting-as-me\"}\n onClick={() => {\n clearSelection();\n setActiveTab(\"acting-as-me\");\n }}\n className={cn(\n \"-mb-px border-b-2 px-4 py-3 text-sm font-medium transition-colors\",\n activeTab === \"acting-as-me\"\n ? \"border-primary text-primary\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {tr(\"devportal.appsActingAsMe\", \"Apps Acting as Me\")}\n {totalActive > 0 && (\n <span className=\"ml-2 inline-flex items-center justify-center rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {totalActive}\n </span>\n )}\n </button>\n <button\n id=\"delegations-tab-granted-by-me\"\n role=\"tab\"\n aria-selected={activeTab === \"granted-by-me\"}\n onClick={() => {\n clearSelection();\n setActiveTab(\"granted-by-me\");\n }}\n className={cn(\n \"-mb-px border-b-2 px-4 py-3 text-sm font-medium transition-colors\",\n activeTab === \"granted-by-me\"\n ? \"border-primary text-primary\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {tr(\"devportal.delegationsIGranted\", \"Delegations I Granted\")}\n {totalGranted > 0 && (\n <span className=\"ml-2 inline-flex items-center justify-center rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {totalGranted}\n </span>\n )}\n </button>\n </div>\n </div>\n\n {visibleActiveDelegations.length > 0 && (\n <div className=\"rounded-lg border bg-card 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={toggleAllVisibleDelegations}\n ariaLabel={tr(\n \"devportal.selectAllVisibleDelegations\",\n \"Select all visible delegations\",\n )}\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedDelegationIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\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-muted px-3 py-1 text-sm font-medium text-muted-foreground\">\n {selectedDelegationIds.size}{\" \"}\n {tr(\"devportal.selected\", \"selected\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalActive}</p>\n </div>\n </div>\n )}\n\n {/* Loading */}\n {isLoading && !delegationsData && !grantedData && (\n <LoadingState\n message={tr(\n \"devportal.loadingDelegations\",\n \"Loading delegations...\",\n )}\n />\n )}\n\n {/* ------------------------------------------------------------------- */}\n {/* Tab 1: Apps Acting as Me */}\n {/* ------------------------------------------------------------------- */}\n {!isLoading && activeTab === \"acting-as-me\" && (\n <div\n className=\"space-y-8\"\n id=\"delegations-panel-acting-as-me\"\n role=\"tabpanel\"\n aria-labelledby=\"delegations-tab-acting-as-me\"\n tabIndex={0}\n >\n {activeDelegations.length === 0 &&\n inactiveDelegations.length === 0 ? (\n <EmptyState\n icon={<Shield size={32} />}\n title={tr(\n \"devportal.noActiveDelegations\",\n \"No Active Delegations\",\n )}\n description={tr(\n \"devportal.noAppsAuthorizedOnYourBehalf\",\n \"No apps are currently authorized to act on your behalf.\",\n )}\n />\n ) : (\n <>\n {/* Active delegation cards */}\n {activeDelegations.length === 0 ? (\n <EmptyState\n icon={<Shield size={32} />}\n title={tr(\n \"devportal.noActiveDelegations\",\n \"No Active Delegations\",\n )}\n description={tr(\n \"devportal.allPreviousDelegationsRevokedOrExpired\",\n \"All previous delegations have been revoked or expired.\",\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {activeDelegations.map((delegation) => (\n <DelegationCard\n key={delegation.id}\n delegation={delegation}\n onRevoke={handleRevokeClick}\n isRevoking={\n revokeLoading &&\n revokeTargets.some(\n (item) => item.id === delegation.id,\n )\n }\n selected={selectedDelegationIds.has(delegation.id)}\n onToggleSelect={(checked) =>\n toggleDelegationSelection(delegation.id, checked)\n }\n canRevoke={canRevoke}\n />\n ))}\n </div>\n )}\n\n {/* Revocation history */}\n <RevocationHistory delegations={inactiveDelegations} />\n </>\n )}\n </div>\n )}\n\n {/* ------------------------------------------------------------------- */}\n {/* Tab 2: Delegations I Granted */}\n {/* ------------------------------------------------------------------- */}\n {!isLoading && activeTab === \"granted-by-me\" && (\n <div\n className=\"space-y-4\"\n id=\"delegations-panel-granted-by-me\"\n role=\"tabpanel\"\n aria-labelledby=\"delegations-tab-granted-by-me\"\n tabIndex={0}\n >\n {grantedDelegations.length === 0 ? (\n <EmptyState\n icon={<Users size={32} />}\n title={tr(\n \"devportal.noDelegationsGranted\",\n \"No Delegations Granted\",\n )}\n description={tr(\n \"devportal.noGrantedDelegationsDescription\",\n \"You haven't granted any app delegations to other users.\",\n )}\n />\n ) : (\n grantedDelegations.map((delegation) => (\n <GrantedDelegationCard\n key={delegation.id}\n delegation={delegation}\n onRevoke={handleRevokeClick}\n isRevoking={\n revokeLoading &&\n revokeTargets.some((item) => item.id === delegation.id)\n }\n selected={selectedDelegationIds.has(delegation.id)}\n onToggleSelect={(checked) =>\n toggleDelegationSelection(delegation.id, checked)\n }\n canRevoke={canRevoke}\n />\n ))\n )}\n </div>\n )}\n\n {/* Info card */}\n <div className=\"rounded-lg border border-border-subtle bg-status-info-bg-subtle p-5\">\n <div className=\"flex gap-4\">\n <AlertCircle className=\"h-6 w-6 shrink-0 text-accent-blue\" />\n <div>\n <h3 className=\"font-semibold text-status-info-text\">\n {tr(\"devportal.aboutAppDelegations\", \"About App Delegations\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {tr(\n \"devportal.aboutAppDelegationsText\",\n \"App delegations allow automated services, bots, and integrations to perform actions on your behalf. Each delegation specifies which permissions (scopes) and workspaces the app can access. You can revoke access at any time, which immediately prevents the app from acting as you.\",\n )}\n </p>\n </div>\n </div>\n </div>\n\n {/* Revoke confirmation dialog */}\n <ConfirmDialog\n open={revokeTargets.length > 0}\n onOpenChange={(open) => {\n if (!open) setRevokeTargets([]);\n }}\n onConfirm={handleRevokeConfirm}\n title={\n revokeTargets.length > 1\n ? tr(\"devportal.revokeDelegations\", \"Revoke Delegations\")\n : tr(\"devportal.revokeDelegation\", \"Revoke Delegation\")\n }\n description={\n revokeTargets.length > 1\n ? tr(\n \"devportal.revokeDelegationsBulkConfirm\",\n `Are you sure you want to revoke ${revokeTargets.length} selected delegations? Those apps will no longer be able to act on the delegated users' behalf.`,\n )\n : tr(\n \"devportal.revokeDelegationSingleConfirm\",\n `Are you sure you want to revoke access for \"${revokeTargets[0]?.app?.name ?? \"this app\"}\"? This app will no longer be able to act on your behalf.`,\n )\n }\n confirmLabel={tr(\"devportal.revokeAccess\", \"Revoke Access\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"danger\"\n loading={revokeLoading}\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"DelegationsPage.js","names":[],"sources":["../../../src/devportal/pages/DelegationsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState, useCallback, useEffect, useMemo } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Shield,\n Users,\n Clock,\n XCircle,\n AlertTriangle,\n Loader2,\n AlertCircle,\n Bot,\n Zap,\n Cog,\n} from \"lucide-react\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { LoadingState } from \"../components/LoadingState\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { cn } from \"../utils/cn\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\nimport type { AppUserDelegation, DelegationStatus } from \"../types\";\nimport {\n useCurrentUserDelegationsQuery,\n useCurrentUserGrantedDelegationsQuery,\n useRevokeDelegationMutation,\n} from \"../../generated/global-operations\";\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\ntype ActiveTab = \"acting-as-me\" | \"granted-by-me\";\n\n/**\n * Map a GraphQL delegation fragment to the local AppUserDelegation type\n */\nfunction mapDelegation(d: {\n id: string;\n appId: string;\n userId: string;\n scopes: string[];\n workspaceIds?: string[] | null;\n status: string;\n grantedAt: string;\n grantedBy: string;\n expiresAt?: string | null;\n revokedAt?: string | null;\n revokedBy?: string | null;\n app: {\n id: string;\n name: string;\n description?: string | null;\n type: string;\n status: string;\n };\n}): AppUserDelegation {\n return {\n id: d.id,\n appId: d.appId,\n userId: d.userId,\n scopes: d.scopes,\n workspaceIds: d.workspaceIds ?? [],\n status: d.status as DelegationStatus,\n grantedAt: d.grantedAt,\n grantedBy: d.grantedBy,\n expiresAt: d.expiresAt ?? undefined,\n revokedAt: d.revokedAt ?? undefined,\n revokedBy: d.revokedBy ?? undefined,\n app: {\n id: d.app.id,\n name: d.app.name,\n description: d.app.description ?? undefined,\n type: d.app.type,\n status: d.app.status,\n },\n };\n}\n\n/**\n * Format a date string to relative time (\"2 hours ago\", \"3 days ago\", etc.)\n */\nfunction formatRelativeTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n const diffWeeks = Math.floor(diffDays / 7);\n const diffMonths = Math.floor(diffDays / 30);\n\n if (diffSeconds < 60) return \"just now\";\n if (diffMinutes < 60)\n return `${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"} ago`;\n if (diffHours < 24)\n return `${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"} ago`;\n if (diffDays < 7) return `${diffDays} day${diffDays !== 1 ? \"s\" : \"\"} ago`;\n if (diffWeeks < 5)\n return `${diffWeeks} week${diffWeeks !== 1 ? \"s\" : \"\"} ago`;\n if (diffMonths < 12)\n return `${diffMonths} month${diffMonths !== 1 ? \"s\" : \"\"} ago`;\n\n return date.toLocaleDateString();\n}\n\n/**\n * Format a future date as relative time (\"in 3 days\", \"in 2 hours\", etc.)\n */\nfunction formatExpirationTime(dateString: string): string {\n const date = new Date(dateString);\n const now = new Date();\n const diffMs = date.getTime() - now.getTime();\n\n if (diffMs <= 0) return \"expired\";\n\n const diffMinutes = Math.floor(diffMs / (1000 * 60));\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffMinutes < 60)\n return `in ${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"}`;\n if (diffHours < 24)\n return `in ${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"}`;\n return `in ${diffDays} day${diffDays !== 1 ? \"s\" : \"\"}`;\n}\n\n// =============================================================================\n// SUB-COMPONENTS\n// =============================================================================\n\n/**\n * Icon for app type (SERVICE, BOT, INTEGRATION, etc.)\n */\nconst AppTypeIcon: FC<{ type: string; className?: string }> = ({\n type,\n className,\n}) => {\n const iconProps = { className: cn(\"h-5 w-5\", className) };\n\n switch (type) {\n case \"SERVICE\":\n return <Cog {...iconProps} />;\n case \"BOT\":\n return <Bot {...iconProps} />;\n case \"INTEGRATION\":\n return <Zap {...iconProps} />;\n default:\n return <Shield {...iconProps} />;\n }\n};\n\n/**\n * Delegation status badge with color coding\n */\nconst DelegationStatusBadge: FC<{ status: DelegationStatus }> = ({\n status,\n}) => {\n const styles: Record<DelegationStatus, string> = {\n ACTIVE: \"bg-status-success-bg-subtle text-status-success-text\",\n REVOKED: \"bg-status-error-bg-subtle text-status-error-text\",\n EXPIRED: \"bg-bg-sunken text-text-primary\",\n };\n\n return (\n <span\n className={cn(\n \"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium\",\n styles[status],\n )}\n >\n {status}\n </span>\n );\n};\n\n/**\n * Success toast that auto-dismisses\n */\nconst SuccessToast: FC<{ message: string; onDismiss: () => void }> = ({\n message,\n onDismiss,\n}) => {\n useEffect(() => {\n const timer = setTimeout(onDismiss, 5000);\n return () => clearTimeout(timer);\n }, [onDismiss]);\n\n return (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-4\">\n <div className=\"flex items-center gap-3\">\n <Shield className=\"h-5 w-5 text-status-success-text\" />\n <p className=\"text-sm font-medium text-status-success-text\">\n {message}\n </p>\n </div>\n </div>\n );\n};\n\n/**\n * Error banner\n */\nconst ErrorBanner: FC<{ message: string; onRetry?: () => void }> = ({\n message,\n onRetry,\n}) => {\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 return (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-4\">\n <div className=\"flex items-center gap-3\">\n <AlertCircle className=\"h-5 w-5 text-destructive\" />\n <p className=\"flex-1 text-sm text-destructive\">{message}</p>\n {onRetry && (\n <button\n onClick={onRetry}\n className=\"rounded-md border px-3 py-1.5 text-xs font-medium hover:bg-muted transition-colors\"\n >\n {tr(\"devportal.retry\", \"Retry\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Single delegation card for \"Apps Acting as Me\" tab\n */\nconst DelegationCard: FC<{\n delegation: AppUserDelegation;\n onRevoke: (delegation: AppUserDelegation) => void;\n isRevoking: boolean;\n selected: boolean;\n onToggleSelect: (checked: boolean) => void;\n canRevoke: boolean;\n}> = ({\n delegation,\n onRevoke,\n isRevoking,\n selected,\n onToggleSelect,\n canRevoke,\n}) => {\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 return (\n <div\n className={cn(\n \"rounded-lg border bg-card p-5 transition-colors hover:border-border/80\",\n selected && \"border-primary bg-primary/5\",\n delegation.status !== \"ACTIVE\" && \"opacity-60\",\n )}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Left: App info */}\n <div className=\"flex items-start gap-4 min-w-0\">\n {delegation.status === \"ACTIVE\" && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={tr(\n \"devportal.selectDelegationAriaLabel\",\n `Select delegation ${delegation.app?.name ?? delegation.id}`,\n )}\n className=\"mt-1\"\n />\n </div>\n )}\n <div\n className={cn(\n \"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg\",\n delegation.status === \"ACTIVE\"\n ? \"bg-primary/10 text-primary\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-6 w-6\"\n />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n {/* Name + status */}\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate font-semibold\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n\n {/* Description */}\n {delegation.app?.description && (\n <p className=\"text-sm text-muted-foreground line-clamp-2\">\n {delegation.app.description}\n </p>\n )}\n\n {/* Metadata row */}\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.granted\", \"Granted\")}{\" \"}\n {formatRelativeTime(delegation.grantedAt)}\n </span>\n {delegation.expiresAt && (\n <span className=\"flex items-center gap-1\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.expires\", \"Expires\")}{\" \"}\n {formatExpirationTime(delegation.expiresAt)}\n </span>\n )}\n <span\n className={cn(\n \"rounded-md px-2 py-0.5 text-xs font-mono\",\n \"bg-muted text-muted-foreground\",\n )}\n >\n {delegation.app?.type ?? \"UNKNOWN\"}\n </span>\n </div>\n\n {/* Scopes */}\n {delegation.scopes.length > 0 && (\n <div>\n <p className=\"mb-1.5 text-xs text-muted-foreground\">\n {tr(\"devportal.permissions\", \"Permissions:\")}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {delegation.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-md bg-status-info-bg-subtle px-2 py-0.5 text-xs text-status-info-text\"\n >\n {scope}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Workspace count */}\n {delegation.workspaceIds.length > 0 && (\n <p className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n <Users className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.accessToWorkspaces\", \"Access to\")}{\" \"}\n {delegation.workspaceIds.length}{\" \"}\n {tr(\"devportal.workspace\", \"workspace\")}\n {delegation.workspaceIds.length !== 1\n ? tr(\"devportal.workspacePluralSuffix\", \"s\")\n : \"\"}\n </p>\n )}\n </div>\n </div>\n\n {/* Right: Revoke button (only for active delegations) */}\n {delegation.status === \"ACTIVE\" && (\n <button\n onClick={() => onRevoke(delegation)}\n disabled={isRevoking || !canRevoke}\n aria-disabled={!canRevoke}\n className=\"inline-flex shrink-0 items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isRevoking ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n {tr(\"devportal.revoke\", \"Revoke\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Single delegation card for \"Delegations I Granted\" tab\n */\nconst GrantedDelegationCard: FC<{\n delegation: AppUserDelegation;\n onRevoke: (delegation: AppUserDelegation) => void;\n isRevoking: boolean;\n selected: boolean;\n onToggleSelect: (checked: boolean) => void;\n canRevoke: boolean;\n}> = ({\n delegation,\n onRevoke,\n isRevoking,\n selected,\n onToggleSelect,\n canRevoke,\n}) => {\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 return (\n <div\n className={cn(\n \"rounded-lg border bg-card p-5 transition-colors hover:border-border/80\",\n selected && \"border-primary bg-primary/5\",\n )}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Left: App info */}\n <div className=\"flex items-start gap-4 min-w-0\">\n {delegation.status === \"ACTIVE\" && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={tr(\n \"devportal.selectDelegationAriaLabel\",\n `Select delegation ${delegation.app?.name ?? delegation.id}`,\n )}\n className=\"mt-1\"\n />\n </div>\n )}\n <div className=\"flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-accent-purple-subtle text-accent-purple\">\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-6 w-6\"\n />\n </div>\n\n <div className=\"min-w-0 space-y-2\">\n {/* Name + status */}\n <div className=\"flex items-center gap-2\">\n <h3 className=\"truncate font-semibold\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n\n {/* Target user */}\n <p className=\"text-sm text-muted-foreground\">\n {tr(\"devportal.grantedToUser\", \"Granted to user:\")}{\" \"}\n <code className=\"rounded bg-muted px-1.5 py-0.5 text-xs\">\n {delegation.userId}\n </code>\n </p>\n\n {/* Time */}\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.granted\", \"Granted\")}{\" \"}\n {formatRelativeTime(delegation.grantedAt)}\n </span>\n {delegation.expiresAt && (\n <span className=\"flex items-center gap-1\">\n <AlertTriangle className=\"h-3.5 w-3.5\" />\n {tr(\"devportal.expires\", \"Expires\")}{\" \"}\n {new Date(delegation.expiresAt).toLocaleDateString()}\n </span>\n )}\n </div>\n\n {/* Scopes */}\n {delegation.scopes.length > 0 && (\n <div>\n <p className=\"mb-1.5 text-xs text-muted-foreground\">\n {tr(\"devportal.permissionsGranted\", \"Permissions granted:\")}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {delegation.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-md bg-accent-purple-subtle px-2 py-0.5 text-xs text-accent-purple\"\n >\n {scope}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Right: Revoke button */}\n {delegation.status === \"ACTIVE\" && (\n <button\n onClick={() => onRevoke(delegation)}\n disabled={isRevoking || !canRevoke}\n aria-disabled={!canRevoke}\n className=\"inline-flex shrink-0 items-center gap-2 rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isRevoking ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <XCircle className=\"h-4 w-4\" />\n )}\n {tr(\"devportal.revoke\", \"Revoke\")}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Revocation history section (dimmed, for revoked/expired delegations)\n */\nconst RevocationHistory: FC<{ delegations: AppUserDelegation[] }> = ({\n delegations,\n}) => {\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\n if (delegations.length === 0) return null;\n\n return (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-semibold\">\n {tr(\"devportal.revocationHistory\", \"Revocation History\")}\n </h2>\n <div className=\"space-y-3 opacity-60\">\n {delegations.map((delegation) => (\n <div\n key={delegation.id}\n className=\"flex items-center justify-between rounded-lg border bg-muted/30 p-4\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-muted text-muted-foreground\">\n <AppTypeIcon\n type={delegation.app?.type ?? \"SERVICE\"}\n className=\"h-5 w-5\"\n />\n </div>\n <div>\n <h3 className=\"font-medium\">\n {delegation.app?.name ??\n tr(\"devportal.unknownApp\", \"Unknown App\")}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n {delegation.status === \"REVOKED\"\n ? tr(\"devportal.revoked\", \"Revoked\")\n : tr(\"devportal.expired\", \"Expired\")}{\" \"}\n {delegation.revokedAt\n ? formatRelativeTime(delegation.revokedAt)\n : \"\"}\n </p>\n </div>\n </div>\n <DelegationStatusBadge status={delegation.status} />\n </div>\n ))}\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * Delegations management page for the Developer Portal.\n *\n * Consolidates delegation management previously at /auth/delegations in\n * microfe-auth. Shows two tabs:\n * - \"Apps Acting as Me\": delegations where the current user is the subject\n * - \"Delegations I Granted\": delegations the current user granted to others\n */\nexport const DelegationsPage: 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: canRevoke } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.DELEGATION_REVOKE,\n );\n // ---------------------------------------------------------------------------\n // State\n // ---------------------------------------------------------------------------\n const [activeTab, setActiveTab] = useState<ActiveTab>(\"acting-as-me\");\n const [revokeTargets, setRevokeTargets] = useState<AppUserDelegation[]>([]);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [selectedDelegationIds, setSelectedDelegationIds] = useState<\n Set<string>\n >(new Set());\n\n // ---------------------------------------------------------------------------\n // Queries\n // ---------------------------------------------------------------------------\n const {\n data: delegationsData,\n loading: delegationsLoading,\n error: delegationsError,\n refetch: refetchDelegations,\n } = useCurrentUserDelegationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const {\n data: grantedData,\n loading: grantedLoading,\n error: grantedError,\n refetch: refetchGranted,\n } = useCurrentUserGrantedDelegationsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n // ---------------------------------------------------------------------------\n // Mutation\n // ---------------------------------------------------------------------------\n const [revokeDelegationMutation, { loading: revokeLoading }] =\n useRevokeDelegationMutation();\n\n // ---------------------------------------------------------------------------\n // Derived data\n // ---------------------------------------------------------------------------\n const delegations: AppUserDelegation[] = (\n delegationsData?.currentUserDelegations?.delegations ?? []\n ).map(mapDelegation);\n\n const grantedDelegations: AppUserDelegation[] = (\n grantedData?.currentUserGrantedDelegations?.delegations ?? []\n ).map(mapDelegation);\n\n const activeDelegations = delegations.filter((d) => d.status === \"ACTIVE\");\n const inactiveDelegations = delegations.filter((d) => d.status !== \"ACTIVE\");\n const activeGrantedDelegations = grantedDelegations.filter(\n (d) => d.status === \"ACTIVE\",\n );\n const visibleActiveDelegations =\n activeTab === \"acting-as-me\" ? activeDelegations : activeGrantedDelegations;\n const selectedActiveDelegations = useMemo(\n () =>\n visibleActiveDelegations.filter((delegation) =>\n selectedDelegationIds.has(delegation.id),\n ),\n [selectedDelegationIds, visibleActiveDelegations],\n );\n const allVisibleSelected =\n visibleActiveDelegations.length > 0 &&\n visibleActiveDelegations.every((delegation) =>\n selectedDelegationIds.has(delegation.id),\n );\n const partiallySelected =\n visibleActiveDelegations.some((delegation) =>\n selectedDelegationIds.has(delegation.id),\n ) && !allVisibleSelected;\n\n const isLoading =\n activeTab === \"acting-as-me\" ? delegationsLoading : grantedLoading;\n const queryError =\n activeTab === \"acting-as-me\" ? delegationsError : grantedError;\n\n // ---------------------------------------------------------------------------\n // Handlers\n // ---------------------------------------------------------------------------\n const handleRevokeClick = useCallback((delegation: AppUserDelegation) => {\n setRevokeTargets([delegation]);\n }, []);\n\n const toggleDelegationSelection = useCallback(\n (delegationId: string, checked: boolean) => {\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(delegationId);\n } else {\n next.delete(delegationId);\n }\n return next;\n });\n },\n [],\n );\n\n const toggleAllVisibleDelegations = useCallback(\n (checked: boolean) => {\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n visibleActiveDelegations.forEach((delegation) => {\n if (checked) {\n next.add(delegation.id);\n } else {\n next.delete(delegation.id);\n }\n });\n return next;\n });\n },\n [visibleActiveDelegations],\n );\n\n const clearSelection = useCallback(() => {\n setSelectedDelegationIds(new Set());\n }, []);\n\n const handleBulkRevokeClick = useCallback(() => {\n if (selectedActiveDelegations.length === 0) return;\n setRevokeTargets(selectedActiveDelegations);\n }, [selectedActiveDelegations]);\n\n const handleRevokeConfirm = useCallback(async () => {\n if (revokeTargets.length === 0) return;\n try {\n const results = await Promise.allSettled(\n revokeTargets.map((delegation) =>\n revokeDelegationMutation({\n variables: {\n id: delegation.id,\n reason: tr(\n \"devportal.userRevokedAccessReason\",\n \"User revoked access via Developer Portal\",\n ),\n },\n }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) =>\n result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setSuccessMessage(\n `Revoked ${succeeded} delegation${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n setSelectedDelegationIds((prev) => {\n const next = new Set(prev);\n revokeTargets.forEach((delegation) => next.delete(delegation.id));\n return next;\n });\n setRevokeTargets([]);\n // Refetch both queries to keep data fresh\n void refetchDelegations();\n void refetchGranted();\n } catch {\n // Error is available via the mutation hook's error state;\n // the ConfirmDialog will close and we show the error via queryError fallback\n setRevokeTargets([]);\n }\n }, [\n revokeTargets,\n revokeDelegationMutation,\n refetchDelegations,\n refetchGranted,\n ]);\n\n const handleDismissSuccess = useCallback(() => {\n setSuccessMessage(null);\n }, []);\n\n // ---------------------------------------------------------------------------\n // Summary stats for header\n // ---------------------------------------------------------------------------\n const totalActive = activeDelegations.length;\n const totalGranted = activeGrantedDelegations.length;\n\n // ---------------------------------------------------------------------------\n // Render\n // ---------------------------------------------------------------------------\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.INSTALLATION_READ}>\n <PageLayout\n title={tr(\"devportal.appDelegationsTitle\", \"App Delegations\")}\n description={tr(\n \"devportal.appDelegationsDescription\",\n \"Manage which apps can act on your behalf. Revoke access at any time.\",\n )}\n >\n {/* Success message */}\n {successMessage && (\n <SuccessToast\n message={successMessage}\n onDismiss={handleDismissSuccess}\n />\n )}\n\n {/* Error message */}\n {queryError && (\n <ErrorBanner\n message={\n queryError.message ||\n tr(\n \"devportal.failedToLoadDelegations\",\n \"Failed to load delegations\",\n )\n }\n onRetry={() => {\n void refetchDelegations();\n void refetchGranted();\n }}\n />\n )}\n\n {/* Summary stats */}\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Shield className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.activeDelegations\", \"Active Delegations\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalActive}</p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Users className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.grantedByMe\", \"Granted by Me\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalGranted}</p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-status-warning-text\">\n <AlertTriangle className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.revokedExpired\", \"Revoked / Expired\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">\n {inactiveDelegations.length}\n </p>\n </div>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground\">\n <Clock className=\"h-4 w-4\" />\n <span className=\"text-sm\">\n {tr(\"devportal.totalAllTime\", \"Total All-Time\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{delegations.length}</p>\n </div>\n </div>\n\n {/* Tabs */}\n <div className=\"border-b\">\n <div\n className=\"flex overflow-x-auto\"\n role=\"tablist\"\n aria-label={tr(\n \"devportal.delegationSections\",\n \"Delegation sections\",\n )}\n >\n <button\n id=\"delegations-tab-acting-as-me\"\n role=\"tab\"\n aria-selected={activeTab === \"acting-as-me\"}\n onClick={() => {\n clearSelection();\n setActiveTab(\"acting-as-me\");\n }}\n className={cn(\n \"-mb-px border-b-2 px-4 py-3 text-sm font-medium transition-colors\",\n activeTab === \"acting-as-me\"\n ? \"border-primary text-primary\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {tr(\"devportal.appsActingAsMe\", \"Apps Acting as Me\")}\n {totalActive > 0 && (\n <span className=\"ml-2 inline-flex items-center justify-center rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {totalActive}\n </span>\n )}\n </button>\n <button\n id=\"delegations-tab-granted-by-me\"\n role=\"tab\"\n aria-selected={activeTab === \"granted-by-me\"}\n onClick={() => {\n clearSelection();\n setActiveTab(\"granted-by-me\");\n }}\n className={cn(\n \"-mb-px border-b-2 px-4 py-3 text-sm font-medium transition-colors\",\n activeTab === \"granted-by-me\"\n ? \"border-primary text-primary\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {tr(\"devportal.delegationsIGranted\", \"Delegations I Granted\")}\n {totalGranted > 0 && (\n <span className=\"ml-2 inline-flex items-center justify-center rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary\">\n {totalGranted}\n </span>\n )}\n </button>\n </div>\n </div>\n\n {visibleActiveDelegations.length > 0 && (\n <div className=\"rounded-lg border bg-card 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={toggleAllVisibleDelegations}\n ariaLabel={tr(\n \"devportal.selectAllVisibleDelegations\",\n \"Select all visible delegations\",\n )}\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedDelegationIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\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-muted px-3 py-1 text-sm font-medium text-muted-foreground\">\n {selectedDelegationIds.size}{\" \"}\n {tr(\"devportal.selected\", \"selected\")}\n </span>\n </div>\n <p className=\"mt-1 text-2xl font-semibold\">{totalActive}</p>\n </div>\n </div>\n )}\n\n {/* Loading */}\n {isLoading && !delegationsData && !grantedData && (\n <LoadingState\n message={tr(\n \"devportal.loadingDelegations\",\n \"Loading delegations...\",\n )}\n />\n )}\n\n {/* ------------------------------------------------------------------- */}\n {/* Tab 1: Apps Acting as Me */}\n {/* ------------------------------------------------------------------- */}\n {!isLoading && activeTab === \"acting-as-me\" && (\n <div\n className=\"space-y-8\"\n id=\"delegations-panel-acting-as-me\"\n role=\"tabpanel\"\n aria-labelledby=\"delegations-tab-acting-as-me\"\n tabIndex={0}\n >\n {activeDelegations.length === 0 &&\n inactiveDelegations.length === 0 ? (\n <EmptyState\n icon={<Shield size={32} />}\n title={tr(\n \"devportal.noActiveDelegations\",\n \"No Active Delegations\",\n )}\n description={tr(\n \"devportal.noAppsAuthorizedOnYourBehalf\",\n \"No apps are currently authorized to act on your behalf.\",\n )}\n />\n ) : (\n <>\n {/* Active delegation cards */}\n {activeDelegations.length === 0 ? (\n <EmptyState\n icon={<Shield size={32} />}\n title={tr(\n \"devportal.noActiveDelegations\",\n \"No Active Delegations\",\n )}\n description={tr(\n \"devportal.allPreviousDelegationsRevokedOrExpired\",\n \"All previous delegations have been revoked or expired.\",\n )}\n />\n ) : (\n <div className=\"space-y-4\">\n {activeDelegations.map((delegation) => (\n <DelegationCard\n key={delegation.id}\n delegation={delegation}\n onRevoke={handleRevokeClick}\n isRevoking={\n revokeLoading &&\n revokeTargets.some(\n (item) => item.id === delegation.id,\n )\n }\n selected={selectedDelegationIds.has(delegation.id)}\n onToggleSelect={(checked) =>\n toggleDelegationSelection(delegation.id, checked)\n }\n canRevoke={canRevoke}\n />\n ))}\n </div>\n )}\n\n {/* Revocation history */}\n <RevocationHistory delegations={inactiveDelegations} />\n </>\n )}\n </div>\n )}\n\n {/* ------------------------------------------------------------------- */}\n {/* Tab 2: Delegations I Granted */}\n {/* ------------------------------------------------------------------- */}\n {!isLoading && activeTab === \"granted-by-me\" && (\n <div\n className=\"space-y-4\"\n id=\"delegations-panel-granted-by-me\"\n role=\"tabpanel\"\n aria-labelledby=\"delegations-tab-granted-by-me\"\n tabIndex={0}\n >\n {grantedDelegations.length === 0 ? (\n <EmptyState\n icon={<Users size={32} />}\n title={tr(\n \"devportal.noDelegationsGranted\",\n \"No Delegations Granted\",\n )}\n description={tr(\n \"devportal.noGrantedDelegationsDescription\",\n \"You haven't granted any app delegations to other users.\",\n )}\n />\n ) : (\n grantedDelegations.map((delegation) => (\n <GrantedDelegationCard\n key={delegation.id}\n delegation={delegation}\n onRevoke={handleRevokeClick}\n isRevoking={\n revokeLoading &&\n revokeTargets.some((item) => item.id === delegation.id)\n }\n selected={selectedDelegationIds.has(delegation.id)}\n onToggleSelect={(checked) =>\n toggleDelegationSelection(delegation.id, checked)\n }\n canRevoke={canRevoke}\n />\n ))\n )}\n </div>\n )}\n\n {/* Info card */}\n <div className=\"rounded-lg border border-border-subtle bg-status-info-bg-subtle p-5\">\n <div className=\"flex gap-4\">\n <AlertCircle className=\"h-6 w-6 shrink-0 text-accent-blue\" />\n <div>\n <h3 className=\"font-semibold text-status-info-text\">\n {tr(\"devportal.aboutAppDelegations\", \"About App Delegations\")}\n </h3>\n <p className=\"mt-1 text-sm text-status-info-text\">\n {tr(\n \"devportal.aboutAppDelegationsText\",\n \"App delegations allow automated services, bots, and integrations to perform actions on your behalf. Each delegation specifies which permissions (scopes) and workspaces the app can access. You can revoke access at any time, which immediately prevents the app from acting as you.\",\n )}\n </p>\n </div>\n </div>\n </div>\n\n {/* Revoke confirmation dialog */}\n <ConfirmDialog\n open={revokeTargets.length > 0}\n onOpenChange={(open) => {\n if (!open) setRevokeTargets([]);\n }}\n onConfirm={handleRevokeConfirm}\n title={\n revokeTargets.length > 1\n ? tr(\"devportal.revokeDelegations\", \"Revoke Delegations\")\n : tr(\"devportal.revokeDelegation\", \"Revoke Delegation\")\n }\n description={\n revokeTargets.length > 1\n ? tr(\n \"devportal.revokeDelegationsBulkConfirm\",\n `Are you sure you want to revoke ${revokeTargets.length} selected delegations? Those apps will no longer be able to act on the delegated users' behalf.`,\n )\n : tr(\n \"devportal.revokeDelegationSingleConfirm\",\n `Are you sure you want to revoke access for \"${revokeTargets[0]?.app?.name ?? \"this app\"}\"? This app will no longer be able to act on your behalf.`,\n )\n }\n confirmLabel={tr(\"devportal.revokeAccess\", \"Revoke Access\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"danger\"\n loading={revokeLoading}\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAwCA,SAAS,EAAc,GAmBD;AACpB,QAAO;EACL,IAAI,EAAE;EACN,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,QAAQ,EAAE;EACV,cAAc,EAAE,gBAAgB,EAAE;EAClC,QAAQ,EAAE;EACV,WAAW,EAAE;EACb,WAAW,EAAE;EACb,WAAW,EAAE,aAAa,KAAA;EAC1B,WAAW,EAAE,aAAa,KAAA;EAC1B,WAAW,EAAE,aAAa,KAAA;EAC1B,KAAK;GACH,IAAI,EAAE,IAAI;GACV,MAAM,EAAE,IAAI;GACZ,aAAa,EAAE,IAAI,eAAe,KAAA;GAClC,MAAM,EAAE,IAAI;GACZ,QAAQ,EAAE,IAAI;GACf;EACF;;AAMH,SAAS,EAAmB,GAA4B;CACtD,IAAM,IAAO,IAAI,KAAK,EAAW,EAE3B,qBADM,IAAI,MAAM,EACH,SAAS,GAAG,EAAK,SAAS,EACvC,IAAc,KAAK,MAAM,IAAS,IAAK,EACvC,IAAc,KAAK,MAAM,IAAc,GAAG,EAC1C,IAAY,KAAK,MAAM,IAAc,GAAG,EACxC,IAAW,KAAK,MAAM,IAAY,GAAG,EACrC,IAAY,KAAK,MAAM,IAAW,EAAE,EACpC,IAAa,KAAK,MAAM,IAAW,GAAG;AAa5C,QAXI,IAAc,KAAW,aACzB,IAAc,KACT,GAAG,EAAY,SAAS,MAAgB,IAAU,KAAN,IAAS,QAC1D,IAAY,KACP,GAAG,EAAU,OAAO,MAAc,IAAU,KAAN,IAAS,QACpD,IAAW,IAAU,GAAG,EAAS,MAAM,MAAa,IAAU,KAAN,IAAS,QACjE,IAAY,IACP,GAAG,EAAU,OAAO,MAAc,IAAU,KAAN,IAAS,QACpD,IAAa,KACR,GAAG,EAAW,QAAQ,MAAe,IAAU,KAAN,IAAS,QAEpD,EAAK,oBAAoB;;AAMlC,SAAS,EAAqB,GAA4B;CACxD,IAAM,IAAO,IAAI,KAAK,EAAW,EAC3B,oBAAM,IAAI,MAAM,EAChB,IAAS,EAAK,SAAS,GAAG,EAAI,SAAS;AAE7C,KAAI,KAAU,EAAG,QAAO;CAExB,IAAM,IAAc,KAAK,MAAM,KAAU,MAAO,IAAI,EAC9C,IAAY,KAAK,MAAM,KAAU,MAAO,KAAK,IAAI,EACjD,IAAW,KAAK,MAAM,KAAU,MAAO,KAAK,KAAK,IAAI;AAM3D,QAJI,IAAc,KACT,MAAM,EAAY,SAAS,MAAgB,IAAU,KAAN,QACpD,IAAY,KACP,MAAM,EAAU,OAAO,MAAc,IAAU,KAAN,QAC3C,MAAM,EAAS,MAAM,MAAa,IAAU,KAAN;;AAU/C,IAAM,KAAyD,EAC7D,SACA,mBACI;CACJ,IAAM,IAAY,EAAE,WAAW,EAAG,WAAW,EAAU,EAAE;AAEzD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO,kBAAC,GAAD,EAAK,GAAI,GAAa,CAAA;EAC/B,KAAK,MACH,QAAO,kBAAC,GAAD,EAAK,GAAI,GAAa,CAAA;EAC/B,KAAK,cACH,QAAO,kBAAC,GAAD,EAAK,GAAI,GAAa,CAAA;EAC/B,QACE,QAAO,kBAAC,GAAD,EAAQ,GAAI,GAAa,CAAA;;GAOhC,KAA2D,EAC/D,gBASE,kBAAC,QAAD;CACE,WAAW,EACT,2EAT2C;EAC/C,QAAQ;EACR,SAAS;EACT,SAAS;EACV,CAMY,GACR;WAEA;CACI,CAAA,EAOL,MAAgE,EACpE,YACA,oBAEA,QAAgB;CACd,IAAM,IAAQ,WAAW,GAAW,IAAK;AACzC,cAAa,aAAa,EAAM;GAC/B,CAAC,EAAU,CAAC,EAGb,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,oCAAqC,CAAA,EACvD,kBAAC,KAAD;GAAG,WAAU;aACV;GACC,CAAA,CACA;;CACF,CAAA,GAOJ,MAA8D,EAClE,YACA,iBACI;CACJ,IAAM,EAAE,SAAM,GAAS;AAKvB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD,EAAa,WAAU,4BAA6B,CAAA;IACpD,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAAY,CAAA;IAC3D,KACC,kBAAC,UAAD;KACE,SAAS;KACT,WAAU;iBAZR,GAAa,MAA6B;MACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,aAAO,MAAU,IAAM,IAAW;QAYtB,mBAAmB,QAAQ;KACxB,CAAA;IAEP;;EACF,CAAA;GAOJ,MAOA,EACJ,eACA,aACA,eACA,aACA,mBACA,mBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;;AAEpC,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0EACA,KAAY,+BACZ,EAAW,WAAW,YAAY,aACnC;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,EAAW,WAAW,YACrB,kBAAC,OAAD;MAAK,UAAU,MAAU,EAAM,iBAAiB;gBAC9C,kBAAC,GAAD;OACE,SAAS;OACT,UAAU;OACV,WAAW,EACT,uCACA,qBAAqB,EAAW,KAAK,QAAQ,EAAW,KACzD;OACD,WAAU;OACV,CAAA;MACE,CAAA;KAER,kBAAC,OAAD;MACE,WAAW,EACT,kEACA,EAAW,WAAW,WAClB,+BACA,iCACL;gBAED,kBAAC,GAAD;OACE,MAAM,EAAW,KAAK,QAAQ;OAC9B,WAAU;OACV,CAAA;MACE,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAW,KAAK,QACf,EAAG,wBAAwB,cAAc;SACxC,CAAA,EACL,kBAAC,GAAD,EAAuB,QAAQ,EAAW,QAAU,CAAA,CAChD;;OAGL,EAAW,KAAK,eACf,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAW,IAAI;QACd,CAAA;OAIN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;WAChC,EAAG,qBAAqB,UAAU;WAAE;WACpC,EAAmB,EAAW,UAAU;WACpC;;SACN,EAAW,aACV,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAe,WAAU,eAAgB,CAAA;WACxC,EAAG,qBAAqB,UAAU;WAAE;WACpC,EAAqB,EAAW,UAAU;WACtC;;SAET,kBAAC,QAAD;UACE,WAAW,EACT,4CACA,iCACD;oBAEA,EAAW,KAAK,QAAQ;UACpB,CAAA;SACH;;OAGL,EAAW,OAAO,SAAS,KAC1B,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,yBAAyB,eAAe;QAC1C,CAAA,EACJ,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAW,OAAO,KAAK,MACtB,kBAAC,QAAD;SAEE,WAAU;mBAET;SACI,EAJA,EAIA,CACP;QACE,CAAA,CACF,EAAA,CAAA;OAIP,EAAW,aAAa,SAAS,KAChC,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;SAChC,EAAG,gCAAgC,YAAY;SAAE;SACjD,EAAW,aAAa;SAAQ;SAChC,EAAG,uBAAuB,YAAY;SACtC,EAAW,aAAa,WAAW,IAEhC,KADA,EAAG,mCAAmC,IAAI;SAE5C;;OAEF;;KACF;OAGL,EAAW,WAAW,YACrB,kBAAC,UAAD;IACE,eAAe,EAAS,EAAW;IACnC,UAAU,KAAc,CAAC;IACzB,iBAAe,CAAC;IAChB,WAAU;cAJZ,CAMG,IACC,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA,EAEhC,EAAG,oBAAoB,SAAS,CAC1B;MAEP;;EACF,CAAA;GAOJ,MAOA,EACJ,eACA,aACA,eACA,aACA,mBACA,mBACI;CACJ,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;;AAEpC,QACE,kBAAC,OAAD;EACE,WAAW,EACT,0EACA,KAAY,8BACb;YAED,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG,EAAW,WAAW,YACrB,kBAAC,OAAD;MAAK,UAAU,MAAU,EAAM,iBAAiB;gBAC9C,kBAAC,GAAD;OACE,SAAS;OACT,UAAU;OACV,WAAW,EACT,uCACA,qBAAqB,EAAW,KAAK,QAAQ,EAAW,KACzD;OACD,WAAU;OACV,CAAA;MACE,CAAA;KAER,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,MAAM,EAAW,KAAK,QAAQ;OAC9B,WAAU;OACV,CAAA;MACE,CAAA;KAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OAEE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAW,KAAK,QACf,EAAG,wBAAwB,cAAc;SACxC,CAAA,EACL,kBAAC,GAAD,EAAuB,QAAQ,EAAW,QAAU,CAAA,CAChD;;OAGN,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAG,2BAA2B,mBAAmB;SAAE;SACpD,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAW;UACP,CAAA;SACL;;OAGJ,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;UAChC,EAAG,qBAAqB,UAAU;UAAE;UACpC,EAAmB,EAAW,UAAU;UACpC;YACN,EAAW,aACV,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACE,kBAAC,GAAD,EAAe,WAAU,eAAgB,CAAA;UACxC,EAAG,qBAAqB,UAAU;UAAE;UACpC,IAAI,KAAK,EAAW,UAAU,CAAC,oBAAoB;UAC/C;WAEL;;OAGL,EAAW,OAAO,SAAS,KAC1B,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAG,gCAAgC,uBAAuB;QACzD,CAAA,EACJ,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAW,OAAO,KAAK,MACtB,kBAAC,QAAD;SAEE,WAAU;mBAET;SACI,EAJA,EAIA,CACP;QACE,CAAA,CACF,EAAA,CAAA;OAEJ;;KACF;OAGL,EAAW,WAAW,YACrB,kBAAC,UAAD;IACE,eAAe,EAAS,EAAW;IACnC,UAAU,KAAc,CAAC;IACzB,iBAAe,CAAC;IAChB,WAAU;cAJZ,CAMG,IACC,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,GAE5C,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA,EAEhC,EAAG,oBAAoB,SAAS,CAC1B;MAEP;;EACF,CAAA;GAOJ,MAA+D,EACnE,qBACI;CACJ,IAAM,EAAE,MAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;;AAKpC,QAFI,EAAY,WAAW,IAAU,OAGnC,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX,EAAG,+BAA+B,qBAAqB;GACrD,CAAA,EACL,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAY,KAAK,MAChB,kBAAC,OAAD;IAEE,WAAU;cAFZ,CAIE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,MAAM,EAAW,KAAK,QAAQ;OAC9B,WAAU;OACV,CAAA;MACE,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAW,KAAK,QACf,EAAG,wBAAwB,cAAc;MACxC,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAW,WAAW,YACnB,EAAG,qBAAqB,UAAU,GAClC,EAAG,qBAAqB,UAAU;OAAE;OACvC,EAAW,YACR,EAAmB,EAAW,UAAU,GACxC;OACF;QACA,EAAA,CAAA,CACF;QACN,kBAAC,GAAD,EAAuB,QAAQ,EAAW,QAAU,CAAA,CAChD;MA1BC,EAAW,GA0BZ,CACN;GACE,CAAA,CACF;;GAgBG,UAA4B;CACvC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,EAAE,SAAS,MAAc,GAC7B,EAAsB,kBACvB,EAIK,CAAC,GAAW,KAAgB,EAAoB,eAAe,EAC/D,CAAC,GAAe,KAAoB,EAA8B,EAAE,CAAC,EACrE,CAAC,GAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,GAAuB,KAA4B,kBAExD,IAAI,KAAK,CAAC,EAKN,EACJ,MAAM,GACN,SAAS,IACT,OAAO,IACP,SAAS,MACP,EAA+B,EACjC,aAAa,qBACd,CAAC,EAEI,EACJ,MAAM,GACN,SAAS,IACT,OAAO,IACP,SAAS,MACP,EAAsC,EACxC,aAAa,qBACd,CAAC,EAKI,CAAC,GAA0B,EAAE,SAAS,OAC1C,GAA6B,EAKzB,KACJ,GAAiB,wBAAwB,eAAe,EAAE,EAC1D,IAAI,EAAc,EAEd,KACJ,GAAa,+BAA+B,eAAe,EAAE,EAC7D,IAAI,EAAc,EAEd,IAAoB,EAAY,QAAQ,MAAM,EAAE,WAAW,SAAS,EACpE,IAAsB,EAAY,QAAQ,MAAM,EAAE,WAAW,SAAS,EACtE,IAA2B,EAAmB,QACjD,MAAM,EAAE,WAAW,SACrB,EACK,IACJ,MAAc,iBAAiB,IAAoB,GAC/C,IAA4B,SAE9B,EAAyB,QAAQ,MAC/B,EAAsB,IAAI,EAAW,GAAG,CACzC,EACH,CAAC,GAAuB,EAAyB,CAClD,EACK,IACJ,EAAyB,SAAS,KAClC,EAAyB,OAAO,MAC9B,EAAsB,IAAI,EAAW,GAAG,CACzC,EACG,KACJ,EAAyB,MAAM,MAC7B,EAAsB,IAAI,EAAW,GAAG,CACzC,IAAI,CAAC,GAEF,IACJ,MAAc,iBAAiB,KAAqB,IAChD,IACJ,MAAc,iBAAiB,KAAmB,IAK9C,IAAoB,GAAa,MAAkC;AACvE,IAAiB,CAAC,EAAW,CAAC;IAC7B,EAAE,CAAC,EAEA,IAA4B,GAC/B,GAAsB,MAAqB;AAC1C,KAA0B,MAAS;GACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,IACF,EAAK,IAAI,EAAa,GAEtB,EAAK,OAAO,EAAa,EAEpB;IACP;IAEJ,EAAE,CACH,EAEK,KAA8B,GACjC,MAAqB;AACpB,KAA0B,MAAS;GACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAQ1B,UAPA,EAAyB,SAAS,MAAe;AAC/C,IAAI,IACF,EAAK,IAAI,EAAW,GAAG,GAEvB,EAAK,OAAO,EAAW,GAAG;KAE5B,EACK;IACP;IAEJ,CAAC,EAAyB,CAC3B,EAEK,IAAiB,QAAkB;AACvC,oBAAyB,IAAI,KAAK,CAAC;IAClC,EAAE,CAAC;AAEwB,SAAkB;AAC1C,IAA0B,WAAW,KACzC,EAAiB,EAA0B;IAC1C,CAAC,EAA0B,CAAC;CAE/B,IAAM,KAAsB,EAAY,YAAY;AAC9C,QAAc,WAAW,EAC7B,KAAI;GACF,IAAM,IAAU,MAAM,QAAQ,WAC5B,EAAc,KAAK,MACjB,EAAyB,EACvB,WAAW;IACT,IAAI,EAAW;IACf,QAAQ,EACN,qCACA,2CACD;IACF,EACF,CAAC,CACH,CACF,EACK,IAAY,EAAQ,QACvB,MACC,EAAO,WAAW,YACrB,CAAC,QACI,IAAS,EAAQ,SAAS;AAY3B,GAXL,EACE,WAAW,EAAU,aAAa,MAAc,IAAI,KAAK,MAAM,IAAS,IAAI,MAAM,EAAO,WAAW,KACrG,EACD,GAA0B,MAAS;IACjC,IAAM,IAAO,IAAI,IAAI,EAAK;AAE1B,WADA,EAAc,SAAS,MAAe,EAAK,OAAO,EAAW,GAAG,CAAC,EAC1D;KACP,EACF,EAAiB,EAAE,CAAC,EAEf,GAAoB,EACpB,GAAgB;UACf;AAGN,KAAiB,EAAE,CAAC;;IAErB;EACD;EACA;EACA;EACA;EACD,CAAC,EAEI,KAAuB,QAAkB;AAC7C,IAAkB,KAAK;IACtB,EAAE,CAAC,EAKA,IAAc,EAAkB,QAChC,IAAe,EAAyB;AAK9C,QACE,kBAAC,GAAD;EAAiB,YAAY,EAAsB;YACjD,kBAAC,GAAD;GACE,OAAO,EAAG,iCAAiC,kBAAkB;GAC7D,aAAa,EACX,uCACA,uEACD;aALH;IAQG,KACC,kBAAC,IAAD;KACE,SAAS;KACT,WAAW;KACX,CAAA;IAIH,KACC,kBAAC,IAAD;KACE,SACE,EAAW,WACX,EACE,qCACA,6BACD;KAEH,eAAe;AAER,MADA,GAAoB,EACpB,GAAgB;;KAEvB,CAAA;IAIJ,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAC9B,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,+BAA+B,qBAAqB;SACnD,CAAA,CACH;WACN,kBAAC,KAAD;QAAG,WAAU;kBAA+B;QAAgB,CAAA,CACxD;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAC7B,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,yBAAyB,gBAAgB;SACxC,CAAA,CACH;WACN,kBAAC,KAAD;QAAG,WAAU;kBAA+B;QAAiB,CAAA,CACzD;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA,EACrC,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,4BAA4B,oBAAoB;SAC/C,CAAA,CACH;WACN,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAoB;QACnB,CAAA,CACA;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAC7B,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAG,0BAA0B,iBAAiB;SAC1C,CAAA,CACH;WACN,kBAAC,KAAD;QAAG,WAAU;kBAA+B,EAAY;QAAW,CAAA,CAC/D;;MACF;;IAGN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,MAAK;MACL,cAAY,EACV,gCACA,sBACD;gBANH,CAQE,kBAAC,UAAD;OACE,IAAG;OACH,MAAK;OACL,iBAAe,MAAc;OAC7B,eAAe;AAEb,QADA,GAAgB,EAChB,EAAa,eAAe;;OAE9B,WAAW,EACT,qEACA,MAAc,iBACV,gCACA,iEACL;iBAbH,CAeG,EAAG,4BAA4B,oBAAoB,EACnD,IAAc,KACb,kBAAC,QAAD;QAAM,WAAU;kBACb;QACI,CAAA,CAEF;UACT,kBAAC,UAAD;OACE,IAAG;OACH,MAAK;OACL,iBAAe,MAAc;OAC7B,eAAe;AAEb,QADA,GAAgB,EAChB,EAAa,gBAAgB;;OAE/B,WAAW,EACT,qEACA,MAAc,kBACV,gCACA,iEACL;iBAbH,CAeG,EAAG,iCAAiC,wBAAwB,EAC5D,IAAe,KACd,kBAAC,QAAD;QAAM,WAAU;kBACb;QACI,CAAA,CAEF;SACL;;KACF,CAAA;IAEL,EAAyB,SAAS,KACjC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD;UACE,SAAS;UACT,eAAe;UACf,UAAU;UACV,WAAW,EACT,yCACA,iCACD;UACD,CAAA,EACF,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAG,8BAA8B,qBAAqB;UAClD,CAAA,CACH;YACL,EAAsB,OAAO,KAC5B,kBAAC,UAAD;SACE,SAAS;SACT,WAAU;mBAET,EAAG,4BAA4B,kBAAkB;SAC3C,CAAA,CAEP;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACG,EAAsB;UAAM;UAC5B,EAAG,sBAAsB,WAAW;UAChC;;QACH,CAAA;OACN,kBAAC,KAAD;QAAG,WAAU;kBAA+B;QAAgB,CAAA;OACxD;;KACF,CAAA;IAIP,KAAa,CAAC,KAAmB,CAAC,KACjC,kBAAC,GAAD,EACE,SAAS,EACP,gCACA,yBACD,EACD,CAAA;IAMH,CAAC,KAAa,MAAc,kBAC3B,kBAAC,OAAD;KACE,WAAU;KACV,IAAG;KACH,MAAK;KACL,mBAAgB;KAChB,UAAU;eAET,EAAkB,WAAW,KAC9B,EAAoB,WAAW,IAC7B,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;MAC1B,OAAO,EACL,iCACA,wBACD;MACD,aAAa,EACX,0CACA,0DACD;MACD,CAAA,GAEF,kBAAA,IAAA,EAAA,UAAA,CAEG,EAAkB,WAAW,IAC5B,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;MAC1B,OAAO,EACL,iCACA,wBACD;MACD,aAAa,EACX,oDACA,yDACD;MACD,CAAA,GAEF,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAkB,KAAK,MACtB,kBAAC,IAAD;OAEc;OACZ,UAAU;OACV,YACE,KACA,EAAc,MACX,MAAS,EAAK,OAAO,EAAW,GAClC;OAEH,UAAU,EAAsB,IAAI,EAAW,GAAG;OAClD,iBAAiB,MACf,EAA0B,EAAW,IAAI,EAAQ;OAExC;OACX,EAdK,EAAW,GAchB,CACF;MACE,CAAA,EAIR,kBAAC,IAAD,EAAmB,aAAa,GAAuB,CAAA,CACtD,EAAA,CAAA;KAED,CAAA;IAMP,CAAC,KAAa,MAAc,mBAC3B,kBAAC,OAAD;KACE,WAAU;KACV,IAAG;KACH,MAAK;KACL,mBAAgB;KAChB,UAAU;eAET,EAAmB,WAAW,IAC7B,kBAAC,GAAD;MACE,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;MACzB,OAAO,EACL,kCACA,yBACD;MACD,aAAa,EACX,6CACA,0DACD;MACD,CAAA,GAEF,EAAmB,KAAK,MACtB,kBAAC,IAAD;MAEc;MACZ,UAAU;MACV,YACE,KACA,EAAc,MAAM,MAAS,EAAK,OAAO,EAAW,GAAG;MAEzD,UAAU,EAAsB,IAAI,EAAW,GAAG;MAClD,iBAAiB,MACf,EAA0B,EAAW,IAAI,EAAQ;MAExC;MACX,EAZK,EAAW,GAYhB,CACF;KAEA,CAAA;IAIR,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,qCAAsC,CAAA,EAC7D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,iCAAiC,wBAAwB;OAC1D,CAAA,EACL,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,qCACA,wRACD;OACC,CAAA,CACA,EAAA,CAAA,CACF;;KACF,CAAA;IAGN,kBAAC,GAAD;KACE,MAAM,EAAc,SAAS;KAC7B,eAAe,MAAS;AACtB,MAAK,KAAM,EAAiB,EAAE,CAAC;;KAEjC,WAAW;KACX,OACE,EAAc,SAAS,IACnB,EAAG,+BAA+B,qBAAqB,GACvD,EAAG,8BAA8B,oBAAoB;KAE3D,aACE,EAAc,SAAS,IACnB,EACE,0CACA,mCAAmC,EAAc,OAAO,iGACzD,GACD,EACE,2CACA,+CAA+C,EAAc,IAAI,KAAK,QAAQ,WAAW,2DAC1F;KAEP,cAAc,EAAG,0BAA0B,gBAAgB;KAC3D,aAAa,EAAG,oBAAoB,SAAS;KAC7C,SAAQ;KACR,SAAS;KACT,CAAA;IACS;;EACG,CAAA"}
@@ -7,14 +7,14 @@ import { StatusBadge as o } from "../components/StatusBadge.js";
7
7
  import { nativeImpact as s, nativeNotify as c } from "../../utils/nativeBridge.js";
8
8
  import { CheckboxField as l, InputField as u, SelectField as d, TagInput as f, TextareaField as p } from "../components/FormField.js";
9
9
  import { ConfirmDialog as m } from "../components/ConfirmDialog.js";
10
- import { useDeactivateAppMutation as ee, useDeleteApplicationMutation as te, useGetApplicationQuery as ne, useSubmitApplicationMutation as h, useUpdateApplicationMutation as g } from "../../generated/global-operations.js";
11
- import { PermissionGuard as _ } from "../components/PermissionGuard.js";
10
+ import { useDeactivateAppMutation as h, useDeleteApplicationMutation as ee, useGetApplicationQuery as te, useSubmitApplicationMutation as ne, useUpdateApplicationMutation as re } from "../../generated/global-operations.js";
11
+ import { PermissionGuard as g } from "../components/PermissionGuard.js";
12
12
  import "../components/index.js";
13
- import { DEVPORTAL_PERMISSIONS as v } from "../constants/permissions.js";
14
- import { useDevportalPermission as y } from "../hooks/usePermission.js";
15
- import { useEffect as b, useState as x } from "react";
16
- import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
- import { AlertCircle as C, AlertTriangle as w, AppWindow as re, Archive as ie, Power as ae, Save as oe, Send as se, Trash2 as ce } from "lucide-react";
13
+ import { DEVPORTAL_PERMISSIONS as _ } from "../constants/permissions.js";
14
+ import { useDevportalPermission as v } from "../hooks/usePermission.js";
15
+ import { useEffect as y, useState as b } from "react";
16
+ import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
+ import { AlertCircle as S, AlertTriangle as C, AppWindow as w, Archive as ie, Power as ae, Save as oe, Send as se, Trash2 as ce } from "lucide-react";
18
18
  import { useParams as le } from "react-router-dom";
19
19
  import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
20
20
  //#region src/devportal/pages/EditApplicationPage.tsx
@@ -130,13 +130,20 @@ function pe(e) {
130
130
  } : null;
131
131
  }
132
132
  var O = () => {
133
- let { navigate: a, basePath: O } = e(), { appId: k } = le(), { t: A } = S(), { allowed: j } = y(v.APP_UPDATE), { allowed: M } = y(v.APP_DELETE), N = (e, t) => {
133
+ let { navigate: a, basePath: O } = e(), { appId: k } = le(), { t: A } = x(), { allowed: j } = v(_.APP_UPDATE), { allowed: M } = v(_.APP_DELETE), N = (e, t) => {
134
134
  let n = A(e);
135
135
  return n === e ? t : n;
136
- }, [P, F] = x(!1), [I, L] = x({}), [R, z] = x(!1), [B, V] = x(!1), [me, H] = x(!1), [U, W] = x(!1), { data: G, loading: he, error: K } = ne({
136
+ }, [P, F] = b(!1), [I, L] = b({}), [R, z] = b(!1), [B, V] = b(!1), [me, H] = b(!1), [U, W] = b(!1), { data: G, loading: he, error: K } = te({
137
137
  variables: { id: k },
138
138
  skip: !k
139
- }), [q] = g(), [ge] = te(), [_e] = h(), [ve] = ee(), J = pe(G?.application), [Y, X] = x({
139
+ }), [q] = re(), [ge] = ee(), [_e] = ne(), [ve] = h(), J = pe(G?.application);
140
+ y(() => {
141
+ let e = J?.name;
142
+ if (!k || !e) return;
143
+ let t = window.__breadcrumbRegistry;
144
+ return t?.register(k, e), () => t?.unregister(k);
145
+ }, [k, J?.name]);
146
+ let [Y, X] = b({
140
147
  name: "",
141
148
  slug: "",
142
149
  shortDescription: "",
@@ -154,7 +161,7 @@ var O = () => {
154
161
  githubUrl: "",
155
162
  requiresApproval: !1
156
163
  });
157
- b(() => {
164
+ y(() => {
158
165
  J && (X({
159
166
  name: J.name,
160
167
  slug: J.slug,
@@ -257,7 +264,7 @@ var O = () => {
257
264
  children: /* @__PURE__ */ D("div", {
258
265
  className: "flex flex-col items-center justify-center py-12 text-center",
259
266
  children: [
260
- /* @__PURE__ */ E(C, {
267
+ /* @__PURE__ */ E(S, {
261
268
  size: 48,
262
269
  className: "text-destructive mb-4"
263
270
  }),
@@ -277,14 +284,14 @@ var O = () => {
277
284
  backLink: `${O}/apps`,
278
285
  backLabel: N("devportal.backToApplications", "Back to Applications"),
279
286
  children: /* @__PURE__ */ E(n, {
280
- icon: /* @__PURE__ */ E(re, { size: 48 }),
287
+ icon: /* @__PURE__ */ E(w, { size: 48 }),
281
288
  title: N("devportal.applicationNotFound", "Application not found"),
282
289
  description: N("devportal.applicationNotFoundDescription", "The application you're looking for doesn't exist or you don't have access to it.")
283
290
  })
284
291
  });
285
292
  let we = J.status === "DRAFT" || J.status === "REJECTED", Te = J.status === "PUBLISHED", Ee = J.status === "ACTIVE" || J.status === "PUBLISHED";
286
- return /* @__PURE__ */ E(_, {
287
- permission: v.APP_UPDATE,
293
+ return /* @__PURE__ */ E(g, {
294
+ permission: _.APP_UPDATE,
288
295
  children: /* @__PURE__ */ D(r, {
289
296
  title: `${N("devportal.editAppTitlePrefix", "Edit:")} ${J.name}`,
290
297
  description: N("devportal.editAppDescription", "Update your application settings"),
@@ -304,7 +311,7 @@ var O = () => {
304
311
  children: [
305
312
  J.status === "PUBLISHED" && /* @__PURE__ */ D("div", {
306
313
  className: "rounded-lg border border-border-subtle bg-status-warning-bg-subtle p-4 flex gap-3",
307
- children: [/* @__PURE__ */ E(w, {
314
+ children: [/* @__PURE__ */ E(C, {
308
315
  size: 20,
309
316
  className: "text-status-warning-text flex-shrink-0 mt-0.5"
310
317
  }), /* @__PURE__ */ D("div", {
@@ -389,7 +396,8 @@ var O = () => {
389
396
  label: N("devportal.visibility", "Visibility"),
390
397
  value: Y.visibility,
391
398
  onChange: (e) => Q("visibility", e.target.value),
392
- options: de
399
+ options: de,
400
+ hint: N("devportal.visibilityHint", "Controls who can see this app once published. Public apps still require admin review and approval before appearing in the store.")
393
401
  })
394
402
  ]
395
403
  }),