@burdenoff/microfe-admin 2026.610.1 → 2026.625.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/admin/components/AccessDenied.js +2 -2
- package/dist/admin/components/AccessDenied.js.map +1 -1
- package/dist/admin/components/ActivityFeed.js +4 -4
- package/dist/admin/components/ActivityFeed.js.map +1 -1
- package/dist/admin/components/Charts.js +4 -4
- package/dist/admin/components/Charts.js.map +1 -1
- package/dist/admin/components/ConfirmDialog.js +6 -6
- package/dist/admin/components/ConfirmDialog.js.map +1 -1
- package/dist/admin/components/DataTable.js +8 -8
- package/dist/admin/components/DataTable.js.map +1 -1
- package/dist/admin/components/FormSheet.js +7 -7
- package/dist/admin/components/FormSheet.js.map +1 -1
- package/dist/admin/components/MetricCard.js +43 -40
- package/dist/admin/components/MetricCard.js.map +1 -1
- package/dist/admin/components/PageLayout.js +28 -22
- package/dist/admin/components/PageLayout.js.map +1 -1
- package/dist/admin/components/PermissionGuard.js +1 -1
- package/dist/admin/components/PermissionGuard.js.map +1 -1
- package/dist/admin/components/RowActions.js +2 -2
- package/dist/admin/components/RowActions.js.map +1 -1
- package/dist/admin/components/TenantMembersPanel.js +15 -15
- package/dist/admin/components/TenantMembersPanel.js.map +1 -1
- package/dist/admin/components/TenantSsoPanel.js +14 -14
- package/dist/admin/components/TenantSsoPanel.js.map +1 -1
- package/dist/admin/components/Toast.js +9 -9
- package/dist/admin/components/Toast.js.map +1 -1
- package/dist/admin/components/forms/SubscriptionForm.js +10 -10
- package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
- package/dist/admin/components/forms/TenantForm.js +8 -8
- package/dist/admin/components/forms/TenantForm.js.map +1 -1
- package/dist/admin/components/forms/TicketForm.js +2 -2
- package/dist/admin/components/forms/TicketForm.js.map +1 -1
- package/dist/admin/components/forms/UserForm.js +8 -8
- package/dist/admin/components/forms/UserForm.js.map +1 -1
- package/dist/admin/components/ui.js +58 -51
- package/dist/admin/components/ui.js.map +1 -1
- package/dist/admin/constants/index.js +8 -8
- package/dist/admin/constants/index.js.map +1 -1
- package/dist/admin/pages/AuditLogsPage.js +10 -8
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/BillingPage.js +51 -43
- package/dist/admin/pages/BillingPage.js.map +1 -1
- package/dist/admin/pages/CommunicationsPage.js +5 -2
- package/dist/admin/pages/CommunicationsPage.js.map +1 -1
- package/dist/admin/pages/ContentPagesPage.js +2 -0
- package/dist/admin/pages/ContentPagesPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +7 -4
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/FeatureFlagsPage.js +6 -4
- package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +8 -6
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/ProductsPage.js +10 -7
- package/dist/admin/pages/ProductsPage.js.map +1 -1
- package/dist/admin/pages/ReferralsPage.js +27 -23
- package/dist/admin/pages/ReferralsPage.js.map +1 -1
- package/dist/admin/pages/RolesPermissionsPage.js +11 -8
- package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
- package/dist/admin/pages/SREPage.js +12 -8
- package/dist/admin/pages/SREPage.js.map +1 -1
- package/dist/admin/pages/SandboxSettingsPage.js +21 -20
- package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage.js +154 -142
- package/dist/admin/pages/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SyncFailuresPage.js +14 -11
- package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
- package/dist/admin/pages/TenantDetailPage.js +22 -22
- package/dist/admin/pages/TenantDetailPage.js.map +1 -1
- package/dist/admin/pages/TenantFormRoutePage.js +2 -2
- package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
- package/dist/admin/pages/TenantsListPage.js +7 -5
- package/dist/admin/pages/TenantsListPage.js.map +1 -1
- package/dist/admin/pages/UsersListPage.js +8 -6
- package/dist/admin/pages/UsersListPage.js.map +1 -1
- package/dist/admin/pages/WaitlistPage.js +9 -7
- package/dist/admin/pages/WaitlistPage.js.map +1 -1
- package/dist/admin/pages/WorkflowsPage.js +6 -3
- package/dist/admin/pages/WorkflowsPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +9 -8
- package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MetricCard.js","names":[],"sources":["../../../src/admin/components/MetricCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport {
|
|
1
|
+
{"version":3,"file":"MetricCard.js","names":[],"sources":["../../../src/admin/components/MetricCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { Badge, Skeleton } from './ui';\nimport { cn } from '../utils/cn';\nimport { formatNumber, formatCurrency, formatChange } from '../utils/formatters';\n\nexport interface MetricCardProps {\n /** Card title */\n title: string;\n /** Metric value (number or formatted string) */\n value: number | string;\n /** Change from previous period */\n change?: number;\n /** Type of change display */\n changeType?: 'number' | 'percentage' | 'currency';\n /** Trend direction */\n trend?: 'up' | 'down' | 'neutral';\n /** Whether change is positive or negative for styling */\n changeIsPositive?: boolean;\n /** Icon to display */\n icon?: ReactNode;\n /** Subtitle text */\n subtitle?: string;\n /** Link to navigate to on click */\n linkTo?: string;\n /** Priority indicator */\n priority?: 'normal' | 'high' | 'critical';\n /** Loading state */\n loading?: boolean;\n /**\n * Hero metric — uses the glass treatment with an accent glow (Boff UI 2.0).\n * Reserve for the single most important metric per group.\n */\n hero?: boolean;\n /** Additional class name */\n className?: string;\n}\n\n/**\n * Metric card component for dashboard\n */\nexport function MetricCard({\n title,\n value,\n change,\n changeType = 'number',\n trend,\n changeIsPositive,\n icon,\n subtitle,\n linkTo,\n priority,\n loading,\n hero = false,\n className,\n}: MetricCardProps) {\n // Determine if change is positive based on trend or explicit prop\n const isPositive = changeIsPositive ?? trend === 'up';\n\n // Format the value\n const formattedValue =\n typeof value === 'number'\n ? changeType === 'currency'\n ? formatCurrency(value)\n : formatNumber(value)\n : value;\n\n // Format the change\n const formattedChange =\n change !== undefined ? formatChange(change, changeType === 'percentage') : null;\n\n // Get trend icon\n const TrendIcon = trend === 'up' ? TrendingUp : trend === 'down' ? TrendingDown : Minus;\n\n // Priority accent (left rail) — semantic tokens only.\n const priorityClasses = {\n normal: '',\n high: 'border-l-4 border-l-status-warning-border',\n critical: 'border-l-4 border-l-status-error-border',\n };\n\n const cardContent = (\n <GlassCard\n treatment={hero ? 'glass' : 'metric'}\n glow={hero}\n className={cn('h-full p-6', priority && priorityClasses[priority], className)}\n >\n <div className=\"flex flex-row items-center justify-between pb-2\">\n <span className=\"text-sm font-medium text-text-secondary\">{title}</span>\n {icon && <div className=\"text-text-secondary\">{icon}</div>}\n </div>\n {loading ? (\n <div className=\"space-y-2\">\n <Skeleton className=\"h-8 w-24\" />\n <Skeleton className=\"h-4 w-16\" />\n </div>\n ) : (\n <div>\n <div className=\"flex items-baseline justify-between\">\n <div className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {formattedValue}\n </div>\n {formattedChange !== null && (\n <Badge\n variant={isPositive ? 'success' : 'destructive'}\n className=\"flex items-center gap-1 tabular-nums\"\n >\n <TrendIcon className=\"h-3 w-3\" />\n {formattedChange}\n </Badge>\n )}\n </div>\n {subtitle && <p className=\"mt-1 text-xs text-text-secondary\">{subtitle}</p>}\n </div>\n )}\n </GlassCard>\n );\n\n if (linkTo) {\n return (\n <Link\n to={linkTo}\n className=\"block rounded-card focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] focus-visible:ring-offset-2\"\n >\n {cardContent}\n </Link>\n );\n }\n\n return cardContent;\n}\n\n/**\n * Grid container for metric cards\n */\nexport function MetricsGrid({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div className={cn('grid gap-4', 'grid-cols-1 sm:grid-cols-2 xl:grid-cols-4', className)}>\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA2CA,SAAgB,EAAW,EACzB,UACA,UACA,WACA,gBAAa,UACb,UACA,qBACA,SACA,aACA,WACA,aACA,YACA,UAAO,IACP,gBACkB;CAElB,IAAM,IAAa,KAAoB,MAAU,MAG3C,IACJ,OAAO,KAAU,WACb,MAAe,aACb,EAAe,EAAM,GACrB,EAAa,EAAM,GACrB,GAGA,IACJ,MAAW,KAAA,IAAgE,OAApD,EAAa,GAAQ,MAAe,aAAa,EAGpE,IAAY,MAAU,OAAO,IAAa,MAAU,SAAS,IAAe,GAS5E,IACJ,kBAAC,GAAD;EACE,WAAW,IAAO,UAAU;EAC5B,MAAM;EACN,WAAW,EAAG,cAAc,KAVR;GACtB,QAAQ;GACR,MAAM;GACN,UAAU;GACX,CAM2D,IAAW,EAAU;YAH/E,CAKE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAU;cAA2C;IAAa,CAAA,EACvE,KAAQ,kBAAC,OAAD;IAAK,WAAU;cAAuB;IAAW,CAAA,CACtD;MACL,IACC,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,EACjC,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA,CAC7B;OAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ;IACG,CAAA,EACL,MAAoB,QACnB,kBAAC,GAAD;IACE,SAAS,IAAa,YAAY;IAClC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,EACK;MAEN;MACL,KAAY,kBAAC,KAAD;GAAG,WAAU;aAAoC;GAAa,CAAA,CACvE,EAAA,CAAA,CAEE;;AAcd,QAXI,IAEA,kBAAC,GAAD;EACE,IAAI;EACJ,WAAU;YAET;EACI,CAAA,GAIJ;;AAMT,SAAgB,EAAY,EAAE,aAAU,gBAA0D;AAChG,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,cAAc,6CAA6C,EAAU;EACrF;EACG,CAAA"}
|
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
import { cn as e } from "../utils/cn.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Fragment as t, jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
3
|
+
import { AuroraBackground as i, GradientText as a } from "@burdenoff/fe-libs/ui";
|
|
3
4
|
//#region src/admin/components/PageLayout.tsx
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
function o({ title: o, description: s, actions: c, children: l, className: u, gradientWord: d, aurora: f = !1 }) {
|
|
6
|
+
let p = o, m = null;
|
|
7
|
+
return d && o.endsWith(d) && o.length > d.length ? (p = o.slice(0, o.length - d.length), m = d) : d && o === d && (p = "", m = d), /* @__PURE__ */ r("div", {
|
|
8
|
+
className: e("flex h-full flex-col", u),
|
|
9
|
+
children: [/* @__PURE__ */ r("header", {
|
|
10
|
+
className: e("relative flex flex-col gap-4 border-b border-border-seam bg-bg-surface px-6 py-4 sm:flex-row sm:items-center sm:justify-between", f && "overflow-hidden"),
|
|
11
|
+
children: [
|
|
12
|
+
f && /* @__PURE__ */ n(i, { intensity: "subtle" }),
|
|
13
|
+
/* @__PURE__ */ r("div", {
|
|
14
|
+
className: "min-w-0 flex-1",
|
|
15
|
+
children: [/* @__PURE__ */ n("h1", {
|
|
16
|
+
className: "text-2xl font-bold tracking-tight text-text-primary sm:text-3xl",
|
|
17
|
+
children: m === null ? o : /* @__PURE__ */ r(t, { children: [p, /* @__PURE__ */ n(a, { children: m })] })
|
|
18
|
+
}), s && /* @__PURE__ */ n("p", {
|
|
19
|
+
className: "mt-1 text-sm text-text-secondary sm:text-base",
|
|
20
|
+
children: s
|
|
21
|
+
})]
|
|
22
|
+
}),
|
|
23
|
+
c && /* @__PURE__ */ n("div", {
|
|
24
|
+
className: "flex flex-shrink-0 flex-wrap items-center gap-2",
|
|
25
|
+
children: c
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
}), /* @__PURE__ */ n("div", {
|
|
23
29
|
className: "flex-1 overflow-auto p-6",
|
|
24
|
-
children:
|
|
30
|
+
children: l
|
|
25
31
|
})]
|
|
26
32
|
});
|
|
27
33
|
}
|
|
28
34
|
//#endregion
|
|
29
|
-
export {
|
|
35
|
+
export { o as PageLayout };
|
|
30
36
|
|
|
31
37
|
//# sourceMappingURL=PageLayout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/admin/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n /** Page title */\n title: string;\n /** Optional description */\n description?: string;\n /** Actions to display in the header (buttons, etc.) */\n actions?: ReactNode;\n /** Page content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Standard page layout component with title, description, and content area\n */\nexport function PageLayout({
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/admin/components/PageLayout.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { AuroraBackground, GradientText } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ninterface PageLayoutProps {\n /** Page title */\n title: string;\n /** Optional description */\n description?: string;\n /** Actions to display in the header (buttons, etc.) */\n actions?: ReactNode;\n /** Page content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n /**\n * When set, the trailing words of the title are clipped with a brand\n * gradient (Boff UI 2.0 hero treatment). The leading text stays solid.\n * Provide the exact substring that appears at the end of `title`.\n */\n gradientWord?: string;\n /**\n * Render the living aurora backdrop behind the page header (Boff UI 2.0).\n * Use at most ONE per MFE — set on the primary/landing page header only.\n */\n aurora?: boolean;\n}\n\n/**\n * Standard page layout component with title, description, and content area\n */\nexport function PageLayout({\n title,\n description,\n actions,\n children,\n className,\n gradientWord,\n aurora = false,\n}: PageLayoutProps) {\n // Split the title so only the trailing key noun is gradient-clipped (ONE per page).\n let leading = title;\n let gradient: string | null = null;\n if (gradientWord && title.endsWith(gradientWord) && title.length > gradientWord.length) {\n leading = title.slice(0, title.length - gradientWord.length);\n gradient = gradientWord;\n } else if (gradientWord && title === gradientWord) {\n leading = '';\n gradient = gradientWord;\n }\n\n return (\n <div className={cn('flex h-full flex-col', className)}>\n {/* Page Header */}\n <header\n className={cn(\n 'relative flex flex-col gap-4 border-b border-border-seam bg-bg-surface px-6 py-4 sm:flex-row sm:items-center sm:justify-between',\n aurora && 'overflow-hidden'\n )}\n >\n {aurora && <AuroraBackground intensity=\"subtle\" />}\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"text-2xl font-bold tracking-tight text-text-primary sm:text-3xl\">\n {gradient !== null ? (\n <>\n {leading}\n <GradientText>{gradient}</GradientText>\n </>\n ) : (\n title\n )}\n </h1>\n {description && (\n <p className=\"mt-1 text-sm text-text-secondary sm:text-base\">{description}</p>\n )}\n </div>\n {actions && (\n <div className=\"flex flex-shrink-0 flex-wrap items-center gap-2\">{actions}</div>\n )}\n </header>\n\n {/* Page Content — must not use a <main> landmark here because the\n host shell (AppShellLayout) already renders one; nesting <main>\n elements is invalid HTML and an a11y violation. */}\n <div className=\"flex-1 overflow-auto p-6\">{children}</div>\n </div>\n );\n}\n\ninterface PageSectionProps {\n /** Section title */\n title?: string;\n /** Optional description */\n description?: string;\n /** Section content */\n children: ReactNode;\n /** Additional class names */\n className?: string;\n}\n\n/**\n * Section within a page\n */\nexport function PageSection({ title, description, children, className }: PageSectionProps) {\n return (\n <section className={cn('space-y-4', className)}>\n {(title || description) && (\n <div>\n {title && <h2 className=\"text-lg font-semibold text-text-primary\">{title}</h2>}\n {description && <p className=\"text-sm text-text-secondary\">{description}</p>}\n </div>\n )}\n {children}\n </section>\n );\n}\n"],"mappings":";;;;AA+BA,SAAgB,EAAW,EACzB,UACA,gBACA,YACA,aACA,cACA,iBACA,YAAS,MACS;CAElB,IAAI,IAAU,GACV,IAA0B;AAS9B,QARI,KAAgB,EAAM,SAAS,EAAa,IAAI,EAAM,SAAS,EAAa,UAC9E,IAAU,EAAM,MAAM,GAAG,EAAM,SAAS,EAAa,OAAO,EAC5D,IAAW,KACF,KAAgB,MAAU,MACnC,IAAU,IACV,IAAW,IAIX,kBAAC,OAAD;EAAK,WAAW,EAAG,wBAAwB,EAAU;YAArD,CAEE,kBAAC,UAAD;GACE,WAAW,EACT,mIACA,KAAU,kBACX;aAJH;IAMG,KAAU,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA;IAClD,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,MAAa,OAMZ,IALA,kBAAA,GAAA,EAAA,UAAA,CACG,GACD,kBAAC,GAAD,EAAA,UAAe,GAAwB,CAAA,CACtC,EAAA,CAAA;MAIF,CAAA,EACJ,KACC,kBAAC,KAAD;MAAG,WAAU;gBAAiD;MAAgB,CAAA,CAE5E;;IACL,KACC,kBAAC,OAAD;KAAK,WAAU;eAAmD;KAAc,CAAA;IAE3E;MAKT,kBAAC,OAAD;GAAK,WAAU;GAA4B;GAAe,CAAA,CACtD"}
|
|
@@ -5,7 +5,7 @@ import { Fragment as n, jsx as r } from "react/jsx-runtime";
|
|
|
5
5
|
var i = ({ allowed: i, isLoading: a, pageTitle: o, denyMessage: s, children: c }) => a ? /* @__PURE__ */ r(e, {
|
|
6
6
|
title: o,
|
|
7
7
|
children: /* @__PURE__ */ r("div", {
|
|
8
|
-
className: "text-center py-16 text-
|
|
8
|
+
className: "text-center py-16 text-text-secondary",
|
|
9
9
|
children: "Loading permissions..."
|
|
10
10
|
})
|
|
11
11
|
}) : i ? /* @__PURE__ */ r(n, { children: c }) : /* @__PURE__ */ r(e, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/admin/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { PageLayout } from './PageLayout';\nimport { AccessDenied } from './AccessDenied';\n\ninterface PermissionGuardProps {\n allowed: boolean;\n isLoading: boolean;\n pageTitle: string;\n denyMessage?: string;\n children: ReactNode;\n}\n\nexport const PermissionGuard: FC<PermissionGuardProps> = ({\n allowed,\n isLoading,\n pageTitle,\n denyMessage,\n children,\n}) => {\n if (isLoading)\n return (\n <PageLayout title={pageTitle}>\n <div className=\"text-center py-16 text-
|
|
1
|
+
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/admin/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from 'react';\nimport { PageLayout } from './PageLayout';\nimport { AccessDenied } from './AccessDenied';\n\ninterface PermissionGuardProps {\n allowed: boolean;\n isLoading: boolean;\n pageTitle: string;\n denyMessage?: string;\n children: ReactNode;\n}\n\nexport const PermissionGuard: FC<PermissionGuardProps> = ({\n allowed,\n isLoading,\n pageTitle,\n denyMessage,\n children,\n}) => {\n if (isLoading)\n return (\n <PageLayout title={pageTitle}>\n <div className=\"text-center py-16 text-text-secondary\">Loading permissions...</div>\n </PageLayout>\n );\n if (!allowed)\n return (\n <PageLayout title={pageTitle}>\n <AccessDenied message={denyMessage} />\n </PageLayout>\n );\n return <>{children}</>;\n};\n"],"mappings":";;;;AAYA,IAAa,KAA6C,EACxD,YACA,cACA,cACA,gBACA,kBAEI,IAEA,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,OAAD;EAAK,WAAU;YAAwC;EAA4B,CAAA;CACxE,CAAA,GAEZ,IAME,kBAAA,GAAA,EAAG,aAAY,CAAA,GAJlB,kBAAC,GAAD;CAAY,OAAO;WACjB,kBAAC,GAAD,EAAc,SAAS,GAAe,CAAA;CAC3B,CAAA"}
|
|
@@ -38,7 +38,7 @@ function p({ actions: a, ariaLabel: o = "Row actions", stopPropagation: s = !0 }
|
|
|
38
38
|
children: /* @__PURE__ */ d(c, { className: "h-4 w-4" })
|
|
39
39
|
}), l && /* @__PURE__ */ d("div", {
|
|
40
40
|
ref: m,
|
|
41
|
-
className: e("absolute right-0 top-full z-50 mt-1 min-w-[160px] rounded-md border border-border bg-popover p-1 shadow-lg", "animate-in fade-in-0 zoom-in-95 duration-100"),
|
|
41
|
+
className: e("absolute right-0 top-full z-50 mt-1 min-w-[160px] rounded-md border border-border-subtle bg-popover p-1 shadow-lg", "animate-in fade-in-0 zoom-in-95 duration-100"),
|
|
42
42
|
role: "menu",
|
|
43
43
|
children: a.map((t, n) => /* @__PURE__ */ f("div", { children: [t.separator && n > 0 && /* @__PURE__ */ d("div", {
|
|
44
44
|
className: "my-1 h-px bg-border",
|
|
@@ -47,7 +47,7 @@ function p({ actions: a, ariaLabel: o = "Row actions", stopPropagation: s = !0 }
|
|
|
47
47
|
role: "menuitem",
|
|
48
48
|
onClick: (e) => g(t, e),
|
|
49
49
|
disabled: t.disabled,
|
|
50
|
-
className: e("flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors", "focus:bg-accent focus:outline-none", t.disabled ? "cursor-not-allowed opacity-50" : t.destructive ? "text-
|
|
50
|
+
className: e("flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors", "focus:bg-accent focus:outline-none", t.disabled ? "cursor-not-allowed opacity-50" : t.destructive ? "text-status-error-text hover:bg-status-error-bg/10 focus:bg-status-error-bg/10" : "text-text-primary hover:bg-accent"),
|
|
51
51
|
children: [t.icon && /* @__PURE__ */ d("span", {
|
|
52
52
|
className: "h-4 w-4",
|
|
53
53
|
children: t.icon
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RowActions.js","names":[],"sources":["../../../src/admin/components/RowActions.tsx"],"sourcesContent":["import { useState, useRef, useEffect, type ReactNode } from 'react';\nimport { MoreHorizontal, Pencil, Trash2, Eye, Copy, ExternalLink } from 'lucide-react';\nimport { Button } from './ui';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RowAction {\n /** Unique key for the action */\n key: string;\n /** Display label */\n label: string;\n /** Icon component */\n icon?: ReactNode;\n /** Action handler */\n onClick: () => void;\n /** Whether this is a destructive action */\n destructive?: boolean;\n /** Whether the action is disabled */\n disabled?: boolean;\n /** Separator before this item */\n separator?: boolean;\n}\n\nexport interface RowActionsProps {\n /** Actions to display */\n actions: RowAction[];\n /** Accessibility label */\n ariaLabel?: string;\n /** Stop event propagation on click */\n stopPropagation?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * RowActions - Dropdown menu for table row actions\n */\nexport function RowActions({\n actions,\n ariaLabel = 'Row actions',\n stopPropagation = true,\n}: RowActionsProps) {\n const [open, setOpen] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n // Close on click outside\n useEffect(() => {\n if (!open) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [open]);\n\n // Close on escape\n useEffect(() => {\n if (!open) return;\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setOpen(false);\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open]);\n\n const handleTriggerClick = (e: React.MouseEvent) => {\n if (stopPropagation) {\n e.stopPropagation();\n }\n setOpen(!open);\n };\n\n const handleActionClick = (action: RowAction, e: React.MouseEvent) => {\n if (stopPropagation) {\n e.stopPropagation();\n }\n if (!action.disabled) {\n setOpen(false);\n action.onClick();\n }\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleTriggerClick}\n aria-label={ariaLabel}\n aria-haspopup=\"true\"\n aria-expanded={open}\n className=\"h-8 w-8\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n\n {open && (\n <div\n ref={menuRef}\n className={cn(\n 'absolute right-0 top-full z-50 mt-1 min-w-[160px] rounded-md border border-border bg-popover p-1 shadow-lg',\n 'animate-in fade-in-0 zoom-in-95 duration-100'\n )}\n role=\"menu\"\n >\n {actions.map((action, index) => (\n <div key={action.key}>\n {action.separator && index > 0 && (\n <div className=\"my-1 h-px bg-border\" role=\"separator\" />\n )}\n <button\n role=\"menuitem\"\n onClick={(e) => handleActionClick(action, e)}\n disabled={action.disabled}\n className={cn(\n 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors',\n 'focus:bg-accent focus:outline-none',\n action.disabled\n ? 'cursor-not-allowed opacity-50'\n : action.destructive\n ? 'text-
|
|
1
|
+
{"version":3,"file":"RowActions.js","names":[],"sources":["../../../src/admin/components/RowActions.tsx"],"sourcesContent":["import { useState, useRef, useEffect, type ReactNode } from 'react';\nimport { MoreHorizontal, Pencil, Trash2, Eye, Copy, ExternalLink } from 'lucide-react';\nimport { Button } from './ui';\nimport { cn } from '../utils/cn';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RowAction {\n /** Unique key for the action */\n key: string;\n /** Display label */\n label: string;\n /** Icon component */\n icon?: ReactNode;\n /** Action handler */\n onClick: () => void;\n /** Whether this is a destructive action */\n destructive?: boolean;\n /** Whether the action is disabled */\n disabled?: boolean;\n /** Separator before this item */\n separator?: boolean;\n}\n\nexport interface RowActionsProps {\n /** Actions to display */\n actions: RowAction[];\n /** Accessibility label */\n ariaLabel?: string;\n /** Stop event propagation on click */\n stopPropagation?: boolean;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\n/**\n * RowActions - Dropdown menu for table row actions\n */\nexport function RowActions({\n actions,\n ariaLabel = 'Row actions',\n stopPropagation = true,\n}: RowActionsProps) {\n const [open, setOpen] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n // Close on click outside\n useEffect(() => {\n if (!open) return;\n\n const handleClickOutside = (e: MouseEvent) => {\n if (containerRef.current && !containerRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [open]);\n\n // Close on escape\n useEffect(() => {\n if (!open) return;\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setOpen(false);\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [open]);\n\n const handleTriggerClick = (e: React.MouseEvent) => {\n if (stopPropagation) {\n e.stopPropagation();\n }\n setOpen(!open);\n };\n\n const handleActionClick = (action: RowAction, e: React.MouseEvent) => {\n if (stopPropagation) {\n e.stopPropagation();\n }\n if (!action.disabled) {\n setOpen(false);\n action.onClick();\n }\n };\n\n return (\n <div ref={containerRef} className=\"relative\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleTriggerClick}\n aria-label={ariaLabel}\n aria-haspopup=\"true\"\n aria-expanded={open}\n className=\"h-8 w-8\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </Button>\n\n {open && (\n <div\n ref={menuRef}\n className={cn(\n 'absolute right-0 top-full z-50 mt-1 min-w-[160px] rounded-md border border-border-subtle bg-popover p-1 shadow-lg',\n 'animate-in fade-in-0 zoom-in-95 duration-100'\n )}\n role=\"menu\"\n >\n {actions.map((action, index) => (\n <div key={action.key}>\n {action.separator && index > 0 && (\n <div className=\"my-1 h-px bg-border\" role=\"separator\" />\n )}\n <button\n role=\"menuitem\"\n onClick={(e) => handleActionClick(action, e)}\n disabled={action.disabled}\n className={cn(\n 'flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-sm transition-colors',\n 'focus:bg-accent focus:outline-none',\n action.disabled\n ? 'cursor-not-allowed opacity-50'\n : action.destructive\n ? 'text-status-error-text hover:bg-status-error-bg/10 focus:bg-status-error-bg/10'\n : 'text-text-primary hover:bg-accent'\n )}\n >\n {action.icon && <span className=\"h-4 w-4\">{action.icon}</span>}\n {action.label}\n </button>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n// ============================================================================\n// Pre-built Action Helpers\n// ============================================================================\n\n/**\n * Create common row actions\n */\nexport function createRowActions(config: {\n onView?: () => void;\n onEdit?: () => void;\n onDuplicate?: () => void;\n onOpenExternal?: () => void;\n onDelete?: () => void;\n}): RowAction[] {\n const actions: RowAction[] = [];\n\n if (config.onView) {\n actions.push({\n key: 'view',\n label: 'View',\n icon: <Eye className=\"h-4 w-4\" />,\n onClick: config.onView,\n });\n }\n\n if (config.onEdit) {\n actions.push({\n key: 'edit',\n label: 'Edit',\n icon: <Pencil className=\"h-4 w-4\" />,\n onClick: config.onEdit,\n });\n }\n\n if (config.onDuplicate) {\n actions.push({\n key: 'duplicate',\n label: 'Duplicate',\n icon: <Copy className=\"h-4 w-4\" />,\n onClick: config.onDuplicate,\n });\n }\n\n if (config.onOpenExternal) {\n actions.push({\n key: 'open-external',\n label: 'Open in new tab',\n icon: <ExternalLink className=\"h-4 w-4\" />,\n onClick: config.onOpenExternal,\n });\n }\n\n if (config.onDelete) {\n actions.push({\n key: 'delete',\n label: 'Delete',\n icon: <Trash2 className=\"h-4 w-4\" />,\n onClick: config.onDelete,\n destructive: true,\n separator: actions.length > 0,\n });\n }\n\n return actions;\n}\n"],"mappings":";;;;;;AA0CA,SAAgB,EAAW,EACzB,YACA,eAAY,eACZ,qBAAkB,MACA;CAClB,IAAM,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAe,EAAuB,KAAK,EAC3C,IAAU,EAAuB,KAAK;AAiB5C,CAdA,QAAgB;AACd,MAAI,CAAC,EAAM;EAEX,IAAM,KAAsB,MAAkB;AAC5C,GAAI,EAAa,WAAW,CAAC,EAAa,QAAQ,SAAS,EAAE,OAAe,IAC1E,EAAQ,GAAM;;AAKlB,SADA,SAAS,iBAAiB,aAAa,EAAmB,QAC7C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,CAAC,EAAK,CAAC,EAGV,QAAgB;AACd,MAAI,CAAC,EAAM;EAEX,IAAM,KAAgB,MAAqB;AACzC,GAAI,EAAE,QAAQ,YACZ,EAAQ,GAAM;;AAKlB,SADA,SAAS,iBAAiB,WAAW,EAAa,QACrC,SAAS,oBAAoB,WAAW,EAAa;IACjE,CAAC,EAAK,CAAC;CAEV,IAAM,KAAsB,MAAwB;AAIlD,EAHI,KACF,EAAE,iBAAiB,EAErB,EAAQ,CAAC,EAAK;IAGV,KAAqB,GAAmB,MAAwB;AAIpE,EAHI,KACF,EAAE,iBAAiB,EAEhB,EAAO,aACV,EAAQ,GAAM,EACd,EAAO,SAAS;;AAIpB,QACE,kBAAC,OAAD;EAAK,KAAK;EAAc,WAAU;YAAlC,CACE,kBAAC,GAAD;GACE,SAAQ;GACR,MAAK;GACL,SAAS;GACT,cAAY;GACZ,iBAAc;GACd,iBAAe;GACf,WAAU;aAEV,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;GAC/B,CAAA,EAER,KACC,kBAAC,OAAD;GACE,KAAK;GACL,WAAW,EACT,qHACA,+CACD;GACD,MAAK;aAEJ,EAAQ,KAAK,GAAQ,MACpB,kBAAC,OAAD,EAAA,UAAA,CACG,EAAO,aAAa,IAAQ,KAC3B,kBAAC,OAAD;IAAK,WAAU;IAAsB,MAAK;IAAc,CAAA,EAE1D,kBAAC,UAAD;IACE,MAAK;IACL,UAAU,MAAM,EAAkB,GAAQ,EAAE;IAC5C,UAAU,EAAO;IACjB,WAAW,EACT,mFACA,sCACA,EAAO,WACH,kCACA,EAAO,cACL,mFACA,oCACP;cAZH,CAcG,EAAO,QAAQ,kBAAC,QAAD;KAAM,WAAU;eAAW,EAAO;KAAY,CAAA,EAC7D,EAAO,MACD;MACL,EAAA,EArBI,EAAO,IAqBX,CACN;GACE,CAAA,CAEJ;;;AAWV,SAAgB,EAAiB,GAMjB;CACd,IAAM,IAAuB,EAAE;AAiD/B,QA/CI,EAAO,UACT,EAAQ,KAAK;EACX,KAAK;EACL,OAAO;EACP,MAAM,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;EACjC,SAAS,EAAO;EACjB,CAAC,EAGA,EAAO,UACT,EAAQ,KAAK;EACX,KAAK;EACL,OAAO;EACP,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACpC,SAAS,EAAO;EACjB,CAAC,EAGA,EAAO,eACT,EAAQ,KAAK;EACX,KAAK;EACL,OAAO;EACP,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;EAClC,SAAS,EAAO;EACjB,CAAC,EAGA,EAAO,kBACT,EAAQ,KAAK;EACX,KAAK;EACL,OAAO;EACP,MAAM,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;EAC1C,SAAS,EAAO;EACjB,CAAC,EAGA,EAAO,YACT,EAAQ,KAAK;EACX,KAAK;EACL,OAAO;EACP,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;EACpC,SAAS,EAAO;EAChB,aAAa;EACb,WAAW,EAAQ,SAAS;EAC7B,CAAC,EAGG"}
|
|
@@ -28,7 +28,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
28
28
|
className: "flex items-center gap-2 text-lg",
|
|
29
29
|
children: [/* @__PURE__ */ x(y, { className: "h-4 w-4" }), A("admin.tenantMembers.title", "Tenant Members")]
|
|
30
30
|
}) }), /* @__PURE__ */ x(r, { children: /* @__PURE__ */ x("p", {
|
|
31
|
-
className: "text-sm text-
|
|
31
|
+
className: "text-sm text-text-secondary",
|
|
32
32
|
children: A("admin.tenantMembers.sharedImplicit", "This is the shared tenant. Every authenticated user has implicit access — no explicit membership rows are stored.")
|
|
33
33
|
}) })] });
|
|
34
34
|
let J = async (e) => {
|
|
@@ -71,7 +71,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
71
71
|
/* @__PURE__ */ x(y, { className: "h-4 w-4" }),
|
|
72
72
|
A("admin.tenantMembers.title", "Tenant Members"),
|
|
73
73
|
Q > 0 && /* @__PURE__ */ S("span", {
|
|
74
|
-
className: "text-sm text-
|
|
74
|
+
className: "text-sm text-text-secondary",
|
|
75
75
|
children: [
|
|
76
76
|
"(",
|
|
77
77
|
Q,
|
|
@@ -83,7 +83,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
83
83
|
className: "space-y-4",
|
|
84
84
|
children: [
|
|
85
85
|
/* @__PURE__ */ x("p", {
|
|
86
|
-
className: "text-sm text-
|
|
86
|
+
className: "text-sm text-text-secondary",
|
|
87
87
|
children: A("admin.tenantMembers.description", "Grant a user explicit access to this dedicated tenant. They will be able to create organizations under it and see it in their tenant switcher.")
|
|
88
88
|
}),
|
|
89
89
|
O && /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ S("form", {
|
|
@@ -93,7 +93,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
93
93
|
/* @__PURE__ */ S("div", {
|
|
94
94
|
className: "flex-1 min-w-[240px]",
|
|
95
95
|
children: [/* @__PURE__ */ x("label", {
|
|
96
|
-
className: "mb-1 block text-xs font-medium text-
|
|
96
|
+
className: "mb-1 block text-xs font-medium text-text-secondary",
|
|
97
97
|
children: A("admin.tenantMembers.form.userIdLabel", "User ID")
|
|
98
98
|
}), /* @__PURE__ */ x(o, {
|
|
99
99
|
value: M.userId,
|
|
@@ -109,7 +109,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
109
109
|
/* @__PURE__ */ S("div", {
|
|
110
110
|
className: "min-w-[140px]",
|
|
111
111
|
children: [/* @__PURE__ */ x("label", {
|
|
112
|
-
className: "mb-1 block text-xs font-medium text-
|
|
112
|
+
className: "mb-1 block text-xs font-medium text-text-secondary",
|
|
113
113
|
children: A("admin.tenantMembers.form.roleLabel", "Role")
|
|
114
114
|
}), /* @__PURE__ */ S("select", {
|
|
115
115
|
value: M.role,
|
|
@@ -118,7 +118,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
118
118
|
role: e.target.value
|
|
119
119
|
}),
|
|
120
120
|
disabled: W,
|
|
121
|
-
className: "h-9 w-full rounded-md border border-
|
|
121
|
+
className: "h-9 w-full rounded-md border border-border-default bg-bg-surface px-3 text-sm",
|
|
122
122
|
children: [/* @__PURE__ */ x("option", {
|
|
123
123
|
value: h.Member,
|
|
124
124
|
children: A("admin.tenantMembers.role.member", "Member")
|
|
@@ -136,25 +136,25 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
136
136
|
]
|
|
137
137
|
}), /* @__PURE__ */ x(s, {})] }),
|
|
138
138
|
B && Z.length === 0 ? /* @__PURE__ */ S("div", {
|
|
139
|
-
className: "flex h-24 items-center justify-center text-
|
|
139
|
+
className: "flex h-24 items-center justify-center text-text-secondary",
|
|
140
140
|
children: [/* @__PURE__ */ x(_, { className: "mr-2 h-4 w-4 animate-spin" }), A("admin.tenantMembers.loading", "Loading members...")]
|
|
141
141
|
}) : V ? /* @__PURE__ */ x("div", {
|
|
142
|
-
className: "rounded-md border border-
|
|
142
|
+
className: "rounded-md border border-status-error-border/50 bg-status-error-bg/10 p-3 text-sm text-status-error-text",
|
|
143
143
|
children: V.message
|
|
144
144
|
}) : Z.length === 0 ? /* @__PURE__ */ x("p", {
|
|
145
|
-
className: "text-sm text-
|
|
145
|
+
className: "text-sm text-text-secondary",
|
|
146
146
|
children: A("admin.tenantMembers.empty", "No explicit members yet. Grant access above to add one.")
|
|
147
147
|
}) : /* @__PURE__ */ S("div", {
|
|
148
148
|
className: "space-y-2",
|
|
149
149
|
children: [Z.map((e) => /* @__PURE__ */ S("div", {
|
|
150
|
-
className: "flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border p-3",
|
|
150
|
+
className: "flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border-subtle p-3",
|
|
151
151
|
children: [/* @__PURE__ */ S("div", {
|
|
152
152
|
className: "min-w-0 flex-1",
|
|
153
153
|
children: [/* @__PURE__ */ x("p", {
|
|
154
|
-
className: "truncate font-mono text-xs text-
|
|
154
|
+
className: "truncate font-mono text-xs text-text-primary",
|
|
155
155
|
children: e.userId
|
|
156
156
|
}), /* @__PURE__ */ x("p", {
|
|
157
|
-
className: "text-xs text-
|
|
157
|
+
className: "text-xs text-text-secondary",
|
|
158
158
|
children: A("admin.tenantMembers.addedAt", "Added {{when}} by {{by}}", {
|
|
159
159
|
when: c(e.createdAt),
|
|
160
160
|
by: e.addedBy
|
|
@@ -166,7 +166,7 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
166
166
|
value: e.role,
|
|
167
167
|
onChange: (t) => X(e.userId, t.target.value),
|
|
168
168
|
disabled: !O,
|
|
169
|
-
className: "h-8 rounded-md border border-
|
|
169
|
+
className: "h-8 rounded-md border border-border-default bg-bg-surface px-2 text-xs",
|
|
170
170
|
children: [/* @__PURE__ */ x("option", {
|
|
171
171
|
value: h.Member,
|
|
172
172
|
children: A("admin.tenantMembers.role.member", "Member")
|
|
@@ -180,13 +180,13 @@ function E({ tenantId: E, tenantType: D, canManage: O }) {
|
|
|
180
180
|
onClick: () => F({ userId: e.userId }),
|
|
181
181
|
disabled: K,
|
|
182
182
|
"aria-label": A("admin.tenantMembers.remove", "Remove"),
|
|
183
|
-
children: /* @__PURE__ */ x(v, { className: "h-4 w-4 text-
|
|
183
|
+
children: /* @__PURE__ */ x(v, { className: "h-4 w-4 text-status-error-text" })
|
|
184
184
|
})]
|
|
185
185
|
})]
|
|
186
186
|
}, e.id)), te && /* @__PURE__ */ S("div", {
|
|
187
187
|
className: "flex items-center justify-between gap-2 pt-2",
|
|
188
188
|
children: [/* @__PURE__ */ x("span", {
|
|
189
|
-
className: "text-xs text-
|
|
189
|
+
className: "text-xs text-text-secondary",
|
|
190
190
|
children: A("admin.tenantMembers.shownOfTotal", "Showing {{shown}} of {{total}}", {
|
|
191
191
|
shown: Z.length,
|
|
192
192
|
total: Q
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TenantMembersPanel.js","names":[],"sources":["../../../src/admin/components/TenantMembersPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Trash2, UserPlus, Users } from 'lucide-react';\nimport {\n useListTenantMembersQuery,\n useAddTenantMemberMutation,\n useRemoveTenantMemberMutation,\n useUpdateTenantMemberRoleMutation,\n type TenantMemberFieldsFragment,\n} from '../../generated/global-operations';\nimport { TenantMemberRole } from '../../generated/global-types';\nimport { Card, CardHeader, CardTitle, CardContent, Button, Input, Separator } from './ui';\nimport { useToast } from './Toast';\nimport { ConfirmDialog } from './ConfirmDialog';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { formatDate } from '../utils/formatters';\n\ninterface TenantMembersPanelProps {\n tenantId: string;\n tenantType: string;\n canManage: boolean;\n}\n\nconst EMPTY_ADD_FORM = { userId: '', role: TenantMemberRole.Member };\nconst PAGE_SIZE = 100;\n\n/**\n * Tenant Members panel (BOFF-2652) — super_admin UI to grant non-super-admin\n * users explicit access to a DEDICATED tenant. SHARED-tenant access is\n * implicit, so the panel is hidden for SHARED tenants.\n */\nexport function TenantMembersPanel({ tenantId, tenantType, canManage }: TenantMembersPanelProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const toast = useToast();\n\n const [addForm, setAddForm] = useState(EMPTY_ADD_FORM);\n const [removeTarget, setRemoveTarget] = useState<{ userId: string } | null>(null);\n // Pagination: each \"Load more\" click widens the window by PAGE_SIZE rows;\n // Apollo re-fetches the bigger page rather than appending in JS so we\n // don't need a custom merge type policy. (BOFF-2652 review)\n const [pageSize, setPageSize] = useState(PAGE_SIZE);\n\n const isShared = tenantType?.toUpperCase() === 'SHARED';\n\n const { data, loading, error, refetch } = useListTenantMembersQuery({\n variables: { tenantId, limit: pageSize, offset: 0 },\n skip: !tenantId || isShared,\n fetchPolicy: 'cache-and-network',\n });\n\n const [addMember, { loading: adding }] = useAddTenantMemberMutation();\n const [removeMember, { loading: removing }] = useRemoveTenantMemberMutation();\n const [updateRole] = useUpdateTenantMemberRoleMutation();\n\n if (isShared) {\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Users className=\"h-4 w-4\" />\n {tr('admin.tenantMembers.title', 'Tenant Members')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.tenantMembers.sharedImplicit',\n 'This is the shared tenant. Every authenticated user has implicit access — no explicit membership rows are stored.'\n )}\n </p>\n </CardContent>\n </Card>\n );\n }\n\n const handleAdd = async (e: React.FormEvent) => {\n e.preventDefault();\n const userId = addForm.userId.trim();\n if (!userId) return;\n try {\n await addMember({ variables: { input: { tenantId, userId, role: addForm.role } } });\n toast.success(\n tr('admin.tenantMembers.toast.addedTitle', 'Member added'),\n tr(\n 'admin.tenantMembers.toast.addedDescription',\n 'User {{userId}} can now access this tenant.',\n {\n userId,\n }\n )\n );\n setAddForm(EMPTY_ADD_FORM);\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.addFailed', 'Failed to add tenant member.')\n );\n }\n };\n\n const handleRemove = async () => {\n if (!removeTarget) return;\n const { userId } = removeTarget;\n try {\n await removeMember({ variables: { tenantId, userId } });\n toast.success(\n tr('admin.tenantMembers.toast.removedTitle', 'Member removed'),\n tr(\n 'admin.tenantMembers.toast.removedDescription',\n 'User {{userId}} no longer has access.',\n {\n userId,\n }\n )\n );\n setRemoveTarget(null);\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.removeFailed', 'Failed to remove tenant member.')\n );\n }\n };\n\n const handleRoleChange = async (userId: string, role: TenantMemberRole) => {\n try {\n await updateRole({ variables: { tenantId, userId, role } });\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.updateRoleFailed', 'Failed to update role.')\n );\n }\n };\n\n const members = data?.tenantMembers?.items ?? [];\n const total = data?.tenantMembers?.total ?? 0;\n const hasMore = data?.tenantMembers?.hasMore ?? false;\n const loadingMore = loading && members.length > 0;\n\n return (\n <>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Users className=\"h-4 w-4\" />\n {tr('admin.tenantMembers.title', 'Tenant Members')}\n {total > 0 && <span className=\"text-sm text-muted-foreground\">({total})</span>}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.tenantMembers.description',\n 'Grant a user explicit access to this dedicated tenant. They will be able to create organizations under it and see it in their tenant switcher.'\n )}\n </p>\n\n {canManage && (\n <>\n <form onSubmit={handleAdd} className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex-1 min-w-[240px]\">\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n {tr('admin.tenantMembers.form.userIdLabel', 'User ID')}\n </label>\n <Input\n value={addForm.userId}\n onChange={(e) => setAddForm({ ...addForm, userId: e.target.value })}\n placeholder={tr(\n 'admin.tenantMembers.form.userIdPlaceholder',\n 'UUID of the user to grant access'\n )}\n required\n disabled={adding}\n />\n </div>\n <div className=\"min-w-[140px]\">\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n {tr('admin.tenantMembers.form.roleLabel', 'Role')}\n </label>\n <select\n value={addForm.role}\n onChange={(e) =>\n setAddForm({ ...addForm, role: e.target.value as TenantMemberRole })\n }\n disabled={adding}\n className=\"h-9 w-full rounded-md border border-input bg-background px-3 text-sm\"\n >\n <option value={TenantMemberRole.Member}>\n {tr('admin.tenantMembers.role.member', 'Member')}\n </option>\n <option value={TenantMemberRole.Admin}>\n {tr('admin.tenantMembers.role.admin', 'Admin')}\n </option>\n </select>\n </div>\n <Button type=\"submit\" disabled={adding || !addForm.userId.trim()}>\n {adding ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <UserPlus className=\"mr-2 h-4 w-4\" />\n )}\n {tr('admin.tenantMembers.form.submit', 'Grant access')}\n </Button>\n </form>\n <Separator />\n </>\n )}\n\n {loading && members.length === 0 ? (\n <div className=\"flex h-24 items-center justify-center text-muted-foreground\">\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {tr('admin.tenantMembers.loading', 'Loading members...')}\n </div>\n ) : error ? (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive\">\n {error.message}\n </div>\n ) : members.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'admin.tenantMembers.empty',\n 'No explicit members yet. Grant access above to add one.'\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {members.map((m: TenantMemberFieldsFragment) => (\n <div\n key={m.id}\n className=\"flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-mono text-xs text-foreground\">{m.userId}</p>\n <p className=\"text-xs text-muted-foreground\">\n {tr('admin.tenantMembers.addedAt', 'Added {{when}} by {{by}}', {\n when: formatDate(m.createdAt),\n by: m.addedBy,\n })}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n value={m.role}\n onChange={(e) =>\n handleRoleChange(m.userId, e.target.value as TenantMemberRole)\n }\n disabled={!canManage}\n className=\"h-8 rounded-md border border-input bg-background px-2 text-xs\"\n >\n <option value={TenantMemberRole.Member}>\n {tr('admin.tenantMembers.role.member', 'Member')}\n </option>\n <option value={TenantMemberRole.Admin}>\n {tr('admin.tenantMembers.role.admin', 'Admin')}\n </option>\n </select>\n {canManage && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setRemoveTarget({ userId: m.userId })}\n disabled={removing}\n aria-label={tr('admin.tenantMembers.remove', 'Remove')}\n >\n <Trash2 className=\"h-4 w-4 text-destructive\" />\n </Button>\n )}\n </div>\n </div>\n ))}\n {hasMore && (\n <div className=\"flex items-center justify-between gap-2 pt-2\">\n <span className=\"text-xs text-muted-foreground\">\n {tr('admin.tenantMembers.shownOfTotal', 'Showing {{shown}} of {{total}}', {\n shown: members.length,\n total,\n })}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPageSize((prev) => prev + PAGE_SIZE)}\n disabled={loadingMore}\n >\n {loadingMore ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {tr('admin.tenantMembers.loadMore', 'Load more')}\n </Button>\n </div>\n )}\n </div>\n )}\n </CardContent>\n </Card>\n\n <ConfirmDialog\n open={Boolean(removeTarget)}\n title={tr('admin.tenantMembers.confirmRemove.title', 'Revoke tenant access?')}\n description={tr(\n 'admin.tenantMembers.confirmRemove.description',\n 'User {{userId}} will no longer be able to create organizations in this tenant. Existing organization and workspace memberships are not affected.',\n { userId: removeTarget?.userId ?? '' }\n )}\n confirmLabel={tr('admin.tenantMembers.confirmRemove.confirm', 'Revoke')}\n variant=\"destructive\"\n loading={removing}\n onConfirm={handleRemove}\n onClose={() => setRemoveTarget(null)}\n />\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TenantMembersPanel.js","names":[],"sources":["../../../src/admin/components/TenantMembersPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Loader2, Trash2, UserPlus, Users } from 'lucide-react';\nimport {\n useListTenantMembersQuery,\n useAddTenantMemberMutation,\n useRemoveTenantMemberMutation,\n useUpdateTenantMemberRoleMutation,\n type TenantMemberFieldsFragment,\n} from '../../generated/global-operations';\nimport { TenantMemberRole } from '../../generated/global-types';\nimport { Card, CardHeader, CardTitle, CardContent, Button, Input, Separator } from './ui';\nimport { useToast } from './Toast';\nimport { ConfirmDialog } from './ConfirmDialog';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\nimport { formatDate } from '../utils/formatters';\n\ninterface TenantMembersPanelProps {\n tenantId: string;\n tenantType: string;\n canManage: boolean;\n}\n\nconst EMPTY_ADD_FORM = { userId: '', role: TenantMemberRole.Member };\nconst PAGE_SIZE = 100;\n\n/**\n * Tenant Members panel (BOFF-2652) — super_admin UI to grant non-super-admin\n * users explicit access to a DEDICATED tenant. SHARED-tenant access is\n * implicit, so the panel is hidden for SHARED tenants.\n */\nexport function TenantMembersPanel({ tenantId, tenantType, canManage }: TenantMembersPanelProps) {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const toast = useToast();\n\n const [addForm, setAddForm] = useState(EMPTY_ADD_FORM);\n const [removeTarget, setRemoveTarget] = useState<{ userId: string } | null>(null);\n // Pagination: each \"Load more\" click widens the window by PAGE_SIZE rows;\n // Apollo re-fetches the bigger page rather than appending in JS so we\n // don't need a custom merge type policy. (BOFF-2652 review)\n const [pageSize, setPageSize] = useState(PAGE_SIZE);\n\n const isShared = tenantType?.toUpperCase() === 'SHARED';\n\n const { data, loading, error, refetch } = useListTenantMembersQuery({\n variables: { tenantId, limit: pageSize, offset: 0 },\n skip: !tenantId || isShared,\n fetchPolicy: 'cache-and-network',\n });\n\n const [addMember, { loading: adding }] = useAddTenantMemberMutation();\n const [removeMember, { loading: removing }] = useRemoveTenantMemberMutation();\n const [updateRole] = useUpdateTenantMemberRoleMutation();\n\n if (isShared) {\n return (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Users className=\"h-4 w-4\" />\n {tr('admin.tenantMembers.title', 'Tenant Members')}\n </CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.tenantMembers.sharedImplicit',\n 'This is the shared tenant. Every authenticated user has implicit access — no explicit membership rows are stored.'\n )}\n </p>\n </CardContent>\n </Card>\n );\n }\n\n const handleAdd = async (e: React.FormEvent) => {\n e.preventDefault();\n const userId = addForm.userId.trim();\n if (!userId) return;\n try {\n await addMember({ variables: { input: { tenantId, userId, role: addForm.role } } });\n toast.success(\n tr('admin.tenantMembers.toast.addedTitle', 'Member added'),\n tr(\n 'admin.tenantMembers.toast.addedDescription',\n 'User {{userId}} can now access this tenant.',\n {\n userId,\n }\n )\n );\n setAddForm(EMPTY_ADD_FORM);\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.addFailed', 'Failed to add tenant member.')\n );\n }\n };\n\n const handleRemove = async () => {\n if (!removeTarget) return;\n const { userId } = removeTarget;\n try {\n await removeMember({ variables: { tenantId, userId } });\n toast.success(\n tr('admin.tenantMembers.toast.removedTitle', 'Member removed'),\n tr(\n 'admin.tenantMembers.toast.removedDescription',\n 'User {{userId}} no longer has access.',\n {\n userId,\n }\n )\n );\n setRemoveTarget(null);\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.removeFailed', 'Failed to remove tenant member.')\n );\n }\n };\n\n const handleRoleChange = async (userId: string, role: TenantMemberRole) => {\n try {\n await updateRole({ variables: { tenantId, userId, role } });\n await refetch();\n } catch (err) {\n toast.error(\n tr('admin.tenantMembers.toast.errorTitle', 'Error'),\n err instanceof Error\n ? err.message\n : tr('admin.tenantMembers.toast.updateRoleFailed', 'Failed to update role.')\n );\n }\n };\n\n const members = data?.tenantMembers?.items ?? [];\n const total = data?.tenantMembers?.total ?? 0;\n const hasMore = data?.tenantMembers?.hasMore ?? false;\n const loadingMore = loading && members.length > 0;\n\n return (\n <>\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Users className=\"h-4 w-4\" />\n {tr('admin.tenantMembers.title', 'Tenant Members')}\n {total > 0 && <span className=\"text-sm text-text-secondary\">({total})</span>}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.tenantMembers.description',\n 'Grant a user explicit access to this dedicated tenant. They will be able to create organizations under it and see it in their tenant switcher.'\n )}\n </p>\n\n {canManage && (\n <>\n <form onSubmit={handleAdd} className=\"flex flex-wrap items-end gap-2\">\n <div className=\"flex-1 min-w-[240px]\">\n <label className=\"mb-1 block text-xs font-medium text-text-secondary\">\n {tr('admin.tenantMembers.form.userIdLabel', 'User ID')}\n </label>\n <Input\n value={addForm.userId}\n onChange={(e) => setAddForm({ ...addForm, userId: e.target.value })}\n placeholder={tr(\n 'admin.tenantMembers.form.userIdPlaceholder',\n 'UUID of the user to grant access'\n )}\n required\n disabled={adding}\n />\n </div>\n <div className=\"min-w-[140px]\">\n <label className=\"mb-1 block text-xs font-medium text-text-secondary\">\n {tr('admin.tenantMembers.form.roleLabel', 'Role')}\n </label>\n <select\n value={addForm.role}\n onChange={(e) =>\n setAddForm({ ...addForm, role: e.target.value as TenantMemberRole })\n }\n disabled={adding}\n className=\"h-9 w-full rounded-md border border-border-default bg-bg-surface px-3 text-sm\"\n >\n <option value={TenantMemberRole.Member}>\n {tr('admin.tenantMembers.role.member', 'Member')}\n </option>\n <option value={TenantMemberRole.Admin}>\n {tr('admin.tenantMembers.role.admin', 'Admin')}\n </option>\n </select>\n </div>\n <Button type=\"submit\" disabled={adding || !addForm.userId.trim()}>\n {adding ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n ) : (\n <UserPlus className=\"mr-2 h-4 w-4\" />\n )}\n {tr('admin.tenantMembers.form.submit', 'Grant access')}\n </Button>\n </form>\n <Separator />\n </>\n )}\n\n {loading && members.length === 0 ? (\n <div className=\"flex h-24 items-center justify-center text-text-secondary\">\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />\n {tr('admin.tenantMembers.loading', 'Loading members...')}\n </div>\n ) : error ? (\n <div className=\"rounded-md border border-status-error-border/50 bg-status-error-bg/10 p-3 text-sm text-status-error-text\">\n {error.message}\n </div>\n ) : members.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'admin.tenantMembers.empty',\n 'No explicit members yet. Grant access above to add one.'\n )}\n </p>\n ) : (\n <div className=\"space-y-2\">\n {members.map((m: TenantMemberFieldsFragment) => (\n <div\n key={m.id}\n className=\"flex flex-wrap items-center justify-between gap-2 rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-mono text-xs text-text-primary\">{m.userId}</p>\n <p className=\"text-xs text-text-secondary\">\n {tr('admin.tenantMembers.addedAt', 'Added {{when}} by {{by}}', {\n when: formatDate(m.createdAt),\n by: m.addedBy,\n })}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <select\n value={m.role}\n onChange={(e) =>\n handleRoleChange(m.userId, e.target.value as TenantMemberRole)\n }\n disabled={!canManage}\n className=\"h-8 rounded-md border border-border-default bg-bg-surface px-2 text-xs\"\n >\n <option value={TenantMemberRole.Member}>\n {tr('admin.tenantMembers.role.member', 'Member')}\n </option>\n <option value={TenantMemberRole.Admin}>\n {tr('admin.tenantMembers.role.admin', 'Admin')}\n </option>\n </select>\n {canManage && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setRemoveTarget({ userId: m.userId })}\n disabled={removing}\n aria-label={tr('admin.tenantMembers.remove', 'Remove')}\n >\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n )}\n </div>\n </div>\n ))}\n {hasMore && (\n <div className=\"flex items-center justify-between gap-2 pt-2\">\n <span className=\"text-xs text-text-secondary\">\n {tr('admin.tenantMembers.shownOfTotal', 'Showing {{shown}} of {{total}}', {\n shown: members.length,\n total,\n })}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setPageSize((prev) => prev + PAGE_SIZE)}\n disabled={loadingMore}\n >\n {loadingMore ? <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" /> : null}\n {tr('admin.tenantMembers.loadMore', 'Load more')}\n </Button>\n </div>\n )}\n </div>\n )}\n </CardContent>\n </Card>\n\n <ConfirmDialog\n open={Boolean(removeTarget)}\n title={tr('admin.tenantMembers.confirmRemove.title', 'Revoke tenant access?')}\n description={tr(\n 'admin.tenantMembers.confirmRemove.description',\n 'User {{userId}} will no longer be able to create organizations in this tenant. Existing organization and workspace memberships are not affected.',\n { userId: removeTarget?.userId ?? '' }\n )}\n confirmLabel={tr('admin.tenantMembers.confirmRemove.confirm', 'Revoke')}\n variant=\"destructive\"\n loading={removing}\n onConfirm={handleRemove}\n onClose={() => setRemoveTarget(null)}\n />\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
36
36
|
}), /* @__PURE__ */ S("button", {
|
|
37
37
|
type: "button",
|
|
38
38
|
onClick: () => M((e) => !e),
|
|
39
|
-
className: "flex items-center gap-1 text-sm text-
|
|
39
|
+
className: "flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
40
40
|
"aria-expanded": j,
|
|
41
41
|
"aria-label": j ? k("admin.tenantDetail.sso.collapse", "Collapse SSO configuration") : k("admin.tenantDetail.sso.expand", "Expand SSO configuration"),
|
|
42
42
|
children: S(j ? g : h, { className: "h-4 w-4" })
|
|
@@ -45,9 +45,9 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
45
45
|
className: "space-y-4",
|
|
46
46
|
children: B ? /* @__PURE__ */ S("div", {
|
|
47
47
|
className: "flex items-center justify-center py-6",
|
|
48
|
-
children: /* @__PURE__ */ S(_, { className: "h-6 w-6 animate-spin text-
|
|
48
|
+
children: /* @__PURE__ */ S(_, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
49
49
|
}) : V ? /* @__PURE__ */ S("p", {
|
|
50
|
-
className: "text-sm text-
|
|
50
|
+
className: "text-sm text-status-error-text",
|
|
51
51
|
children: V.message
|
|
52
52
|
}) : q && !N ? /* @__PURE__ */ C("div", {
|
|
53
53
|
className: "space-y-3",
|
|
@@ -65,7 +65,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
65
65
|
/* @__PURE__ */ C("div", {
|
|
66
66
|
className: "flex flex-col gap-0.5",
|
|
67
67
|
children: [/* @__PURE__ */ S("dt", {
|
|
68
|
-
className: "text-xs text-
|
|
68
|
+
className: "text-xs text-text-secondary",
|
|
69
69
|
children: k("admin.tenantDetail.sso.entityId", "Entity ID")
|
|
70
70
|
}), /* @__PURE__ */ S("dd", {
|
|
71
71
|
className: "font-mono text-xs break-all",
|
|
@@ -75,7 +75,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
75
75
|
/* @__PURE__ */ C("div", {
|
|
76
76
|
className: "flex flex-col gap-0.5",
|
|
77
77
|
children: [/* @__PURE__ */ S("dt", {
|
|
78
|
-
className: "text-xs text-
|
|
78
|
+
className: "text-xs text-text-secondary",
|
|
79
79
|
children: k("admin.tenantDetail.sso.ssoUrl", "SSO URL")
|
|
80
80
|
}), /* @__PURE__ */ S("dd", {
|
|
81
81
|
className: "font-mono text-xs break-all",
|
|
@@ -85,7 +85,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
85
85
|
q.sloUrl && /* @__PURE__ */ C("div", {
|
|
86
86
|
className: "flex flex-col gap-0.5",
|
|
87
87
|
children: [/* @__PURE__ */ S("dt", {
|
|
88
|
-
className: "text-xs text-
|
|
88
|
+
className: "text-xs text-text-secondary",
|
|
89
89
|
children: k("admin.tenantDetail.sso.sloUrl", "SLO URL")
|
|
90
90
|
}), /* @__PURE__ */ S("dd", {
|
|
91
91
|
className: "font-mono text-xs break-all",
|
|
@@ -104,7 +104,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
104
104
|
}), /* @__PURE__ */ C(t, {
|
|
105
105
|
variant: "outline",
|
|
106
106
|
size: "sm",
|
|
107
|
-
className: "text-
|
|
107
|
+
className: "text-status-error-text hover:text-status-error-text",
|
|
108
108
|
onClick: () => R(!0),
|
|
109
109
|
children: [/* @__PURE__ */ S(b, { className: "mr-1.5 h-3.5 w-3.5" }), k("admin.tenantDetail.sso.deleteButton", "Remove")]
|
|
110
110
|
})]
|
|
@@ -134,7 +134,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
134
134
|
className: "space-y-4",
|
|
135
135
|
children: [
|
|
136
136
|
!q && /* @__PURE__ */ S("p", {
|
|
137
|
-
className: "text-sm text-
|
|
137
|
+
className: "text-sm text-text-secondary",
|
|
138
138
|
children: k("admin.tenantDetail.sso.notConfigured", "No SSO configuration. Fill in the SAML details below to enable enterprise SSO.")
|
|
139
139
|
}),
|
|
140
140
|
/* @__PURE__ */ S(s, {}),
|
|
@@ -147,7 +147,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
147
147
|
className: "text-sm font-medium",
|
|
148
148
|
htmlFor: `sso-entityId-${E}`,
|
|
149
149
|
children: [k("admin.tenantDetail.sso.entityId", "Entity ID"), /* @__PURE__ */ S("span", {
|
|
150
|
-
className: "ml-1 text-
|
|
150
|
+
className: "ml-1 text-status-error-text",
|
|
151
151
|
children: "*"
|
|
152
152
|
})]
|
|
153
153
|
}), /* @__PURE__ */ S(o, {
|
|
@@ -167,7 +167,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
167
167
|
className: "text-sm font-medium",
|
|
168
168
|
htmlFor: `sso-ssoUrl-${E}`,
|
|
169
169
|
children: [k("admin.tenantDetail.sso.ssoUrl", "SSO URL"), /* @__PURE__ */ S("span", {
|
|
170
|
-
className: "ml-1 text-
|
|
170
|
+
className: "ml-1 text-status-error-text",
|
|
171
171
|
children: "*"
|
|
172
172
|
})]
|
|
173
173
|
}), /* @__PURE__ */ S(o, {
|
|
@@ -187,7 +187,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
187
187
|
className: "text-sm font-medium",
|
|
188
188
|
htmlFor: `sso-sloUrl-${E}`,
|
|
189
189
|
children: [k("admin.tenantDetail.sso.sloUrl", "SLO URL"), /* @__PURE__ */ S("span", {
|
|
190
|
-
className: "ml-0.5 text-xs text-
|
|
190
|
+
className: "ml-0.5 text-xs text-text-secondary",
|
|
191
191
|
children: k("admin.tenantDetail.sso.optional", "(optional)")
|
|
192
192
|
})]
|
|
193
193
|
}), /* @__PURE__ */ S(o, {
|
|
@@ -206,7 +206,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
206
206
|
className: "text-sm font-medium",
|
|
207
207
|
htmlFor: `sso-cert-${E}`,
|
|
208
208
|
children: [k("admin.tenantDetail.sso.certificate", "X.509 Certificate"), /* @__PURE__ */ S("span", {
|
|
209
|
-
className: "ml-1 text-
|
|
209
|
+
className: "ml-1 text-status-error-text",
|
|
210
210
|
children: "*"
|
|
211
211
|
})]
|
|
212
212
|
}), /* @__PURE__ */ S("textarea", {
|
|
@@ -219,7 +219,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
219
219
|
placeholder: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
|
|
220
220
|
required: !0,
|
|
221
221
|
rows: 5,
|
|
222
|
-
className: "w-full rounded-md border border-
|
|
222
|
+
className: "w-full rounded-md border border-border-default bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 resize-none"
|
|
223
223
|
})]
|
|
224
224
|
}),
|
|
225
225
|
/* @__PURE__ */ C("div", {
|
|
@@ -233,7 +233,7 @@ function E({ tenantId: E, canManage: D }) {
|
|
|
233
233
|
...e,
|
|
234
234
|
enabled: !e.enabled
|
|
235
235
|
})),
|
|
236
|
-
className: `relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 ${F.enabled ? "bg-primary" : "bg-
|
|
236
|
+
className: `relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 ${F.enabled ? "bg-action-primary-bg" : "bg-bg-sunken"}`,
|
|
237
237
|
children: /* @__PURE__ */ S("span", { className: `pointer-events-none inline-block h-4 w-4 transform rounded-full bg-white shadow-lg transition-transform ${F.enabled ? "translate-x-4" : "translate-x-0"}` })
|
|
238
238
|
}), /* @__PURE__ */ S("span", {
|
|
239
239
|
className: "text-sm",
|