@fayz-ai/admin 0.62.0 → 0.63.0
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/{CommandPalette-B5PMQFDG.js → CommandPalette-XDJJ5E3O.js} +3 -3
- package/dist/{CommandPalette-B5PMQFDG.js.map → CommandPalette-XDJJ5E3O.js.map} +1 -1
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js +680 -0
- package/dist/ConnectedBrandingSettings-43ZM3HCI.js.map +1 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js +197 -0
- package/dist/ConnectedCompanySettings-I4MALI2Q.js.map +1 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js +563 -0
- package/dist/ConnectedModulesSettings-A2VJ5BMJ.js.map +1 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js +189 -0
- package/dist/ConnectedSecuritySettings-25AHA4HJ.js.map +1 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js +424 -0
- package/dist/ConnectedUserProfile-E7USJRUE.js.map +1 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js +129 -0
- package/dist/FieldRulesEditor-YGIPKI5T.js.map +1 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js +12 -0
- package/dist/GettingStartedWidget-HMIJ3Y24.js.map +1 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js +548 -0
- package/dist/PermissionProfilesTab-3ZPRCHMZ.js.map +1 -0
- package/dist/PersonTimeline-HQZT527S.js +3 -0
- package/dist/{PersonTimeline-X7JTPUWJ.js.map → PersonTimeline-HQZT527S.js.map} +1 -1
- package/dist/ProjectsSettings-7O2F27MD.js +5 -0
- package/dist/ProjectsSettings-7O2F27MD.js.map +1 -0
- package/dist/ScheduleEditor-54ELWR45.js +8 -0
- package/dist/{ScheduleEditor-L2UJXKMY.js.map → ScheduleEditor-54ELWR45.js.map} +1 -1
- package/dist/SeuSiteWidget-QLSUVC63.js +6 -0
- package/dist/SeuSiteWidget-QLSUVC63.js.map +1 -0
- package/dist/SubscriptionPage-5GE5HFJK.js +17 -0
- package/dist/{SubscriptionPage-IDVEFLZ5.js.map → SubscriptionPage-5GE5HFJK.js.map} +1 -1
- package/dist/TeamTab-6RAULFLW.js +502 -0
- package/dist/TeamTab-6RAULFLW.js.map +1 -0
- package/dist/TrialWidget-Z6CLUCU3.js +7 -0
- package/dist/TrialWidget-Z6CLUCU3.js.map +1 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +14 -0
- package/dist/app/config.d.ts.map +1 -1
- package/dist/app/routing.d.ts +6 -0
- package/dist/app/routing.d.ts.map +1 -1
- package/dist/app/scaffold.d.ts.map +1 -1
- package/dist/billing/index.js +9 -4
- package/dist/chunk-2H46WDG5.js +53 -0
- package/dist/chunk-2H46WDG5.js.map +1 -0
- package/dist/chunk-3HLX7YB2.js +94 -0
- package/dist/chunk-3HLX7YB2.js.map +1 -0
- package/dist/chunk-3OUD4XBK.js +3 -0
- package/dist/chunk-3OUD4XBK.js.map +1 -0
- package/dist/{chunk-IXSY4GPK.js → chunk-5D5QLMOQ.js} +5 -3
- package/dist/chunk-5D5QLMOQ.js.map +1 -0
- package/dist/chunk-5WR62X2R.js +1081 -0
- package/dist/chunk-5WR62X2R.js.map +1 -0
- package/dist/{chunk-KPYRTOCK.js → chunk-6CQFGMEO.js} +4 -140
- package/dist/chunk-6CQFGMEO.js.map +1 -0
- package/dist/chunk-6MMSYC2I.js +116 -0
- package/dist/chunk-6MMSYC2I.js.map +1 -0
- package/dist/chunk-6QFOE3UD.js +1466 -0
- package/dist/chunk-6QFOE3UD.js.map +1 -0
- package/dist/chunk-6UUFVQ2E.js +283 -0
- package/dist/chunk-6UUFVQ2E.js.map +1 -0
- package/dist/chunk-7WNLL3F6.js +7889 -0
- package/dist/chunk-7WNLL3F6.js.map +1 -0
- package/dist/chunk-A74IHAQE.js +439 -0
- package/dist/chunk-A74IHAQE.js.map +1 -0
- package/dist/{chunk-JOBRLBIQ.js → chunk-AFA6IO23.js} +4 -3
- package/dist/chunk-AFA6IO23.js.map +1 -0
- package/dist/{chunk-P5UIGQFY.js → chunk-ALCSIN3Z.js} +5 -5
- package/dist/chunk-ALCSIN3Z.js.map +1 -0
- package/dist/chunk-AWM4AMBS.js +39 -0
- package/dist/chunk-AWM4AMBS.js.map +1 -0
- package/dist/{chunk-OQARLKZN.js → chunk-BG5Z6KLN.js} +6 -18
- package/dist/chunk-BG5Z6KLN.js.map +1 -0
- package/dist/chunk-DPNFZONC.js +19 -0
- package/dist/chunk-DPNFZONC.js.map +1 -0
- package/dist/chunk-DRCV643L.js +193 -0
- package/dist/chunk-DRCV643L.js.map +1 -0
- package/dist/chunk-E6DF3VR6.js +206 -0
- package/dist/chunk-E6DF3VR6.js.map +1 -0
- package/dist/chunk-ENHCVGXZ.js +120 -0
- package/dist/chunk-ENHCVGXZ.js.map +1 -0
- package/dist/chunk-FK6RHGI7.js +56 -0
- package/dist/chunk-FK6RHGI7.js.map +1 -0
- package/dist/chunk-G5HNA5QK.js +233 -0
- package/dist/chunk-G5HNA5QK.js.map +1 -0
- package/dist/chunk-HCFO4Z6A.js +53 -0
- package/dist/chunk-HCFO4Z6A.js.map +1 -0
- package/dist/chunk-JFIU6NQP.js +51 -0
- package/dist/chunk-JFIU6NQP.js.map +1 -0
- package/dist/chunk-JRZJBMOY.js +3 -0
- package/dist/chunk-JRZJBMOY.js.map +1 -0
- package/dist/{chunk-3YP7KW47.js → chunk-JVVOL4BB.js} +41 -512
- package/dist/chunk-JVVOL4BB.js.map +1 -0
- package/dist/chunk-KAQ3UIAA.js +415 -0
- package/dist/chunk-KAQ3UIAA.js.map +1 -0
- package/dist/{chunk-P6XUDT2P.js → chunk-KJELMULA.js} +4 -51
- package/dist/chunk-KJELMULA.js.map +1 -0
- package/dist/chunk-KLO7DKXX.js +1651 -0
- package/dist/chunk-KLO7DKXX.js.map +1 -0
- package/dist/chunk-M2CMDWUI.js +75 -0
- package/dist/chunk-M2CMDWUI.js.map +1 -0
- package/dist/chunk-PQ47MLTH.js +255 -0
- package/dist/chunk-PQ47MLTH.js.map +1 -0
- package/dist/{chunk-VX3WJLKK.js → chunk-QD7BMUDF.js} +17 -8
- package/dist/chunk-QD7BMUDF.js.map +1 -0
- package/dist/{chunk-E4MESMNR.js → chunk-QTSKCPSW.js} +7 -10
- package/dist/chunk-QTSKCPSW.js.map +1 -0
- package/dist/chunk-S56FQKPL.js +186 -0
- package/dist/chunk-S56FQKPL.js.map +1 -0
- package/dist/chunk-SGAFPNMA.js +75 -0
- package/dist/chunk-SGAFPNMA.js.map +1 -0
- package/dist/{chunk-OD7P4L3D.js → chunk-SOKCLVM4.js} +3 -3
- package/dist/{chunk-OD7P4L3D.js.map → chunk-SOKCLVM4.js.map} +1 -1
- package/dist/chunk-TL42NKTH.js +3 -0
- package/dist/chunk-TL42NKTH.js.map +1 -0
- package/dist/chunk-U6ZCO2PS.js +2964 -0
- package/dist/chunk-U6ZCO2PS.js.map +1 -0
- package/dist/chunk-VSXNROYH.js +16 -0
- package/dist/chunk-VSXNROYH.js.map +1 -0
- package/dist/chunk-WPFZCYZL.js +619 -0
- package/dist/chunk-WPFZCYZL.js.map +1 -0
- package/dist/chunk-XFTNIBDW.js +23 -0
- package/dist/chunk-XFTNIBDW.js.map +1 -0
- package/dist/chunk-Y66JDL6I.js +1020 -0
- package/dist/chunk-Y66JDL6I.js.map +1 -0
- package/dist/chunk-YEHIBPIX.js +137 -0
- package/dist/chunk-YEHIBPIX.js.map +1 -0
- package/dist/chunk-YKPPCV2Z.js +53 -0
- package/dist/chunk-YKPPCV2Z.js.map +1 -0
- package/dist/components/chat/markdown.d.ts.map +1 -1
- package/dist/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
- package/dist/components/settings/SettingsPage.d.ts.map +1 -1
- package/dist/connect-XKQJOWR7.js +18 -0
- package/dist/connect-XKQJOWR7.js.map +1 -0
- package/dist/crud/CrudDetailPage.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/RelatedListTab.d.ts.map +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts +1 -1
- package/dist/crud/archetypes/EntityLink.d.ts.map +1 -1
- package/dist/crud/record-chrome.d.ts.map +1 -1
- package/dist/entity-routes-PGU7EHAD.js +4 -0
- package/dist/{entity-routes-LXOBW5IB.js.map → entity-routes-PGU7EHAD.js.map} +1 -1
- package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
- package/dist/impersonation/ImpersonateDialog.d.ts.map +1 -1
- package/dist/impersonation/session.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11902 -33551
- package/dist/index.js.map +1 -1
- package/dist/lib/entity-routes.d.ts.map +1 -1
- package/dist/org/index.js +4 -2
- package/dist/plugins/PluginRegistryManager.d.ts.map +1 -1
- package/dist/settings/MessageTemplateRow.d.ts.map +1 -1
- package/dist/timeline/PersonTimeline.d.ts.map +1 -1
- package/dist/timeline/TimelineList.d.ts.map +1 -1
- package/dist/units/UnitRailPage.d.ts.map +1 -1
- package/dist/units/useUnitFilter.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/PersonTimeline-X7JTPUWJ.js +0 -3
- package/dist/ScheduleEditor-L2UJXKMY.js +0 -6
- package/dist/SubscriptionPage-IDVEFLZ5.js +0 -8
- package/dist/chunk-3YP7KW47.js.map +0 -1
- package/dist/chunk-E4MESMNR.js.map +0 -1
- package/dist/chunk-IXSY4GPK.js.map +0 -1
- package/dist/chunk-JOBRLBIQ.js.map +0 -1
- package/dist/chunk-KPYRTOCK.js.map +0 -1
- package/dist/chunk-OQARLKZN.js.map +0 -1
- package/dist/chunk-P5UIGQFY.js.map +0 -1
- package/dist/chunk-P6XUDT2P.js.map +0 -1
- package/dist/chunk-VX3WJLKK.js.map +0 -1
- package/dist/entity-routes-LXOBW5IB.js +0 -4
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
import { useAgentSurface, askAI, DeleteConfirmDialog } from './chunk-6QFOE3UD.js';
|
|
2
|
+
import { PageActions } from './chunk-PQ47MLTH.js';
|
|
3
|
+
import './chunk-S56FQKPL.js';
|
|
4
|
+
import { useTranslation } from './chunk-YUHCC7JY.js';
|
|
5
|
+
import './chunk-ALCSIN3Z.js';
|
|
6
|
+
import './chunk-SGAFPNMA.js';
|
|
7
|
+
import './chunk-KLO7DKXX.js';
|
|
8
|
+
import './chunk-TL42NKTH.js';
|
|
9
|
+
import { useOrgAdapterOptional } from './chunk-5D5QLMOQ.js';
|
|
10
|
+
import { usePermissionsStore } from './chunk-UQ2NTFQC.js';
|
|
11
|
+
import './chunk-KJELMULA.js';
|
|
12
|
+
import './chunk-JRZJBMOY.js';
|
|
13
|
+
import { useOrganizationStore } from './chunk-YKPPCV2Z.js';
|
|
14
|
+
import './chunk-QD7BMUDF.js';
|
|
15
|
+
import { errorMessage, tl } from '@fayz-ai/core';
|
|
16
|
+
import * as React from 'react';
|
|
17
|
+
import { Plus, WandSparkles, Shield, Eye, Copy, Pencil, Trash2, Search, ChevronRight, ChevronDown } from 'lucide-react';
|
|
18
|
+
import { PageHeaderActions, Card, CardContent, Skeleton, CardTitle, Badge, CardDescription, RowActions, useSaveBar, PageHeader, Input, Button, Checkbox } from '@fayz-ai/ui';
|
|
19
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
20
|
+
|
|
21
|
+
var V1_CATEGORY_BY_PREFIX = [
|
|
22
|
+
[/^(agenda|appointments|schedules)(\.|$)/, () => tl("Schedule", "Agenda")],
|
|
23
|
+
[/^(people|registry\.customers|clinical)(\.|$)/, () => tl("Clients", "Clientes")],
|
|
24
|
+
[/^(crm|sales)(\.|$)/, () => "CRM"],
|
|
25
|
+
[/^(catalog|services|registry|units|custom_forms)(\.|$)/, () => tl("Registries", "Cadastros")],
|
|
26
|
+
[/^financial(\.|$)/, () => tl("Financial", "Financeiro")],
|
|
27
|
+
[/^(inventory|production)(\.|$)/, () => tl("Stock", "Estoque")],
|
|
28
|
+
[/^fiscal(\.|$)/, () => tl("Invoices", "Notas Fiscais")],
|
|
29
|
+
[/^(orders|tables|menu|reservations|kds|pos|shop)(\.|$)/, () => tl("Restaurant & sales", "Restaurante e vendas")],
|
|
30
|
+
[/^(marketing|conversations|automations|notifications|reputation|blog|sites)(\.|$)/, () => tl("Communication", "Comunica\xE7\xE3o")],
|
|
31
|
+
[/^(reports|dashboard)(\.|$)/, () => tl("Reports", "Relat\xF3rios")],
|
|
32
|
+
[/^(workforce|tasks)(\.|$)/, () => tl("Team & tasks", "Equipe e tarefas")],
|
|
33
|
+
[/^(settings|audit|admin|scribe)(\.|$)/, () => tl("Settings", "Configura\xE7\xF5es")]
|
|
34
|
+
];
|
|
35
|
+
function v1PermissionGroup(feature) {
|
|
36
|
+
for (const [re, label] of V1_CATEGORY_BY_PREFIX) if (re.test(feature.id)) return label();
|
|
37
|
+
return feature.group ?? tl("Other", "Outros");
|
|
38
|
+
}
|
|
39
|
+
function coreFeatures() {
|
|
40
|
+
return [
|
|
41
|
+
{ id: "people", label: tl("People and clients", "Pessoas e clientes") },
|
|
42
|
+
{ id: "catalog", label: tl("Products and catalog", "Produtos e cat\xE1logo") },
|
|
43
|
+
{ id: "services", label: tl("Services, rooms and groups", "Servi\xE7os, locais e grupos") },
|
|
44
|
+
{ id: "audit", label: tl("Action log", "Log de a\xE7\xF5es"), actions: ["read"] }
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
function specialActions() {
|
|
48
|
+
return [
|
|
49
|
+
{ feature: "financial", action: "amend", label: tl("Edit a paid entry", "Editar lan\xE7amento j\xE1 pago"), enforced: true },
|
|
50
|
+
{ feature: "financial.discount", action: "approve", label: tl("Approve discounts", "Aprovar desconto"), enforced: true },
|
|
51
|
+
{ feature: "financial", action: "close_period", label: tl("Close and reopen a period", "Fechar e reabrir data"), enforced: true },
|
|
52
|
+
{ feature: "workforce", action: "adjust_time", label: tl("Adjust clock-ins", "Ajustar batidas de ponto"), enforced: true },
|
|
53
|
+
{ feature: "workforce", action: "close_period", label: tl("Close the time-clock period", "Fechar o per\xEDodo do ponto"), enforced: true },
|
|
54
|
+
{ feature: "orders", action: "discount_without_pin", label: tl("Discount without a PIN", "Dar desconto sem senha"), enforced: false },
|
|
55
|
+
{ feature: "orders", action: "cancel_without_pin", label: tl("Cancel without a PIN", "Cancelar sem senha"), enforced: false },
|
|
56
|
+
{ feature: "orders", action: "reopen_bill", label: tl("Reopen a closed bill", "Reabrir conta fechada"), enforced: false },
|
|
57
|
+
{ feature: "orders", action: "delete_paid_item", label: tl("Remove a paid item", "Excluir item j\xE1 pago"), enforced: false },
|
|
58
|
+
{ feature: "sales", action: "approve_quote", label: tl("Approve a quote", "Aprovar or\xE7amento"), enforced: false },
|
|
59
|
+
{ feature: "financial", action: "edit_payment_history", label: tl("Edit payment history", "Editar hist\xF3rico de pagamento"), enforced: false },
|
|
60
|
+
{ feature: "financial", action: "manage_bank_integration", label: tl("Manage bank integration", "Gerenciar integra\xE7\xE3o banc\xE1ria"), enforced: false },
|
|
61
|
+
{ feature: "financial", action: "unmatch_reconciled", label: tl("Undo a reconciliation", "Desfazer concilia\xE7\xE3o"), enforced: false },
|
|
62
|
+
{ feature: "financial", action: "force_reconcile", label: tl("Force a reconciliation", "For\xE7ar concilia\xE7\xE3o"), enforced: false },
|
|
63
|
+
{ feature: "people", action: "export_sensitive", label: tl("Export sensitive data", "Exportar dados sens\xEDveis"), enforced: false },
|
|
64
|
+
{ feature: "settings", action: "bulk_import", label: tl("Bulk import", "Importar em massa"), enforced: false },
|
|
65
|
+
{ feature: "catalog", action: "view_cost", label: tl("See cost prices", "Ver pre\xE7o de custo"), enforced: false },
|
|
66
|
+
{ feature: "catalog", action: "edit_fiscal", label: tl("Edit fiscal data", "Editar dados fiscais"), enforced: false },
|
|
67
|
+
{ feature: "inventory", action: "view_margin", label: tl("See recipe margins", "Ver margem da ficha t\xE9cnica"), enforced: false },
|
|
68
|
+
{ feature: "inventory", action: "approve_loss", label: tl("Approve stock losses", "Aprovar perda de estoque"), enforced: false },
|
|
69
|
+
{ feature: "fiscal", action: "delete_import", label: tl("Delete an imported invoice", "Excluir nota importada"), enforced: false },
|
|
70
|
+
{ feature: "conversations", action: "bulk_send", label: tl("Send bulk messages", "Disparar mensagens em massa"), enforced: false },
|
|
71
|
+
{ feature: "tasks", action: "view_all", label: tl("See everyone's tasks", "Ver tarefas de todos"), enforced: false },
|
|
72
|
+
{ feature: "members", action: "qr_access_issue", label: tl("Issue access QR codes", "Gerar QR Code de acesso"), enforced: false }
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
var ACTIONS = ["read", "create", "edit", "delete"];
|
|
76
|
+
var ACTION_COLORS = {
|
|
77
|
+
read: "primary",
|
|
78
|
+
create: "success",
|
|
79
|
+
edit: "warning",
|
|
80
|
+
delete: "destructive"
|
|
81
|
+
};
|
|
82
|
+
var SYSTEM_GRANT = {
|
|
83
|
+
manage_team: ["members", "manage"],
|
|
84
|
+
manage_permissions: ["roles", "manage"],
|
|
85
|
+
manage_settings: ["settings", "manage"],
|
|
86
|
+
manage_billing: ["billing", "manage"]
|
|
87
|
+
};
|
|
88
|
+
var SYSTEM_PERMISSIONS = [
|
|
89
|
+
{ id: "manage_team", key: "organization.permissions.manageTeam" },
|
|
90
|
+
{ id: "manage_billing", key: "organization.permissions.manageBilling" },
|
|
91
|
+
{ id: "manage_settings", key: "organization.permissions.manageSettings" },
|
|
92
|
+
{ id: "manage_permissions", key: "organization.permissions.managePermissions" }
|
|
93
|
+
];
|
|
94
|
+
function PermissionMatrixEditor({ features, profile, onSave, onCancel, saving, isNew, parentLabel }) {
|
|
95
|
+
const { t } = useTranslation();
|
|
96
|
+
const [name, setName] = React.useState(profile?.name ?? "");
|
|
97
|
+
const [description, setDescription] = React.useState(profile?.description ?? "");
|
|
98
|
+
const [systemPerms, setSystemPerms] = React.useState(profile?.systemPermissions ?? []);
|
|
99
|
+
const [grants, setGrants] = React.useState(profile?.grants ?? {});
|
|
100
|
+
const [collapsed, setCollapsed] = React.useState(/* @__PURE__ */ new Set());
|
|
101
|
+
const [search, setSearch] = React.useState("");
|
|
102
|
+
const initial = React.useRef({
|
|
103
|
+
name: profile?.name ?? "",
|
|
104
|
+
description: profile?.description ?? "",
|
|
105
|
+
systemPerms: JSON.stringify(profile?.systemPermissions ?? []),
|
|
106
|
+
grants: JSON.stringify(profile?.grants ?? {})
|
|
107
|
+
});
|
|
108
|
+
const dirty = isNew ? name.trim() !== "" : name !== initial.current.name || description !== initial.current.description || JSON.stringify(systemPerms) !== initial.current.systemPerms || JSON.stringify(grants) !== initial.current.grants;
|
|
109
|
+
const toggleSystemPerm = (perm) => {
|
|
110
|
+
setSystemPerms(
|
|
111
|
+
(prev) => prev.includes(perm) ? prev.filter((p) => p !== perm) : [...prev, perm]
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
const toggleGrant = (featureId, action) => {
|
|
115
|
+
setGrants((prev) => {
|
|
116
|
+
const current = prev[featureId] ?? [];
|
|
117
|
+
const next = current.includes(action) ? current.filter((a) => a !== action) : [...current, action];
|
|
118
|
+
return { ...prev, [featureId]: next };
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const toggleGroup = (group) => {
|
|
122
|
+
setCollapsed((prev) => {
|
|
123
|
+
const next = new Set(prev);
|
|
124
|
+
next.has(group) ? next.delete(group) : next.add(group);
|
|
125
|
+
return next;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
const handleSave = () => {
|
|
129
|
+
if (!name.trim()) return;
|
|
130
|
+
const withSystem = { ...grants };
|
|
131
|
+
for (const [perm, [feature, action]] of Object.entries(SYSTEM_GRANT)) {
|
|
132
|
+
const current = (withSystem[feature] ?? []).filter((a) => a !== action);
|
|
133
|
+
withSystem[feature] = systemPerms.includes(perm) ? [...current, action] : current;
|
|
134
|
+
}
|
|
135
|
+
onSave({ name: name.trim(), description: description.trim() || void 0, systemPermissions: systemPerms, grants: withSystem });
|
|
136
|
+
};
|
|
137
|
+
const specials = React.useMemo(() => {
|
|
138
|
+
const q = search.toLowerCase();
|
|
139
|
+
return specialActions().filter((sa) => !q || sa.label.toLowerCase().includes(q));
|
|
140
|
+
}, [search]);
|
|
141
|
+
useSaveBar({
|
|
142
|
+
dirty,
|
|
143
|
+
saving,
|
|
144
|
+
onSave: handleSave,
|
|
145
|
+
onDiscard: onCancel,
|
|
146
|
+
saveLabel: isNew ? t("organization.permissions.createProfile") : t("organization.permissions.updateProfile")
|
|
147
|
+
});
|
|
148
|
+
const groups = React.useMemo(() => {
|
|
149
|
+
const filtered = search ? features.filter((f) => f.label.toLowerCase().includes(search.toLowerCase())) : features;
|
|
150
|
+
const map = /* @__PURE__ */ new Map();
|
|
151
|
+
for (const f of filtered) {
|
|
152
|
+
const group = v1PermissionGroup(f);
|
|
153
|
+
if (!map.has(group)) map.set(group, []);
|
|
154
|
+
map.get(group).push(f);
|
|
155
|
+
}
|
|
156
|
+
return new Map([...map.entries()].sort(([a], [b]) => a.localeCompare(b, "pt-BR")));
|
|
157
|
+
}, [features, search]);
|
|
158
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
159
|
+
/* @__PURE__ */ jsx(
|
|
160
|
+
PageHeader,
|
|
161
|
+
{
|
|
162
|
+
title: name.trim() || t("organization.permissions.createCustom"),
|
|
163
|
+
parentLabel: parentLabel ?? t("organization.permissions.title"),
|
|
164
|
+
onBack: onCancel
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
|
|
168
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
169
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: t("organization.permissions.profileName") }),
|
|
170
|
+
/* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: t("organization.permissions.profileNamePlaceholder"), className: "mt-1" })
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
173
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: t("organization.permissions.description") }),
|
|
174
|
+
/* @__PURE__ */ jsx(Input, { value: description, onChange: (e) => setDescription(e.target.value), placeholder: t("organization.permissions.descriptionPlaceholder"), className: "mt-1" })
|
|
175
|
+
] })
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
|
|
178
|
+
features.length > 8 && /* @__PURE__ */ jsx("div", { className: "border-b border-border/40 px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
179
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" }),
|
|
180
|
+
/* @__PURE__ */ jsx(
|
|
181
|
+
Input,
|
|
182
|
+
{
|
|
183
|
+
value: search,
|
|
184
|
+
onChange: (e) => setSearch(e.target.value),
|
|
185
|
+
placeholder: t("organization.permissions.searchFeatures"),
|
|
186
|
+
className: "h-8 pl-8 text-xs"
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
] }) }),
|
|
190
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
|
|
191
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/50", children: [
|
|
192
|
+
/* @__PURE__ */ jsx("th", { className: "text-left p-3 text-sm font-medium text-muted-foreground", children: t("common.feature") }),
|
|
193
|
+
ACTIONS.map((action) => /* @__PURE__ */ jsx("th", { className: "p-3 text-center text-sm font-medium text-muted-foreground capitalize w-20", children: t(`organization.permissions.action.${action}`) === `organization.permissions.action.${action}` ? action : t(`organization.permissions.action.${action}`) }, action))
|
|
194
|
+
] }) }),
|
|
195
|
+
/* @__PURE__ */ jsxs("tbody", { className: "divide-y", children: [
|
|
196
|
+
/* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "p-2 px-3", children: /* @__PURE__ */ jsxs(
|
|
197
|
+
Button,
|
|
198
|
+
{
|
|
199
|
+
variant: "bare",
|
|
200
|
+
size: "bare",
|
|
201
|
+
onClick: () => toggleGroup("__system"),
|
|
202
|
+
className: "flex items-center gap-1.5 text-xs font-normal",
|
|
203
|
+
children: [
|
|
204
|
+
collapsed.has("__system") ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground" }),
|
|
205
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: t("organization.permissions.systemPermissions") })
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
) }) }),
|
|
209
|
+
!collapsed.has("__system") && SYSTEM_PERMISSIONS.map((sp) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-muted/30", children: [
|
|
210
|
+
/* @__PURE__ */ jsx("td", { className: "p-3 text-sm font-medium", children: t(sp.key) }),
|
|
211
|
+
/* @__PURE__ */ jsx("td", { colSpan: 4, className: "p-3", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
212
|
+
Checkbox,
|
|
213
|
+
{
|
|
214
|
+
checked: systemPerms.includes(sp.id),
|
|
215
|
+
onChange: () => toggleSystemPerm(sp.id)
|
|
216
|
+
}
|
|
217
|
+
) }) })
|
|
218
|
+
] }, sp.id)),
|
|
219
|
+
Array.from(groups.entries()).map(([groupName, groupFeatures]) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
220
|
+
/* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "p-2 px-3", children: /* @__PURE__ */ jsxs(
|
|
221
|
+
Button,
|
|
222
|
+
{
|
|
223
|
+
variant: "bare",
|
|
224
|
+
size: "bare",
|
|
225
|
+
onClick: () => toggleGroup(groupName),
|
|
226
|
+
className: "flex items-center gap-1.5 text-xs font-normal",
|
|
227
|
+
children: [
|
|
228
|
+
collapsed.has(groupName) ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground" }),
|
|
229
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: groupName }),
|
|
230
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: groupFeatures.length })
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
) }) }),
|
|
234
|
+
!collapsed.has(groupName) && groupFeatures.map((feature) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-muted/30", children: [
|
|
235
|
+
/* @__PURE__ */ jsx("td", { className: "p-3 text-sm font-medium", children: feature.label }),
|
|
236
|
+
ACTIONS.map((action) => /* @__PURE__ */ jsx("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
237
|
+
Checkbox,
|
|
238
|
+
{
|
|
239
|
+
checked: grants[feature.id]?.includes(action) ?? false,
|
|
240
|
+
onChange: () => toggleGrant(feature.id, action),
|
|
241
|
+
color: ACTION_COLORS[action]
|
|
242
|
+
}
|
|
243
|
+
) }) }, action))
|
|
244
|
+
] }, feature.id))
|
|
245
|
+
] }, groupName)),
|
|
246
|
+
specials.length > 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 5, className: "p-2 px-3", children: /* @__PURE__ */ jsxs(Button, { variant: "bare", size: "bare", onClick: () => toggleGroup("__special"), className: "font-normal whitespace-normal flex items-center gap-1.5 text-xs", children: [
|
|
247
|
+
collapsed.has("__special") ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 text-muted-foreground" }),
|
|
248
|
+
/* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: tl("Special actions", "A\xE7\xF5es especiais") }),
|
|
249
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", children: specials.length })
|
|
250
|
+
] }) }) }),
|
|
251
|
+
!collapsed.has("__special") && specials.map((sa) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-muted/30", children: [
|
|
252
|
+
/* @__PURE__ */ jsxs("td", { className: "p-3 text-sm font-medium", children: [
|
|
253
|
+
/* @__PURE__ */ jsx("span", { className: sa.enforced ? void 0 : "text-muted-foreground", children: sa.label }),
|
|
254
|
+
!sa.enforced && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "ml-2 text-[10px]", children: tl("Coming soon", "Em breve") })
|
|
255
|
+
] }),
|
|
256
|
+
/* @__PURE__ */ jsx("td", { colSpan: 4, className: "p-3", children: /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
257
|
+
Checkbox,
|
|
258
|
+
{
|
|
259
|
+
checked: grants[sa.feature]?.includes(sa.action) ?? false,
|
|
260
|
+
onChange: () => toggleGrant(sa.feature, sa.action),
|
|
261
|
+
disabled: !sa.enforced
|
|
262
|
+
}
|
|
263
|
+
) }) })
|
|
264
|
+
] }, `${sa.feature}.${sa.action}`))
|
|
265
|
+
] })
|
|
266
|
+
] }) })
|
|
267
|
+
] })
|
|
268
|
+
] });
|
|
269
|
+
}
|
|
270
|
+
var CREATE_PROFILE_TOOL = {
|
|
271
|
+
id: "org.createPermissionProfile",
|
|
272
|
+
name: "createPermissionProfile",
|
|
273
|
+
description: 'Creates an access profile (role) for this organisation. Use ONLY the feature ids listed in the surface state \u2014 an id that is not there does not exist and the grant is silently dropped. Grant the least that satisfies the description: someone who "sees" something needs read, someone who "handles" it needs read+create+edit. Never grant a system permission (manage_team, manage_billing, manage_settings, manage_permissions) unless the user says so in words.',
|
|
274
|
+
icon: "Shield",
|
|
275
|
+
mode: "persist",
|
|
276
|
+
execution: { plane: "client" },
|
|
277
|
+
category: "Organization",
|
|
278
|
+
suggestions: [
|
|
279
|
+
{ label: "Um perfil de recep\xE7\xE3o que n\xE3o v\xEA o financeiro" }
|
|
280
|
+
],
|
|
281
|
+
parameters: {
|
|
282
|
+
type: "object",
|
|
283
|
+
properties: {
|
|
284
|
+
name: { type: "string", description: "Profile name, in the user's language." },
|
|
285
|
+
description: { type: "string", description: "One line on who this profile is for." },
|
|
286
|
+
grants: {
|
|
287
|
+
type: "array",
|
|
288
|
+
description: "One entry per feature the profile can touch: { feature, actions[] } with feature ids from the surface state.",
|
|
289
|
+
items: { type: "object", description: "{ feature: string, actions: string[] }" }
|
|
290
|
+
},
|
|
291
|
+
systemPermissions: {
|
|
292
|
+
type: "array",
|
|
293
|
+
description: "manage_team | manage_billing | manage_settings | manage_permissions. Leave empty unless asked.",
|
|
294
|
+
items: { type: "string" }
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
required: ["name", "grants"]
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var PERMISSIONS_INSTRUCTIONS = "The user is on the access-profiles screen. `features` in the surface state lists every feature id this app actually has, with the actions each accepts \u2014 use those ids verbatim and never invent one. When they describe a role, call createPermissionProfile once with the full grant set. Say afterwards, in one line, what the profile can and cannot reach.";
|
|
301
|
+
function PermissionProfilesTab() {
|
|
302
|
+
const adapter = useOrgAdapterOptional();
|
|
303
|
+
const currentOrg = useOrganizationStore((s) => s.currentOrg);
|
|
304
|
+
const profiles = usePermissionsStore((s) => s.profiles);
|
|
305
|
+
const features = usePermissionsStore((s) => s.features);
|
|
306
|
+
const setProfiles = usePermissionsStore((s) => s.setProfiles);
|
|
307
|
+
const members = useOrganizationStore((s) => s.members);
|
|
308
|
+
const realProfile = usePermissionsStore((s) => s.realProfile);
|
|
309
|
+
const startImpersonation = usePermissionsStore((s) => s.startImpersonation);
|
|
310
|
+
const canPreview = (realProfile?.systemPermissions ?? []).includes("manage_permissions") ?? false;
|
|
311
|
+
const { t } = useTranslation();
|
|
312
|
+
const [editingProfile, setEditingProfile] = React.useState(null);
|
|
313
|
+
const [creating, setCreating] = React.useState(false);
|
|
314
|
+
const [duplicateSource, setDuplicateSource] = React.useState(null);
|
|
315
|
+
const [saving, setSaving] = React.useState(false);
|
|
316
|
+
const [error, setError] = React.useState(null);
|
|
317
|
+
const [deleting, setDeleting] = React.useState(null);
|
|
318
|
+
const handleDuplicate = (profile) => {
|
|
319
|
+
setEditingProfile(null);
|
|
320
|
+
setCreating(false);
|
|
321
|
+
setDuplicateSource({
|
|
322
|
+
...profile,
|
|
323
|
+
name: t("organization.permissions.copyName", { name: profile.name }) || `${profile.name} (copy)`,
|
|
324
|
+
isSystem: false
|
|
325
|
+
});
|
|
326
|
+
};
|
|
327
|
+
const surfaceRef = React.useRef({ features, profiles, adapter, currentOrg, setProfiles });
|
|
328
|
+
surfaceRef.current = { features, profiles, adapter, currentOrg, setProfiles };
|
|
329
|
+
const agentSurface = React.useMemo(() => ({
|
|
330
|
+
id: "org.permissions",
|
|
331
|
+
instructions: PERMISSIONS_INSTRUCTIONS,
|
|
332
|
+
describe: () => {
|
|
333
|
+
const o = surfaceRef.current;
|
|
334
|
+
return {
|
|
335
|
+
title: "Perfis de acesso",
|
|
336
|
+
state: {
|
|
337
|
+
features: o.features.map((f) => ({
|
|
338
|
+
id: f.id,
|
|
339
|
+
label: f.label,
|
|
340
|
+
group: f.group ?? null,
|
|
341
|
+
actions: f.actions ?? ["read", "create", "edit", "delete"]
|
|
342
|
+
})),
|
|
343
|
+
existingProfiles: o.profiles.map((p) => ({ id: p.id, name: p.name, system: Boolean(p.isSystem) }))
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
},
|
|
347
|
+
tools: [{
|
|
348
|
+
tool: CREATE_PROFILE_TOOL,
|
|
349
|
+
execute: async (args) => {
|
|
350
|
+
const o = surfaceRef.current;
|
|
351
|
+
if (!o.adapter || !o.currentOrg) return { ok: false, error: "no_organization" };
|
|
352
|
+
const known = new Set(o.features.map((f) => f.id));
|
|
353
|
+
const grants = {};
|
|
354
|
+
const unknown = [];
|
|
355
|
+
for (const entry of args.grants ?? []) {
|
|
356
|
+
const feature = entry?.feature;
|
|
357
|
+
if (!feature) continue;
|
|
358
|
+
if (!known.has(feature)) {
|
|
359
|
+
unknown.push(feature);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
grants[feature] = entry.actions ?? ["read"];
|
|
363
|
+
}
|
|
364
|
+
if (Object.keys(grants).length === 0) {
|
|
365
|
+
return { ok: false, error: "no_valid_feature", unknown, available: [...known] };
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
await o.adapter.createProfile(o.currentOrg.id, {
|
|
369
|
+
name: String(args.name ?? "").trim() || "Perfil",
|
|
370
|
+
description: args.description ? String(args.description) : void 0,
|
|
371
|
+
systemPermissions: args.systemPermissions ?? [],
|
|
372
|
+
grants
|
|
373
|
+
});
|
|
374
|
+
o.setProfiles(await o.adapter.listProfiles(o.currentOrg.id));
|
|
375
|
+
return { ok: true, name: args.name, features: Object.keys(grants), ignored: unknown };
|
|
376
|
+
} catch (e) {
|
|
377
|
+
return { ok: false, error: errorMessage(e) };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}]
|
|
381
|
+
}), []);
|
|
382
|
+
useAgentSurface(agentSurface);
|
|
383
|
+
const createActions = React.useMemo(() => [
|
|
384
|
+
{
|
|
385
|
+
id: "new",
|
|
386
|
+
label: t("organization.permissions.createProfile"),
|
|
387
|
+
description: t("organization.permissions.newByHandHint"),
|
|
388
|
+
icon: Plus,
|
|
389
|
+
intent: "primary",
|
|
390
|
+
creates: true,
|
|
391
|
+
onSelect: () => setCreating(true)
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
id: "ai",
|
|
395
|
+
label: t("assistant.askAI"),
|
|
396
|
+
description: t("organization.permissions.newWithAIHint"),
|
|
397
|
+
icon: WandSparkles,
|
|
398
|
+
ai: true,
|
|
399
|
+
intent: "secondary",
|
|
400
|
+
creates: true,
|
|
401
|
+
onSelect: () => askAI({
|
|
402
|
+
hint: t("organization.permissions.askPlaceholder"),
|
|
403
|
+
intro: {
|
|
404
|
+
role: "setup",
|
|
405
|
+
persona: t("organization.permissions.askPersona"),
|
|
406
|
+
pitch: t("organization.permissions.askPitch"),
|
|
407
|
+
description: t("organization.permissions.askDescription"),
|
|
408
|
+
suggestions: [0, 1, 2].map((i) => ({ label: t(`organization.permissions.askSuggestion.${i}`) }))
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
], [t]);
|
|
413
|
+
const memberCountByProfile = React.useMemo(() => {
|
|
414
|
+
const counts = {};
|
|
415
|
+
for (const m of members) {
|
|
416
|
+
counts[m.profileId] = (counts[m.profileId] ?? 0) + 1;
|
|
417
|
+
}
|
|
418
|
+
return counts;
|
|
419
|
+
}, [members]);
|
|
420
|
+
const handleSave = async (data) => {
|
|
421
|
+
if (!adapter || !currentOrg) return;
|
|
422
|
+
setSaving(true);
|
|
423
|
+
setError(null);
|
|
424
|
+
try {
|
|
425
|
+
if (editingProfile) {
|
|
426
|
+
await adapter.updateProfile(currentOrg.id, editingProfile.id, data);
|
|
427
|
+
} else {
|
|
428
|
+
await adapter.createProfile(currentOrg.id, data);
|
|
429
|
+
}
|
|
430
|
+
const updated = await adapter.listProfiles(currentOrg.id);
|
|
431
|
+
setProfiles(updated);
|
|
432
|
+
setEditingProfile(null);
|
|
433
|
+
setCreating(false);
|
|
434
|
+
setDuplicateSource(null);
|
|
435
|
+
} catch (e) {
|
|
436
|
+
setError(errorMessage(e));
|
|
437
|
+
} finally {
|
|
438
|
+
setSaving(false);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
const handleDelete = async (profile) => {
|
|
442
|
+
setDeleting(null);
|
|
443
|
+
if (!adapter || !currentOrg || profile.isSystem) return;
|
|
444
|
+
try {
|
|
445
|
+
await adapter.deleteProfile(currentOrg.id, profile.id);
|
|
446
|
+
const updated = await adapter.listProfiles(currentOrg.id);
|
|
447
|
+
setProfiles(updated);
|
|
448
|
+
} catch (e) {
|
|
449
|
+
setError(errorMessage(e));
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
if (creating || editingProfile || duplicateSource) {
|
|
453
|
+
return /* @__PURE__ */ jsx(
|
|
454
|
+
PermissionMatrixEditor,
|
|
455
|
+
{
|
|
456
|
+
features: [...coreFeatures().filter((c) => !features.some((f) => f.id === c.id)), ...features],
|
|
457
|
+
profile: editingProfile ?? duplicateSource ?? void 0,
|
|
458
|
+
isNew: !editingProfile,
|
|
459
|
+
parentLabel: t("organization.permissions.title"),
|
|
460
|
+
onSave: handleSave,
|
|
461
|
+
onCancel: () => {
|
|
462
|
+
setEditingProfile(null);
|
|
463
|
+
setCreating(false);
|
|
464
|
+
setDuplicateSource(null);
|
|
465
|
+
},
|
|
466
|
+
saving
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
471
|
+
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx(PageActions, { actions: createActions }) }),
|
|
472
|
+
error && /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: error }),
|
|
473
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-3", children: profiles.length === 0 ? (
|
|
474
|
+
// Roles load via central org hydration; show skeletons until they arrive.
|
|
475
|
+
Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex items-center gap-4 p-4", children: [
|
|
476
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-10 shrink-0 rounded-lg" }),
|
|
477
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
|
|
478
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-40" }),
|
|
479
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-64" })
|
|
480
|
+
] })
|
|
481
|
+
] }) }, `sk-${i}`))
|
|
482
|
+
) : profiles.map((profile) => /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex items-center gap-4 p-4", children: [
|
|
483
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10", children: /* @__PURE__ */ jsx(Shield, { className: "h-5 w-5 text-primary" }) }),
|
|
484
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
485
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
486
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-sm", children: profile.name }),
|
|
487
|
+
profile.isSystem && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: t("organization.permissions.system") })
|
|
488
|
+
] }),
|
|
489
|
+
profile.description && /* @__PURE__ */ jsx(CardDescription, { className: "text-xs mt-0.5", children: profile.description }),
|
|
490
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-1", children: t("organization.permissions.memberCount", { count: String(memberCountByProfile[profile.id] ?? 0), plural: (memberCountByProfile[profile.id] ?? 0) !== 1 ? "s" : "" }) })
|
|
491
|
+
] }),
|
|
492
|
+
/* @__PURE__ */ jsx(
|
|
493
|
+
RowActions,
|
|
494
|
+
{
|
|
495
|
+
actions: [
|
|
496
|
+
{
|
|
497
|
+
id: "preview",
|
|
498
|
+
label: t("organization.permissions.previewAs"),
|
|
499
|
+
icon: Eye,
|
|
500
|
+
hidden: !canPreview || profile.id === realProfile?.id,
|
|
501
|
+
onSelect: () => startImpersonation(profile)
|
|
502
|
+
},
|
|
503
|
+
// Duplicate — always available; the primary (and only) way to
|
|
504
|
+
// customize a static system role.
|
|
505
|
+
{
|
|
506
|
+
id: "duplicate",
|
|
507
|
+
label: t("organization.permissions.duplicate"),
|
|
508
|
+
icon: Copy,
|
|
509
|
+
onSelect: () => handleDuplicate(profile)
|
|
510
|
+
},
|
|
511
|
+
// Edit + delete are for custom roles only — system roles are static.
|
|
512
|
+
{
|
|
513
|
+
id: "edit",
|
|
514
|
+
label: t("common.edit"),
|
|
515
|
+
icon: Pencil,
|
|
516
|
+
hidden: Boolean(profile.isSystem),
|
|
517
|
+
onSelect: () => setEditingProfile(profile)
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
id: "delete",
|
|
521
|
+
label: t("common.delete"),
|
|
522
|
+
icon: Trash2,
|
|
523
|
+
intent: "danger",
|
|
524
|
+
hidden: Boolean(profile.isSystem),
|
|
525
|
+
onSelect: () => setDeleting(profile)
|
|
526
|
+
}
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
] }) }, profile.id)) }),
|
|
531
|
+
/* @__PURE__ */ jsx(
|
|
532
|
+
DeleteConfirmDialog,
|
|
533
|
+
{
|
|
534
|
+
open: Boolean(deleting),
|
|
535
|
+
onClose: () => setDeleting(null),
|
|
536
|
+
onConfirm: () => {
|
|
537
|
+
if (deleting) void handleDelete(deleting);
|
|
538
|
+
},
|
|
539
|
+
entityName: t("organization.permissions.profile"),
|
|
540
|
+
displayValue: deleting?.name
|
|
541
|
+
}
|
|
542
|
+
)
|
|
543
|
+
] });
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export { PermissionProfilesTab };
|
|
547
|
+
//# sourceMappingURL=PermissionProfilesTab-3ZPRCHMZ.js.map
|
|
548
|
+
//# sourceMappingURL=PermissionProfilesTab-3ZPRCHMZ.js.map
|