@fayz-ai/admin 0.62.0 → 0.65.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/dist/{CommandPalette-B5PMQFDG.js → CommandPalette-XDJJ5E3O.js} +3 -3
  2. package/dist/{CommandPalette-B5PMQFDG.js.map → CommandPalette-XDJJ5E3O.js.map} +1 -1
  3. package/dist/ConnectedBrandingSettings-43ZM3HCI.js +680 -0
  4. package/dist/ConnectedBrandingSettings-43ZM3HCI.js.map +1 -0
  5. package/dist/ConnectedCompanySettings-I4MALI2Q.js +197 -0
  6. package/dist/ConnectedCompanySettings-I4MALI2Q.js.map +1 -0
  7. package/dist/ConnectedModulesSettings-A2VJ5BMJ.js +563 -0
  8. package/dist/ConnectedModulesSettings-A2VJ5BMJ.js.map +1 -0
  9. package/dist/ConnectedSecuritySettings-25AHA4HJ.js +189 -0
  10. package/dist/ConnectedSecuritySettings-25AHA4HJ.js.map +1 -0
  11. package/dist/ConnectedUserProfile-E7USJRUE.js +424 -0
  12. package/dist/ConnectedUserProfile-E7USJRUE.js.map +1 -0
  13. package/dist/FieldRulesEditor-YGIPKI5T.js +129 -0
  14. package/dist/FieldRulesEditor-YGIPKI5T.js.map +1 -0
  15. package/dist/GettingStartedWidget-HMIJ3Y24.js +12 -0
  16. package/dist/GettingStartedWidget-HMIJ3Y24.js.map +1 -0
  17. package/dist/PermissionProfilesTab-3ZPRCHMZ.js +548 -0
  18. package/dist/PermissionProfilesTab-3ZPRCHMZ.js.map +1 -0
  19. package/dist/PersonTimeline-HQZT527S.js +3 -0
  20. package/dist/{PersonTimeline-X7JTPUWJ.js.map → PersonTimeline-HQZT527S.js.map} +1 -1
  21. package/dist/ProjectsSettings-7O2F27MD.js +5 -0
  22. package/dist/ProjectsSettings-7O2F27MD.js.map +1 -0
  23. package/dist/ScheduleEditor-54ELWR45.js +8 -0
  24. package/dist/{ScheduleEditor-L2UJXKMY.js.map → ScheduleEditor-54ELWR45.js.map} +1 -1
  25. package/dist/SeuSiteWidget-QLSUVC63.js +6 -0
  26. package/dist/SeuSiteWidget-QLSUVC63.js.map +1 -0
  27. package/dist/SubscriptionPage-5GE5HFJK.js +17 -0
  28. package/dist/{SubscriptionPage-IDVEFLZ5.js.map → SubscriptionPage-5GE5HFJK.js.map} +1 -1
  29. package/dist/TeamTab-6RAULFLW.js +502 -0
  30. package/dist/TeamTab-6RAULFLW.js.map +1 -0
  31. package/dist/TrialWidget-Z6CLUCU3.js +7 -0
  32. package/dist/TrialWidget-Z6CLUCU3.js.map +1 -0
  33. package/dist/app/AdminShell.d.ts.map +1 -1
  34. package/dist/app/admin-app.d.ts.map +1 -1
  35. package/dist/app/config.d.ts +14 -0
  36. package/dist/app/config.d.ts.map +1 -1
  37. package/dist/app/routing.d.ts +6 -0
  38. package/dist/app/routing.d.ts.map +1 -1
  39. package/dist/app/scaffold.d.ts.map +1 -1
  40. package/dist/billing/index.js +9 -4
  41. package/dist/chunk-2H46WDG5.js +53 -0
  42. package/dist/chunk-2H46WDG5.js.map +1 -0
  43. package/dist/chunk-3HLX7YB2.js +94 -0
  44. package/dist/chunk-3HLX7YB2.js.map +1 -0
  45. package/dist/chunk-3OUD4XBK.js +3 -0
  46. package/dist/chunk-3OUD4XBK.js.map +1 -0
  47. package/dist/{chunk-IXSY4GPK.js → chunk-5D5QLMOQ.js} +5 -3
  48. package/dist/chunk-5D5QLMOQ.js.map +1 -0
  49. package/dist/chunk-5WR62X2R.js +1081 -0
  50. package/dist/chunk-5WR62X2R.js.map +1 -0
  51. package/dist/{chunk-KPYRTOCK.js → chunk-6CQFGMEO.js} +4 -140
  52. package/dist/chunk-6CQFGMEO.js.map +1 -0
  53. package/dist/chunk-6MMSYC2I.js +116 -0
  54. package/dist/chunk-6MMSYC2I.js.map +1 -0
  55. package/dist/chunk-6QFOE3UD.js +1466 -0
  56. package/dist/chunk-6QFOE3UD.js.map +1 -0
  57. package/dist/chunk-6UUFVQ2E.js +283 -0
  58. package/dist/chunk-6UUFVQ2E.js.map +1 -0
  59. package/dist/chunk-7WNLL3F6.js +7889 -0
  60. package/dist/chunk-7WNLL3F6.js.map +1 -0
  61. package/dist/chunk-A74IHAQE.js +439 -0
  62. package/dist/chunk-A74IHAQE.js.map +1 -0
  63. package/dist/{chunk-JOBRLBIQ.js → chunk-AFA6IO23.js} +4 -3
  64. package/dist/chunk-AFA6IO23.js.map +1 -0
  65. package/dist/{chunk-P5UIGQFY.js → chunk-ALCSIN3Z.js} +5 -5
  66. package/dist/chunk-ALCSIN3Z.js.map +1 -0
  67. package/dist/chunk-AWM4AMBS.js +39 -0
  68. package/dist/chunk-AWM4AMBS.js.map +1 -0
  69. package/dist/{chunk-OQARLKZN.js → chunk-BG5Z6KLN.js} +6 -18
  70. package/dist/chunk-BG5Z6KLN.js.map +1 -0
  71. package/dist/chunk-DPNFZONC.js +19 -0
  72. package/dist/chunk-DPNFZONC.js.map +1 -0
  73. package/dist/chunk-DRCV643L.js +193 -0
  74. package/dist/chunk-DRCV643L.js.map +1 -0
  75. package/dist/chunk-E6DF3VR6.js +206 -0
  76. package/dist/chunk-E6DF3VR6.js.map +1 -0
  77. package/dist/chunk-ENHCVGXZ.js +120 -0
  78. package/dist/chunk-ENHCVGXZ.js.map +1 -0
  79. package/dist/chunk-FK6RHGI7.js +56 -0
  80. package/dist/chunk-FK6RHGI7.js.map +1 -0
  81. package/dist/chunk-G5HNA5QK.js +233 -0
  82. package/dist/chunk-G5HNA5QK.js.map +1 -0
  83. package/dist/chunk-HCFO4Z6A.js +53 -0
  84. package/dist/chunk-HCFO4Z6A.js.map +1 -0
  85. package/dist/chunk-JFIU6NQP.js +51 -0
  86. package/dist/chunk-JFIU6NQP.js.map +1 -0
  87. package/dist/chunk-JRZJBMOY.js +3 -0
  88. package/dist/chunk-JRZJBMOY.js.map +1 -0
  89. package/dist/{chunk-3YP7KW47.js → chunk-JVVOL4BB.js} +41 -512
  90. package/dist/chunk-JVVOL4BB.js.map +1 -0
  91. package/dist/chunk-KAQ3UIAA.js +415 -0
  92. package/dist/chunk-KAQ3UIAA.js.map +1 -0
  93. package/dist/{chunk-P6XUDT2P.js → chunk-KJELMULA.js} +4 -51
  94. package/dist/chunk-KJELMULA.js.map +1 -0
  95. package/dist/chunk-KLO7DKXX.js +1651 -0
  96. package/dist/chunk-KLO7DKXX.js.map +1 -0
  97. package/dist/chunk-M2CMDWUI.js +75 -0
  98. package/dist/chunk-M2CMDWUI.js.map +1 -0
  99. package/dist/chunk-PQ47MLTH.js +255 -0
  100. package/dist/chunk-PQ47MLTH.js.map +1 -0
  101. package/dist/{chunk-VX3WJLKK.js → chunk-QD7BMUDF.js} +17 -8
  102. package/dist/chunk-QD7BMUDF.js.map +1 -0
  103. package/dist/{chunk-E4MESMNR.js → chunk-QTSKCPSW.js} +7 -10
  104. package/dist/chunk-QTSKCPSW.js.map +1 -0
  105. package/dist/chunk-S56FQKPL.js +186 -0
  106. package/dist/chunk-S56FQKPL.js.map +1 -0
  107. package/dist/chunk-SGAFPNMA.js +75 -0
  108. package/dist/chunk-SGAFPNMA.js.map +1 -0
  109. package/dist/{chunk-OD7P4L3D.js → chunk-SOKCLVM4.js} +3 -3
  110. package/dist/{chunk-OD7P4L3D.js.map → chunk-SOKCLVM4.js.map} +1 -1
  111. package/dist/chunk-TL42NKTH.js +3 -0
  112. package/dist/chunk-TL42NKTH.js.map +1 -0
  113. package/dist/chunk-U6ZCO2PS.js +2964 -0
  114. package/dist/chunk-U6ZCO2PS.js.map +1 -0
  115. package/dist/chunk-VSXNROYH.js +16 -0
  116. package/dist/chunk-VSXNROYH.js.map +1 -0
  117. package/dist/chunk-WPFZCYZL.js +619 -0
  118. package/dist/chunk-WPFZCYZL.js.map +1 -0
  119. package/dist/chunk-XFTNIBDW.js +23 -0
  120. package/dist/chunk-XFTNIBDW.js.map +1 -0
  121. package/dist/chunk-Y66JDL6I.js +1020 -0
  122. package/dist/chunk-Y66JDL6I.js.map +1 -0
  123. package/dist/chunk-YEHIBPIX.js +137 -0
  124. package/dist/chunk-YEHIBPIX.js.map +1 -0
  125. package/dist/chunk-YKPPCV2Z.js +53 -0
  126. package/dist/chunk-YKPPCV2Z.js.map +1 -0
  127. package/dist/components/chat/markdown.d.ts.map +1 -1
  128. package/dist/components/plugins/PluginSettingsPanel.d.ts.map +1 -1
  129. package/dist/components/settings/SettingsPage.d.ts.map +1 -1
  130. package/dist/connect-XKQJOWR7.js +18 -0
  131. package/dist/connect-XKQJOWR7.js.map +1 -0
  132. package/dist/crud/CrudDetailPage.d.ts.map +1 -1
  133. package/dist/crud/CrudListView.d.ts.map +1 -1
  134. package/dist/crud/CrudPage.d.ts.map +1 -1
  135. package/dist/crud/RelatedListTab.d.ts.map +1 -1
  136. package/dist/crud/archetypes/EntityLink.d.ts +1 -1
  137. package/dist/crud/archetypes/EntityLink.d.ts.map +1 -1
  138. package/dist/crud/record-chrome.d.ts.map +1 -1
  139. package/dist/entity-routes-PGU7EHAD.js +4 -0
  140. package/dist/{entity-routes-LXOBW5IB.js.map → entity-routes-PGU7EHAD.js.map} +1 -1
  141. package/dist/hooks/useModuleNavigation.d.ts.map +1 -1
  142. package/dist/impersonation/ImpersonateDialog.d.ts.map +1 -1
  143. package/dist/impersonation/session.d.ts.map +1 -1
  144. package/dist/index.d.ts +2 -2
  145. package/dist/index.d.ts.map +1 -1
  146. package/dist/index.js +11951 -33576
  147. package/dist/index.js.map +1 -1
  148. package/dist/lib/attention.d.ts +6 -0
  149. package/dist/lib/attention.d.ts.map +1 -1
  150. package/dist/lib/entity-routes.d.ts.map +1 -1
  151. package/dist/org/index.js +4 -2
  152. package/dist/plugins/PluginRegistryManager.d.ts.map +1 -1
  153. package/dist/settings/MessageTemplateRow.d.ts.map +1 -1
  154. package/dist/timeline/PersonTimeline.d.ts.map +1 -1
  155. package/dist/timeline/TimelineList.d.ts.map +1 -1
  156. package/dist/units/UnitRailPage.d.ts.map +1 -1
  157. package/dist/units/useUnitFilter.d.ts.map +1 -1
  158. package/package.json +5 -5
  159. package/dist/PersonTimeline-X7JTPUWJ.js +0 -3
  160. package/dist/ScheduleEditor-L2UJXKMY.js +0 -6
  161. package/dist/SubscriptionPage-IDVEFLZ5.js +0 -8
  162. package/dist/chunk-3YP7KW47.js.map +0 -1
  163. package/dist/chunk-E4MESMNR.js.map +0 -1
  164. package/dist/chunk-IXSY4GPK.js.map +0 -1
  165. package/dist/chunk-JOBRLBIQ.js.map +0 -1
  166. package/dist/chunk-KPYRTOCK.js.map +0 -1
  167. package/dist/chunk-OQARLKZN.js.map +0 -1
  168. package/dist/chunk-P5UIGQFY.js.map +0 -1
  169. package/dist/chunk-P6XUDT2P.js.map +0 -1
  170. package/dist/chunk-VX3WJLKK.js.map +0 -1
  171. 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