@datatechsolutions/ui 3.11.1 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/astrlabe/index.js +109 -109
  2. package/dist/astrlabe/index.mjs +5 -5
  3. package/dist/astrlabe/workflow-canvas.js +5 -5
  4. package/dist/astrlabe/workflow-canvas.mjs +4 -4
  5. package/dist/{chunk-ZUU7G3PC.js → chunk-22XPYFHJ.js} +2 -2
  6. package/dist/chunk-22XPYFHJ.js.map +1 -0
  7. package/dist/{chunk-7DZ2C5IN.mjs → chunk-4D23CQZM.mjs} +80 -16
  8. package/dist/chunk-4D23CQZM.mjs.map +1 -0
  9. package/dist/{chunk-QASAHGLE.js → chunk-5OZTI7TR.js} +4 -4
  10. package/dist/{chunk-QASAHGLE.js.map → chunk-5OZTI7TR.js.map} +1 -1
  11. package/dist/{chunk-E3NMVWRL.mjs → chunk-5TLKFOEL.mjs} +85 -73
  12. package/dist/chunk-5TLKFOEL.mjs.map +1 -0
  13. package/dist/{chunk-K3LIDGMA.mjs → chunk-BPMYNE7S.mjs} +3 -3
  14. package/dist/{chunk-K3LIDGMA.mjs.map → chunk-BPMYNE7S.mjs.map} +1 -1
  15. package/dist/{chunk-IHOCYBHM.mjs → chunk-EDI46QA7.mjs} +3 -3
  16. package/dist/{chunk-IHOCYBHM.mjs.map → chunk-EDI46QA7.mjs.map} +1 -1
  17. package/dist/{chunk-OSXBMLZP.js → chunk-FKRIQYRG.js} +68 -68
  18. package/dist/{chunk-OSXBMLZP.js.map → chunk-FKRIQYRG.js.map} +1 -1
  19. package/dist/{chunk-DPXCJVJT.js → chunk-HTODLUKG.js} +9 -9
  20. package/dist/{chunk-DPXCJVJT.js.map → chunk-HTODLUKG.js.map} +1 -1
  21. package/dist/{chunk-SSKKTSMA.js → chunk-ID55ABBB.js} +35 -35
  22. package/dist/{chunk-SSKKTSMA.js.map → chunk-ID55ABBB.js.map} +1 -1
  23. package/dist/{chunk-OO4YMN4P.js → chunk-LRNVB7MO.js} +4 -4
  24. package/dist/{chunk-OO4YMN4P.js.map → chunk-LRNVB7MO.js.map} +1 -1
  25. package/dist/{chunk-APONR6ZM.js → chunk-MFF37C24.js} +213 -201
  26. package/dist/chunk-MFF37C24.js.map +1 -0
  27. package/dist/{chunk-NW32BM3F.js → chunk-MYHBXIO2.js} +34 -34
  28. package/dist/{chunk-NW32BM3F.js.map → chunk-MYHBXIO2.js.map} +1 -1
  29. package/dist/{chunk-S46LUR7O.mjs → chunk-N5GQIT7A.mjs} +10 -8
  30. package/dist/chunk-N5GQIT7A.mjs.map +1 -0
  31. package/dist/{chunk-IGOMJHC6.mjs → chunk-NBW6WJQT.mjs} +2 -2
  32. package/dist/chunk-NBW6WJQT.mjs.map +1 -0
  33. package/dist/{chunk-Q6MMJYEG.mjs → chunk-NK4H3JWN.mjs} +3 -3
  34. package/dist/{chunk-Q6MMJYEG.mjs.map → chunk-NK4H3JWN.mjs.map} +1 -1
  35. package/dist/{chunk-O4DIKNNH.mjs → chunk-NQGLSM46.mjs} +3 -3
  36. package/dist/{chunk-O4DIKNNH.mjs.map → chunk-NQGLSM46.mjs.map} +1 -1
  37. package/dist/{chunk-S4LHC5SF.mjs → chunk-OM5NNUD2.mjs} +3 -3
  38. package/dist/{chunk-S4LHC5SF.mjs.map → chunk-OM5NNUD2.mjs.map} +1 -1
  39. package/dist/{chunk-OKCEK7GH.mjs → chunk-OW5LSEHU.mjs} +86 -43
  40. package/dist/chunk-OW5LSEHU.mjs.map +1 -0
  41. package/dist/{chunk-LHAOIPYP.mjs → chunk-PIIY2Z2O.mjs} +3 -3
  42. package/dist/{chunk-LHAOIPYP.mjs.map → chunk-PIIY2Z2O.mjs.map} +1 -1
  43. package/dist/{chunk-CLXEVSGB.js → chunk-PLEWG2L7.js} +13 -13
  44. package/dist/{chunk-CLXEVSGB.js.map → chunk-PLEWG2L7.js.map} +1 -1
  45. package/dist/{chunk-OD2AZBEX.js → chunk-QSCWV47U.js} +25 -25
  46. package/dist/{chunk-OD2AZBEX.js.map → chunk-QSCWV47U.js.map} +1 -1
  47. package/dist/{chunk-QKWNQOJB.mjs → chunk-RBH723MG.mjs} +3 -3
  48. package/dist/{chunk-QKWNQOJB.mjs.map → chunk-RBH723MG.mjs.map} +1 -1
  49. package/dist/{chunk-QDW3IJIL.mjs → chunk-RT264BD4.mjs} +3 -3
  50. package/dist/{chunk-QDW3IJIL.mjs.map → chunk-RT264BD4.mjs.map} +1 -1
  51. package/dist/{chunk-MN777KLN.js → chunk-TDNBO3V7.js} +45 -45
  52. package/dist/{chunk-MN777KLN.js.map → chunk-TDNBO3V7.js.map} +1 -1
  53. package/dist/{chunk-TIJXCRM4.js → chunk-ULKZGM2T.js} +88 -45
  54. package/dist/chunk-ULKZGM2T.js.map +1 -0
  55. package/dist/{chunk-3U3CIARA.mjs → chunk-UVFWKDRO.mjs} +3 -3
  56. package/dist/{chunk-3U3CIARA.mjs.map → chunk-UVFWKDRO.mjs.map} +1 -1
  57. package/dist/{chunk-KDVZIDVF.js → chunk-V3UZCOZI.js} +102 -38
  58. package/dist/chunk-V3UZCOZI.js.map +1 -0
  59. package/dist/{chunk-LM6BJOKX.js → chunk-VYU2FYDE.js} +55 -55
  60. package/dist/{chunk-LM6BJOKX.js.map → chunk-VYU2FYDE.js.map} +1 -1
  61. package/dist/{chunk-I77TTBYO.mjs → chunk-WLNY3GVM.mjs} +6 -6
  62. package/dist/{chunk-I77TTBYO.mjs.map → chunk-WLNY3GVM.mjs.map} +1 -1
  63. package/dist/{chunk-BTMHQCDB.js → chunk-XGAPZT4J.js} +66 -64
  64. package/dist/chunk-XGAPZT4J.js.map +1 -0
  65. package/dist/{chunk-VHU5FWFB.mjs → chunk-YYNGXKUE.mjs} +3 -3
  66. package/dist/{chunk-VHU5FWFB.mjs.map → chunk-YYNGXKUE.mjs.map} +1 -1
  67. package/dist/{chunk-CVCMJJ56.js → chunk-Z75CEP4V.js} +10 -10
  68. package/dist/{chunk-CVCMJJ56.js.map → chunk-Z75CEP4V.js.map} +1 -1
  69. package/dist/index.d.mts +13 -4
  70. package/dist/index.d.ts +13 -4
  71. package/dist/index.js +664 -664
  72. package/dist/index.mjs +3 -3
  73. package/dist/platform/admin/index.js +11 -11
  74. package/dist/platform/admin/index.mjs +5 -5
  75. package/dist/platform/agents-workspace.js +8 -8
  76. package/dist/platform/agents-workspace.mjs +7 -7
  77. package/dist/platform/app-shell.js +5 -5
  78. package/dist/platform/app-shell.mjs +4 -4
  79. package/dist/platform/auth/index.js +23 -23
  80. package/dist/platform/auth/index.mjs +5 -5
  81. package/dist/platform/billing/index.js +5 -5
  82. package/dist/platform/billing/index.mjs +4 -4
  83. package/dist/platform/impersonation/index.js +5 -5
  84. package/dist/platform/impersonation/index.mjs +4 -4
  85. package/dist/platform/index.js +94 -94
  86. package/dist/platform/index.mjs +19 -19
  87. package/dist/platform/pages/index.d.mts +77 -18
  88. package/dist/platform/pages/index.d.ts +77 -18
  89. package/dist/platform/pages/index.js +1090 -506
  90. package/dist/platform/pages/index.js.map +1 -1
  91. package/dist/platform/pages/index.mjs +790 -206
  92. package/dist/platform/pages/index.mjs.map +1 -1
  93. package/dist/platform/settings/index.js +8 -8
  94. package/dist/platform/settings/index.mjs +7 -7
  95. package/dist/platform/workflow-api-client.d.mts +1 -1
  96. package/dist/platform/workflow-api-client.d.ts +1 -1
  97. package/dist/platform/workflow-api-client.js +62 -62
  98. package/dist/platform/workflow-api-client.mjs +1 -1
  99. package/dist/platform/workflow-canvas-shell.js +6 -6
  100. package/dist/platform/workflow-canvas-shell.mjs +5 -5
  101. package/dist/{workflow-api-client-E1QFRgeP.d.ts → workflow-api-client-CThkBj5j.d.ts} +4 -0
  102. package/dist/{workflow-api-client-D9_0Spdz.d.mts → workflow-api-client-crR8L7fy.d.mts} +4 -0
  103. package/package.json +1 -1
  104. package/dist/chunk-7DZ2C5IN.mjs.map +0 -1
  105. package/dist/chunk-APONR6ZM.js.map +0 -1
  106. package/dist/chunk-BTMHQCDB.js.map +0 -1
  107. package/dist/chunk-E3NMVWRL.mjs.map +0 -1
  108. package/dist/chunk-IGOMJHC6.mjs.map +0 -1
  109. package/dist/chunk-KDVZIDVF.js.map +0 -1
  110. package/dist/chunk-OKCEK7GH.mjs.map +0 -1
  111. package/dist/chunk-S46LUR7O.mjs.map +0 -1
  112. package/dist/chunk-TIJXCRM4.js.map +0 -1
  113. package/dist/chunk-ZUU7G3PC.js.map +0 -1
@@ -1,19 +1,19 @@
1
1
  "use client";
2
2
  import { adaptWorkflowGraphToUi, formatDurationMs } from '../../chunk-UQXVCVAN.mjs';
3
- import { WorkflowWorkspace } from '../../chunk-7DZ2C5IN.mjs';
4
- export { RolesPageView, UsersPageView } from '../../chunk-7DZ2C5IN.mjs';
5
- import { DatasourceModal, findDialect, findCategory, DIALECT_CATEGORIES } from '../../chunk-S4LHC5SF.mjs';
6
- export { DIALECT_CATEGORIES, DatasourceFormModal, DatasourceModal, findCategory, findDialect } from '../../chunk-S4LHC5SF.mjs';
7
- import { defaultRuleForm, RuleForm, ExecutionTimelinePanel, defaultRuleCondition, defaultRuleAction, RuleConditionBuilder, RuleActionBuilder } from '../../chunk-E3NMVWRL.mjs';
3
+ import { WorkflowWorkspace } from '../../chunk-4D23CQZM.mjs';
4
+ export { RolesPageView, UsersPageView } from '../../chunk-4D23CQZM.mjs';
5
+ import { DIALECT_CATEGORIES, DatasourceModal, findDialect, findCategory } from '../../chunk-OM5NNUD2.mjs';
6
+ export { DIALECT_CATEGORIES, DatasourceFormModal, DatasourceModal, findCategory, findDialect } from '../../chunk-OM5NNUD2.mjs';
7
+ import { defaultRuleForm, RuleForm, ExecutionTimelinePanel, defaultRuleCondition, defaultRuleAction, RuleConditionBuilder, RuleActionBuilder } from '../../chunk-5TLKFOEL.mjs';
8
8
  import '../../chunk-JB6RNAD2.mjs';
9
9
  import '../../chunk-LEXBTVGM.mjs';
10
- import '../../chunk-IHOCYBHM.mjs';
11
- import { HeroSection, SearchBar, PageLoadingState, PageEmptyState, EntityCard, CreateActionButton, GlassModal, FormGrid, FormSelect, FormInput, FormTextarea, SectionHeader, Text, Form, FormActionsRow, Table, TableHead, TableRow, TableHeader, TableBody, TableCell, InlineForm, CopyableId, ListCard, ListCardItem, Avatar, SectionCard, Tabs, TabsList, TabsTrigger, TabsContent, StepIndicator, FilterTileButton, FormToggle, Spinner, StatusBadge, ChipPicker, FormCheckbox, SegmentedControl, DatePicker, TimePicker } from '../../chunk-OKCEK7GH.mjs';
12
- import '../../chunk-7VJ7CMMT.mjs';
13
- import '../../chunk-IGOMJHC6.mjs';
10
+ import '../../chunk-EDI46QA7.mjs';
11
+ import { HeroSection, SearchBar, FormSection, ChipPicker, PageLoadingState, PageEmptyState, EntityCard, CreateActionButton, GlassModal, FormGrid, FormSelect, FormInput, FormTextarea, SectionHeader, Text, Form, FormActionsRow, Table, TableHead, TableRow, TableHeader, TableBody, TableCell, InlineForm, CopyableId, ListCard, ListCardItem, Avatar, SectionCard, Tabs, TabsList, TabsTrigger, TabsContent, StepIndicator, FilterTileButton, FormToggle, Spinner, StatusBadge, FormCheckbox, SegmentedControl, DatePicker, TimePicker } from '../../chunk-OW5LSEHU.mjs';
12
+ import '../../chunk-NBW6WJQT.mjs';
14
13
  import '../../chunk-3AY5HIQ6.mjs';
14
+ import '../../chunk-7VJ7CMMT.mjs';
15
15
  import '../../chunk-PLTLRL2V.mjs';
16
- import { Badge, Button } from '../../chunk-WR55H7DH.mjs';
16
+ import { Button, Badge } from '../../chunk-WR55H7DH.mjs';
17
17
  import '../../chunk-D2JF6C3E.mjs';
18
18
  import { useLink } from '../../chunk-QWG2FMUN.mjs';
19
19
  import '../../chunk-G7JQ4OCE.mjs';
@@ -23,9 +23,52 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
23
23
 
24
24
  function AgentsModelsPageView({ labels, models, loading }) {
25
25
  const [searchTerm, setSearchTerm] = useState("");
26
- const allModels = models;
27
- const filteredModels = searchTerm.trim() ? allModels.filter((model) => model.name.toLowerCase().includes(searchTerm.trim().toLowerCase())) : allModels;
28
- const isEmpty = allModels.length === 0;
26
+ const [filtersOpen, setFiltersOpen] = useState(false);
27
+ const [selectedProviders, setSelectedProviders] = useState([]);
28
+ const [enabledFilter, setEnabledFilter] = useState("all");
29
+ const isEmpty = models.length === 0;
30
+ const filteredModels = useMemo(() => {
31
+ const term = searchTerm.trim().toLowerCase();
32
+ return models.filter((model) => {
33
+ if (term && !model.name.toLowerCase().includes(term)) return false;
34
+ if (selectedProviders.length > 0 && !selectedProviders.includes(model.provider)) return false;
35
+ if (enabledFilter === "enabled" && !model.enabled) return false;
36
+ if (enabledFilter === "disabled" && model.enabled) return false;
37
+ return true;
38
+ });
39
+ }, [models, searchTerm, selectedProviders, enabledFilter]);
40
+ const providerChipItems = useMemo(
41
+ () => [...new Set(models.map((m) => m.provider))].sort().map((p) => ({
42
+ id: p,
43
+ name: p,
44
+ style: { bg: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300", text: "" }
45
+ })),
46
+ [models]
47
+ );
48
+ const statusLabelFor = (value) => {
49
+ if (value === "enabled") return labels.statusEnabled ?? labels.enabled;
50
+ if (value === "disabled") return labels.statusDisabled ?? labels.disabled;
51
+ return labels.statusAll ?? "Todos";
52
+ };
53
+ const activeFilters = useMemo(() => {
54
+ const chips = [];
55
+ for (const value of selectedProviders) {
56
+ chips.push({ type: "provider", value, label: providerChipItems.find((i) => i.id === value)?.name ?? value });
57
+ }
58
+ if (enabledFilter !== "all") {
59
+ chips.push({ type: "enabled", value: enabledFilter, label: statusLabelFor(enabledFilter) });
60
+ }
61
+ return chips;
62
+ }, [selectedProviders, enabledFilter, providerChipItems]);
63
+ const filterCount = activeFilters.length;
64
+ const removeFilter = (filter) => {
65
+ if (filter.type === "provider") setSelectedProviders((prev) => prev.filter((v) => v !== filter.value));
66
+ if (filter.type === "enabled") setEnabledFilter("all");
67
+ };
68
+ const clearAllFilters = () => {
69
+ setSelectedProviders([]);
70
+ setEnabledFilter("all");
71
+ };
29
72
  const hero = /* @__PURE__ */ jsx(
30
73
  HeroSection,
31
74
  {
@@ -40,7 +83,32 @@ function AgentsModelsPageView({ labels, models, loading }) {
40
83
  searchTerm,
41
84
  onSearchChange: setSearchTerm,
42
85
  placeholder: labels.searchPlaceholder ?? labels.title,
43
- noBorder: true
86
+ noBorder: true,
87
+ activeFilters,
88
+ onRemoveFilter: removeFilter,
89
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
90
+ filtersModal: {
91
+ open: filtersOpen,
92
+ onOpen: () => setFiltersOpen(true),
93
+ onClose: () => setFiltersOpen(false),
94
+ title: labels.filtersTitle ?? "Filtros",
95
+ count: filterCount,
96
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
97
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
98
+ providerChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByProvider ?? "Provedor", children: /* @__PURE__ */ jsx(
99
+ ChipPicker,
100
+ {
101
+ items: providerChipItems,
102
+ selectedIds: selectedProviders,
103
+ onChange: setSelectedProviders,
104
+ selectedLabel: labels.selected ?? "selecionados",
105
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
106
+ clearLabel: labels.clear ?? "Limpar"
107
+ }
108
+ ) }),
109
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByStatus ?? "Status", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "enabled", "disabled"].map((value) => enabledFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setEnabledFilter(value), children: statusLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setEnabledFilter(value), children: statusLabelFor(value) }, value)) }) })
110
+ ] })
111
+ }
44
112
  }
45
113
  ) : void 0
46
114
  }
@@ -65,10 +133,55 @@ function AgentsConfigPageView({ labels, agents, models, loading, onCreate, onUpd
65
133
  const [editing, setEditing] = useState(null);
66
134
  const [createOpen, setCreateOpen] = useState(false);
67
135
  const [searchTerm, setSearchTerm] = useState("");
136
+ const [filtersOpen, setFiltersOpen] = useState(false);
137
+ const [selectedModelIds, setSelectedModelIds] = useState([]);
138
+ const [hasActiveVersion, setHasActiveVersion] = useState("all");
139
+ const isEmpty = agents.length === 0;
68
140
  const modelOptions = models.map((model) => ({ value: model.id, label: `${model.name} (${model.provider})` }));
69
- const allAgents = agents;
70
- const filteredAgents = searchTerm.trim() ? allAgents.filter((agent) => String(agent.name ?? "").toLowerCase().includes(searchTerm.trim().toLowerCase())) : allAgents;
71
- const isEmpty = allAgents.length === 0;
141
+ const filteredAgents = useMemo(() => {
142
+ const term = searchTerm.trim().toLowerCase();
143
+ return agents.filter((agent) => {
144
+ const name = String(agent.name ?? "").toLowerCase();
145
+ if (term && !name.includes(term)) return false;
146
+ const modelId = String(agent.modelId ?? "");
147
+ if (selectedModelIds.length > 0 && !selectedModelIds.includes(modelId)) return false;
148
+ if (hasActiveVersion === "with" && !(agent.activePromptVersion > 0)) return false;
149
+ if (hasActiveVersion === "without" && agent.activePromptVersion > 0) return false;
150
+ return true;
151
+ });
152
+ }, [agents, searchTerm, selectedModelIds, hasActiveVersion]);
153
+ const modelChipItems = useMemo(
154
+ () => models.map((m) => ({
155
+ id: m.id,
156
+ name: m.name,
157
+ style: { bg: "bg-violet-500/15 text-violet-700 dark:text-violet-300", text: "" }
158
+ })),
159
+ [models]
160
+ );
161
+ const versionLabelFor = (value) => {
162
+ if (value === "with") return labels.versionWith ?? "Com vers\xE3o ativa";
163
+ if (value === "without") return labels.versionWithout ?? "Sem vers\xE3o ativa";
164
+ return labels.versionAll ?? "Todos";
165
+ };
166
+ const activeFilters = useMemo(() => {
167
+ const chips = [];
168
+ for (const value of selectedModelIds) {
169
+ chips.push({ type: "model", value, label: modelChipItems.find((i) => i.id === value)?.name ?? value });
170
+ }
171
+ if (hasActiveVersion !== "all") {
172
+ chips.push({ type: "activeVersion", value: hasActiveVersion, label: versionLabelFor(hasActiveVersion) });
173
+ }
174
+ return chips;
175
+ }, [selectedModelIds, hasActiveVersion, modelChipItems]);
176
+ const filterCount = activeFilters.length;
177
+ const removeFilter = (filter) => {
178
+ if (filter.type === "model") setSelectedModelIds((prev) => prev.filter((v) => v !== filter.value));
179
+ if (filter.type === "activeVersion") setHasActiveVersion("all");
180
+ };
181
+ const clearAllFilters = () => {
182
+ setSelectedModelIds([]);
183
+ setHasActiveVersion("all");
184
+ };
72
185
  const hero = /* @__PURE__ */ jsx(
73
186
  HeroSection,
74
187
  {
@@ -83,7 +196,32 @@ function AgentsConfigPageView({ labels, agents, models, loading, onCreate, onUpd
83
196
  searchTerm,
84
197
  onSearchChange: setSearchTerm,
85
198
  placeholder: labels.searchPlaceholder ?? labels.title,
86
- noBorder: true
199
+ noBorder: true,
200
+ activeFilters,
201
+ onRemoveFilter: removeFilter,
202
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
203
+ filtersModal: {
204
+ open: filtersOpen,
205
+ onOpen: () => setFiltersOpen(true),
206
+ onClose: () => setFiltersOpen(false),
207
+ title: labels.filtersTitle ?? "Filtros",
208
+ count: filterCount,
209
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
210
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
211
+ modelChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByModel ?? "Modelo", children: /* @__PURE__ */ jsx(
212
+ ChipPicker,
213
+ {
214
+ items: modelChipItems,
215
+ selectedIds: selectedModelIds,
216
+ onChange: setSelectedModelIds,
217
+ selectedLabel: labels.selected ?? "selecionados",
218
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
219
+ clearLabel: labels.clear ?? "Limpar"
220
+ }
221
+ ) }),
222
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByActiveVersion ?? "Vers\xE3o ativa", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "with", "without"].map((value) => hasActiveVersion === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setHasActiveVersion(value), children: versionLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setHasActiveVersion(value), children: versionLabelFor(value) }, value)) }) })
223
+ ] })
224
+ }
87
225
  }
88
226
  ) : void 0,
89
227
  actions: /* @__PURE__ */ jsx(
@@ -218,6 +356,11 @@ var LOCALE_OPTIONS = [
218
356
  function AgentsPromptsPageView({ labels, agents, prompts, loading, onCreate, onActivate, onDelete }) {
219
357
  const [createOpen, setCreateOpen] = useState(false);
220
358
  const [searchTerm, setSearchTerm] = useState("");
359
+ const [filtersOpen, setFiltersOpen] = useState(false);
360
+ const [selectedAgentIds, setSelectedAgentIds] = useState([]);
361
+ const [selectedLocales, setSelectedLocales] = useState([]);
362
+ const [activeFilter, setActiveFilter] = useState("all");
363
+ const isEmpty = prompts.length === 0;
221
364
  const agentOptions = useMemo(
222
365
  () => agents.map((agent) => ({
223
366
  value: String(agent.agentId ?? agent.id ?? ""),
@@ -233,14 +376,66 @@ function AgentsPromptsPageView({ labels, agents, prompts, loading, onCreate, onA
233
376
  }
234
377
  return map;
235
378
  }, [agents]);
236
- const allPrompts = prompts;
237
- const filteredPrompts = searchTerm.trim() ? allPrompts.filter((prompt) => {
379
+ const filteredPrompts = useMemo(() => {
238
380
  const term = searchTerm.trim().toLowerCase();
239
- const agentName = agentNameById.get(prompt.agentId) ?? prompt.agentId;
240
- const promptText = prompt.prompt ?? "";
241
- return agentName.toLowerCase().includes(term) || promptText.toLowerCase().includes(term);
242
- }) : allPrompts;
243
- const isEmpty = allPrompts.length === 0;
381
+ return prompts.filter((prompt) => {
382
+ if (term) {
383
+ const agentName = (agentNameById.get(prompt.agentId) ?? prompt.agentId).toLowerCase();
384
+ const promptText = (prompt.prompt ?? "").toLowerCase();
385
+ if (!agentName.includes(term) && !promptText.includes(term)) return false;
386
+ }
387
+ if (selectedAgentIds.length > 0 && !selectedAgentIds.includes(prompt.agentId)) return false;
388
+ if (selectedLocales.length > 0 && !selectedLocales.includes(prompt.locale)) return false;
389
+ if (activeFilter === "active" && !prompt.isActive) return false;
390
+ if (activeFilter === "inactive" && prompt.isActive) return false;
391
+ return true;
392
+ });
393
+ }, [prompts, searchTerm, selectedAgentIds, selectedLocales, activeFilter, agentNameById]);
394
+ const agentChipItems = useMemo(
395
+ () => agents.map((agent) => ({
396
+ id: String(agent.agentId ?? agent.id ?? ""),
397
+ name: String(agent.name ?? agent.agentId ?? agent.id ?? ""),
398
+ style: { bg: "bg-sky-500/15 text-sky-700 dark:text-sky-300", text: "" }
399
+ })).filter((item) => item.id.length > 0),
400
+ [agents]
401
+ );
402
+ const localeChipItems = useMemo(
403
+ () => LOCALE_OPTIONS.map((opt) => ({
404
+ id: opt.value,
405
+ name: opt.label,
406
+ style: { bg: "bg-indigo-500/15 text-indigo-700 dark:text-indigo-300", text: "" }
407
+ })),
408
+ []
409
+ );
410
+ const activeLabelFor = (value) => {
411
+ if (value === "active") return labels.activeYes ?? labels.isActive;
412
+ if (value === "inactive") return labels.activeNo ?? "Inativo";
413
+ return labels.activeAll ?? "Todos";
414
+ };
415
+ const activeFilters = useMemo(() => {
416
+ const chips = [];
417
+ for (const value of selectedAgentIds) {
418
+ chips.push({ type: "agent", value, label: agentChipItems.find((i) => i.id === value)?.name ?? value });
419
+ }
420
+ for (const value of selectedLocales) {
421
+ chips.push({ type: "locale", value, label: localeChipItems.find((i) => i.id === value)?.name ?? value });
422
+ }
423
+ if (activeFilter !== "all") {
424
+ chips.push({ type: "active", value: activeFilter, label: activeLabelFor(activeFilter) });
425
+ }
426
+ return chips;
427
+ }, [selectedAgentIds, selectedLocales, activeFilter, agentChipItems, localeChipItems]);
428
+ const filterCount = activeFilters.length;
429
+ const removeFilter = (filter) => {
430
+ if (filter.type === "agent") setSelectedAgentIds((prev) => prev.filter((v) => v !== filter.value));
431
+ if (filter.type === "locale") setSelectedLocales((prev) => prev.filter((v) => v !== filter.value));
432
+ if (filter.type === "active") setActiveFilter("all");
433
+ };
434
+ const clearAllFilters = () => {
435
+ setSelectedAgentIds([]);
436
+ setSelectedLocales([]);
437
+ setActiveFilter("all");
438
+ };
244
439
  const hero = /* @__PURE__ */ jsx(
245
440
  HeroSection,
246
441
  {
@@ -249,7 +444,52 @@ function AgentsPromptsPageView({ labels, agents, prompts, loading, onCreate, onA
249
444
  title: labels.title,
250
445
  subtitle: labels.subtitle,
251
446
  gradient: "from-sky-500 to-blue-700",
252
- toolbar: /* @__PURE__ */ jsx(
447
+ toolbar: !isEmpty ? /* @__PURE__ */ jsx(
448
+ SearchBar,
449
+ {
450
+ searchTerm,
451
+ onSearchChange: setSearchTerm,
452
+ placeholder: labels.searchPlaceholder ?? labels.title,
453
+ noBorder: true,
454
+ activeFilters,
455
+ onRemoveFilter: removeFilter,
456
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
457
+ filtersModal: {
458
+ open: filtersOpen,
459
+ onOpen: () => setFiltersOpen(true),
460
+ onClose: () => setFiltersOpen(false),
461
+ title: labels.filtersTitle ?? "Filtros",
462
+ count: filterCount,
463
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
464
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
465
+ agentChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByAgent ?? "Agente", children: /* @__PURE__ */ jsx(
466
+ ChipPicker,
467
+ {
468
+ items: agentChipItems,
469
+ selectedIds: selectedAgentIds,
470
+ onChange: setSelectedAgentIds,
471
+ selectedLabel: labels.selected ?? "selecionados",
472
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
473
+ clearLabel: labels.clear ?? "Limpar"
474
+ }
475
+ ) }),
476
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByLocale ?? "Idioma", children: /* @__PURE__ */ jsx(
477
+ ChipPicker,
478
+ {
479
+ items: localeChipItems,
480
+ selectedIds: selectedLocales,
481
+ onChange: setSelectedLocales,
482
+ selectedLabel: labels.selected ?? "selecionados",
483
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
484
+ clearLabel: labels.clear ?? "Limpar"
485
+ }
486
+ ) }),
487
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByActive ?? "Status", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "active", "inactive"].map((value) => activeFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setActiveFilter(value), children: activeLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setActiveFilter(value), children: activeLabelFor(value) }, value)) }) })
488
+ ] })
489
+ }
490
+ }
491
+ ) : void 0,
492
+ actions: /* @__PURE__ */ jsx(
253
493
  CreateActionButton,
254
494
  {
255
495
  mode: "desktop",
@@ -269,47 +509,37 @@ function AgentsPromptsPageView({ labels, agents, prompts, loading, onCreate, onA
269
509
  accent: "sky"
270
510
  }
271
511
  );
272
- const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
273
- /* @__PURE__ */ jsx(
274
- SearchBar,
512
+ const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredPrompts.map((prompt) => {
513
+ const agentName = agentNameById.get(prompt.agentId) ?? prompt.agentId;
514
+ const key = `${prompt.agentId}:${prompt.locale}:${prompt.version}`;
515
+ return /* @__PURE__ */ jsx(
516
+ EntityCard,
275
517
  {
276
- searchTerm,
277
- onSearchChange: setSearchTerm,
278
- placeholder: labels.searchPlaceholder ?? labels.title
279
- }
280
- ),
281
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredPrompts.map((prompt) => {
282
- const agentName = agentNameById.get(prompt.agentId) ?? prompt.agentId;
283
- const key = `${prompt.agentId}:${prompt.locale}:${prompt.version}`;
284
- return /* @__PURE__ */ jsx(
285
- EntityCard,
286
- {
287
- accentGradient: "from-sky-500 to-blue-700",
288
- icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-sky-500/10 text-sky-600 dark:bg-sky-500/20 dark:text-sky-400", children: /* @__PURE__ */ jsx(ChatBubbleLeftEllipsisIcon, { className: "h-6 w-6" }) }),
289
- title: agentName,
290
- subtitle: `${prompt.locale} \xB7 v${prompt.version}`,
291
- status: prompt.isActive ? /* @__PURE__ */ jsx(Badge, { color: "emerald", children: labels.isActive }) : null,
292
- footer: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
293
- !prompt.isActive && /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => onActivate(prompt), children: labels.activate }),
294
- /* @__PURE__ */ jsx(
295
- Button,
296
- {
297
- type: "button",
298
- size: "sm",
299
- color: "rose",
300
- onClick: () => {
301
- if (window.confirm(labels.deleteConfirm)) onDelete(prompt);
302
- },
303
- children: labels.delete
304
- }
305
- )
306
- ] }),
307
- children: prompt.prompt && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-3 text-xs text-slate-500 dark:text-slate-400", children: prompt.prompt })
308
- },
309
- key
310
- );
311
- }) })
312
- ] });
518
+ accentGradient: "from-sky-500 to-blue-700",
519
+ icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-sky-500/10 text-sky-600 dark:bg-sky-500/20 dark:text-sky-400", children: /* @__PURE__ */ jsx(ChatBubbleLeftEllipsisIcon, { className: "h-6 w-6" }) }),
520
+ title: agentName,
521
+ subtitle: `${prompt.locale} \xB7 v${prompt.version}`,
522
+ status: prompt.isActive ? /* @__PURE__ */ jsx(Badge, { color: "emerald", children: labels.isActive }) : null,
523
+ footer: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
524
+ !prompt.isActive && /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => onActivate(prompt), children: labels.activate }),
525
+ /* @__PURE__ */ jsx(
526
+ Button,
527
+ {
528
+ type: "button",
529
+ size: "sm",
530
+ color: "rose",
531
+ onClick: () => {
532
+ if (window.confirm(labels.deleteConfirm)) onDelete(prompt);
533
+ },
534
+ children: labels.delete
535
+ }
536
+ )
537
+ ] }),
538
+ children: prompt.prompt && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-3 text-xs text-slate-500 dark:text-slate-400", children: prompt.prompt })
539
+ },
540
+ key
541
+ );
542
+ }) });
313
543
  return /* @__PURE__ */ jsxs(Fragment, { children: [
314
544
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
315
545
  hero,
@@ -387,9 +617,47 @@ function AgentsToolDefinitionsPageView({ labels, tools, loading, onCreate, onUpd
387
617
  const [editing, setEditing] = useState(null);
388
618
  const [createOpen, setCreateOpen] = useState(false);
389
619
  const [searchTerm, setSearchTerm] = useState("");
620
+ const [filtersOpen, setFiltersOpen] = useState(false);
621
+ const [selectedToolTypes, setSelectedToolTypes] = useState([]);
390
622
  const allTools = tools;
391
- const filteredTools = searchTerm.trim() ? allTools.filter((tool) => tool.name.toLowerCase().includes(searchTerm.trim().toLowerCase())) : allTools;
392
623
  const isEmpty = allTools.length === 0;
624
+ const toolTypeOf = (tool) => {
625
+ const raw = tool;
626
+ const t = raw.toolType ?? raw.tool_type ?? tool.category;
627
+ return typeof t === "string" ? t : "";
628
+ };
629
+ const filteredTools = useMemo(() => {
630
+ const term = searchTerm.trim().toLowerCase();
631
+ return allTools.filter((tool) => {
632
+ if (term && !tool.name.toLowerCase().includes(term)) return false;
633
+ if (selectedToolTypes.length > 0) {
634
+ const t = toolTypeOf(tool);
635
+ if (!selectedToolTypes.includes(t)) return false;
636
+ }
637
+ return true;
638
+ });
639
+ }, [allTools, searchTerm, selectedToolTypes]);
640
+ const toolTypeChipItems = useMemo(
641
+ () => TOOL_TYPE_OPTIONS.map((opt) => ({
642
+ id: opt.value,
643
+ name: opt.label,
644
+ style: { bg: "bg-amber-500/15 text-amber-700 dark:text-amber-300", text: "" }
645
+ })),
646
+ []
647
+ );
648
+ const activeFilters = useMemo(() => {
649
+ return selectedToolTypes.map((value) => {
650
+ const label = toolTypeChipItems.find((i) => i.id === value)?.name ?? value;
651
+ return { type: "toolType", value, label };
652
+ });
653
+ }, [selectedToolTypes, toolTypeChipItems]);
654
+ const filterCount = activeFilters.length;
655
+ const removeFilter = (filter) => {
656
+ if (filter.type === "toolType") setSelectedToolTypes((prev) => prev.filter((t) => t !== filter.value));
657
+ };
658
+ const clearAllFilters = () => {
659
+ setSelectedToolTypes([]);
660
+ };
393
661
  const hero = /* @__PURE__ */ jsx(
394
662
  HeroSection,
395
663
  {
@@ -398,7 +666,38 @@ function AgentsToolDefinitionsPageView({ labels, tools, loading, onCreate, onUpd
398
666
  title: labels.title,
399
667
  subtitle: labels.subtitle,
400
668
  gradient: "from-amber-500 to-orange-700",
401
- toolbar: /* @__PURE__ */ jsx(
669
+ toolbar: !isEmpty ? /* @__PURE__ */ jsx(
670
+ SearchBar,
671
+ {
672
+ searchTerm,
673
+ onSearchChange: setSearchTerm,
674
+ placeholder: labels.searchPlaceholder ?? labels.title,
675
+ noBorder: true,
676
+ activeFilters,
677
+ onRemoveFilter: removeFilter,
678
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
679
+ filtersModal: {
680
+ open: filtersOpen,
681
+ onOpen: () => setFiltersOpen(true),
682
+ onClose: () => setFiltersOpen(false),
683
+ title: labels.filtersTitle ?? "Filtros",
684
+ count: filterCount,
685
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
686
+ children: /* @__PURE__ */ jsx("div", { className: "space-y-5", children: /* @__PURE__ */ jsx(FormSection, { title: labels.filterByToolType ?? "Tipo de ferramenta", children: /* @__PURE__ */ jsx(
687
+ ChipPicker,
688
+ {
689
+ items: toolTypeChipItems,
690
+ selectedIds: selectedToolTypes,
691
+ onChange: setSelectedToolTypes,
692
+ selectedLabel: labels.selected ?? "selecionados",
693
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
694
+ clearLabel: labels.clear ?? "Limpar"
695
+ }
696
+ ) }) })
697
+ }
698
+ }
699
+ ) : void 0,
700
+ actions: /* @__PURE__ */ jsx(
402
701
  CreateActionButton,
403
702
  {
404
703
  mode: "desktop",
@@ -418,43 +717,33 @@ function AgentsToolDefinitionsPageView({ labels, tools, loading, onCreate, onUpd
418
717
  accent: "amber"
419
718
  }
420
719
  );
421
- const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
422
- /* @__PURE__ */ jsx(
423
- SearchBar,
424
- {
425
- searchTerm,
426
- onSearchChange: setSearchTerm,
427
- placeholder: labels.searchPlaceholder ?? labels.title
428
- }
429
- ),
430
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredTools.map((tool) => /* @__PURE__ */ jsx(
431
- EntityCard,
432
- {
433
- accentGradient: "from-amber-500 to-orange-700",
434
- icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-amber-500/10 text-amber-600 dark:bg-amber-500/20 dark:text-amber-400", children: /* @__PURE__ */ jsx(WrenchScrewdriverIcon, { className: "h-6 w-6" }) }),
435
- title: tool.name,
436
- subtitle: toolTypeLabel(tool),
437
- status: /* @__PURE__ */ jsx(Badge, { color: tool.enabled ? "emerald" : "zinc", children: tool.enabled ? labels.enabled : labels.disabled }),
438
- footer: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
439
- /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setEditing(tool), children: labels.edit }),
440
- /* @__PURE__ */ jsx(
441
- Button,
442
- {
443
- type: "button",
444
- size: "sm",
445
- color: "rose",
446
- onClick: () => {
447
- if (window.confirm(labels.deleteConfirm)) onDelete(tool);
448
- },
449
- children: labels.delete
450
- }
451
- )
452
- ] }),
453
- children: tool.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: tool.description })
454
- },
455
- tool.agentToolId
456
- )) })
457
- ] });
720
+ const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredTools.map((tool) => /* @__PURE__ */ jsx(
721
+ EntityCard,
722
+ {
723
+ accentGradient: "from-amber-500 to-orange-700",
724
+ icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-amber-500/10 text-amber-600 dark:bg-amber-500/20 dark:text-amber-400", children: /* @__PURE__ */ jsx(WrenchScrewdriverIcon, { className: "h-6 w-6" }) }),
725
+ title: tool.name,
726
+ subtitle: toolTypeLabel(tool),
727
+ status: /* @__PURE__ */ jsx(Badge, { color: tool.enabled ? "emerald" : "zinc", children: tool.enabled ? labels.enabled : labels.disabled }),
728
+ footer: /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
729
+ /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setEditing(tool), children: labels.edit }),
730
+ /* @__PURE__ */ jsx(
731
+ Button,
732
+ {
733
+ type: "button",
734
+ size: "sm",
735
+ color: "rose",
736
+ onClick: () => {
737
+ if (window.confirm(labels.deleteConfirm)) onDelete(tool);
738
+ },
739
+ children: labels.delete
740
+ }
741
+ )
742
+ ] }),
743
+ children: tool.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: tool.description })
744
+ },
745
+ tool.agentToolId
746
+ )) });
458
747
  return /* @__PURE__ */ jsxs(Fragment, { children: [
459
748
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
460
749
  hero,
@@ -943,17 +1232,55 @@ function setFromJson(set, key, raw) {
943
1232
  set(key, raw);
944
1233
  }
945
1234
  }
946
- function RulesPageView({ labels, rules, loading, onCreate, onUpdate, onDelete, onAddClick }) {
1235
+ function RulesPageView({ labels, rules, loading, onCreate, onUpdate, onDelete }) {
947
1236
  const [createOpen, setCreateOpen] = useState(false);
948
- const openCreate = () => {
949
- if (onAddClick) onAddClick();
950
- else setCreateOpen(true);
951
- };
952
1237
  const [editing, setEditing] = useState(null);
953
1238
  const [searchTerm, setSearchTerm] = useState("");
954
- const allRules = rules;
955
- const filteredRules = searchTerm.trim() ? allRules.filter((rule) => (rule.name ?? "").toLowerCase().includes(searchTerm.trim().toLowerCase())) : allRules;
956
- const isEmpty = allRules.length === 0;
1239
+ const [filtersOpen, setFiltersOpen] = useState(false);
1240
+ const [enabledFilter, setEnabledFilter] = useState("all");
1241
+ const [expiryFilter, setExpiryFilter] = useState("all");
1242
+ const isEmpty = rules.length === 0;
1243
+ const filteredRules = useMemo(() => {
1244
+ const term = searchTerm.trim().toLowerCase();
1245
+ return rules.filter((rule) => {
1246
+ if (term && !rule.name.toLowerCase().includes(term)) return false;
1247
+ if (enabledFilter === "enabled" && !rule.enabled) return false;
1248
+ if (enabledFilter === "disabled" && rule.enabled) return false;
1249
+ const hasExpiry = typeof rule.validUntil === "string" && rule.validUntil.length > 0;
1250
+ if (expiryFilter === "expiring" && !hasExpiry) return false;
1251
+ if (expiryFilter === "noExpiry" && hasExpiry) return false;
1252
+ return true;
1253
+ });
1254
+ }, [rules, searchTerm, enabledFilter, expiryFilter]);
1255
+ const statusLabelFor = (value) => {
1256
+ if (value === "enabled") return labels.statusEnabled ?? labels.enabled;
1257
+ if (value === "disabled") return labels.statusDisabled ?? labels.disabled;
1258
+ return labels.statusAll ?? "Todos";
1259
+ };
1260
+ const expiryLabelFor = (value) => {
1261
+ if (value === "expiring") return labels.expiryExpiring ?? "Com validade";
1262
+ if (value === "noExpiry") return labels.expiryNoExpiry ?? "Sem validade";
1263
+ return labels.expiryAll ?? "Todos";
1264
+ };
1265
+ const activeFilters = useMemo(() => {
1266
+ const chips = [];
1267
+ if (enabledFilter !== "all") {
1268
+ chips.push({ type: "enabled", value: enabledFilter, label: statusLabelFor(enabledFilter) });
1269
+ }
1270
+ if (expiryFilter !== "all") {
1271
+ chips.push({ type: "expiry", value: expiryFilter, label: expiryLabelFor(expiryFilter) });
1272
+ }
1273
+ return chips;
1274
+ }, [enabledFilter, expiryFilter]);
1275
+ const filterCount = activeFilters.length;
1276
+ const removeFilter = (filter) => {
1277
+ if (filter.type === "enabled") setEnabledFilter("all");
1278
+ if (filter.type === "expiry") setExpiryFilter("all");
1279
+ };
1280
+ const clearAllFilters = () => {
1281
+ setEnabledFilter("all");
1282
+ setExpiryFilter("all");
1283
+ };
957
1284
  const hero = /* @__PURE__ */ jsx(
958
1285
  HeroSection,
959
1286
  {
@@ -968,7 +1295,22 @@ function RulesPageView({ labels, rules, loading, onCreate, onUpdate, onDelete, o
968
1295
  searchTerm,
969
1296
  onSearchChange: setSearchTerm,
970
1297
  placeholder: labels.searchPlaceholder ?? labels.title,
971
- noBorder: true
1298
+ noBorder: true,
1299
+ activeFilters,
1300
+ onRemoveFilter: removeFilter,
1301
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
1302
+ filtersModal: {
1303
+ open: filtersOpen,
1304
+ onOpen: () => setFiltersOpen(true),
1305
+ onClose: () => setFiltersOpen(false),
1306
+ title: labels.filtersTitle ?? "Filtros",
1307
+ count: filterCount,
1308
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
1309
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
1310
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByStatus ?? "Status", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "enabled", "disabled"].map((value) => enabledFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setEnabledFilter(value), children: statusLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setEnabledFilter(value), children: statusLabelFor(value) }, value)) }) }),
1311
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByExpiry ?? "Validade", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "expiring", "noExpiry"].map((value) => expiryFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setExpiryFilter(value), children: expiryLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setExpiryFilter(value), children: expiryLabelFor(value) }, value)) }) })
1312
+ ] })
1313
+ }
972
1314
  }
973
1315
  ) : void 0,
974
1316
  actions: /* @__PURE__ */ jsx(
@@ -976,7 +1318,7 @@ function RulesPageView({ labels, rules, loading, onCreate, onUpdate, onDelete, o
976
1318
  {
977
1319
  mode: "desktop",
978
1320
  label: labels.addRule,
979
- onClick: openCreate,
1321
+ onClick: () => setCreateOpen(true),
980
1322
  accent: "fuchsia"
981
1323
  }
982
1324
  )
@@ -987,7 +1329,7 @@ function RulesPageView({ labels, rules, loading, onCreate, onUpdate, onDelete, o
987
1329
  {
988
1330
  mode: "mobile",
989
1331
  label: labels.addRule,
990
- onClick: openCreate,
1332
+ onClick: () => setCreateOpen(true),
991
1333
  accent: "fuchsia"
992
1334
  }
993
1335
  );
@@ -1093,10 +1435,7 @@ function ruleToFormValue(rule) {
1093
1435
  status: typeof rule.status === "string" ? rule.status : base.status,
1094
1436
  validFrom: typeof rule.validFrom === "string" ? rule.validFrom : base.validFrom,
1095
1437
  validUntil: typeof rule.validUntil === "string" ? rule.validUntil : base.validUntil,
1096
- tags: Array.isArray(rule.tags) ? rule.tags.filter((tag) => typeof tag === "string") : base.tags,
1097
- // Round-trip recurrence (Fallback 4) — backend persists it as the
1098
- // structured `{ kind, ... }` blob the wizard sends.
1099
- recurrence: rule.recurrence ?? null
1438
+ tags: Array.isArray(rule.tags) ? rule.tags.filter((tag) => typeof tag === "string") : base.tags
1100
1439
  };
1101
1440
  }
1102
1441
  function pickAction(rule) {
@@ -1256,11 +1595,7 @@ var DIALECT_LOGO = {
1256
1595
  cockroachdb: "/logos/datasources/cockroachdb.svg",
1257
1596
  supabase: "/logos/datasources/supabase.svg",
1258
1597
  firebase: "/logos/datasources/firebase.svg",
1259
- neo4j: "/logos/datasources/neo4j.svg",
1260
- // AWS managed services (icepanel.io / official AWS Architecture Icons)
1261
- neptune: "/logos/datasources/neptune.svg",
1262
- timestream: "/logos/datasources/timestream.svg",
1263
- opensearch: "/logos/datasources/opensearch.svg"
1598
+ neo4j: "/logos/datasources/neo4j.svg"
1264
1599
  };
1265
1600
  function getDialectLogoSrc(dialect) {
1266
1601
  if (!dialect) return null;
@@ -1270,21 +1605,76 @@ function DatasourcesPageView({
1270
1605
  labels,
1271
1606
  datasources: externalDatasources,
1272
1607
  useSampleData = false,
1273
- onCreate,
1274
- onAddClick
1608
+ onCreate
1275
1609
  }) {
1276
1610
  const [modalOpen, setModalOpen] = useState(false);
1277
- const openCreate = () => {
1278
- if (onAddClick) onAddClick();
1279
- else setModalOpen(true);
1280
- };
1281
- const [searchTerm, setSearchTerm] = useState("");
1282
1611
  const [localDatasources, setLocalDatasources] = useState(
1283
1612
  useSampleData ? SAMPLE_DATASOURCES : []
1284
1613
  );
1285
- const allDatasources = externalDatasources ?? localDatasources;
1286
- const datasources = searchTerm.trim() ? allDatasources.filter((ds) => (ds.name ?? "").toLowerCase().includes(searchTerm.trim().toLowerCase())) : allDatasources;
1287
- const isEmpty = allDatasources.length === 0;
1614
+ const [searchTerm, setSearchTerm] = useState("");
1615
+ const [filtersOpen, setFiltersOpen] = useState(false);
1616
+ const [selectedDialects, setSelectedDialects] = useState([]);
1617
+ const [enabledFilter, setEnabledFilter] = useState("all");
1618
+ const [readOnlyFilter, setReadOnlyFilter] = useState("all");
1619
+ const datasources = externalDatasources ?? localDatasources;
1620
+ const isEmpty = datasources.length === 0;
1621
+ const filteredDatasources = useMemo(() => {
1622
+ const term = searchTerm.trim().toLowerCase();
1623
+ return datasources.filter((ds) => {
1624
+ const name = (ds.name ?? "").toLowerCase();
1625
+ if (term && !name.includes(term)) return false;
1626
+ if (selectedDialects.length > 0 && (!ds.dialect || !selectedDialects.includes(ds.dialect))) return false;
1627
+ if (enabledFilter === "enabled" && !ds.enabled) return false;
1628
+ if (enabledFilter === "disabled" && ds.enabled) return false;
1629
+ if (readOnlyFilter === "readOnly" && !ds.readOnly) return false;
1630
+ if (readOnlyFilter === "writable" && ds.readOnly) return false;
1631
+ return true;
1632
+ });
1633
+ }, [datasources, searchTerm, selectedDialects, enabledFilter, readOnlyFilter]);
1634
+ const dialectChipItems = useMemo(
1635
+ () => DIALECT_CATEGORIES.flatMap(
1636
+ (cat) => cat.dialects.map((d) => ({
1637
+ id: d.value,
1638
+ name: d.label,
1639
+ style: { bg: `bg-gradient-to-br ${cat.gradient} text-white`, text: "" }
1640
+ }))
1641
+ ),
1642
+ []
1643
+ );
1644
+ const enabledLabelFor = (value) => {
1645
+ if (value === "enabled") return labels.statusEnabled ?? labels.enabled ?? "Ativos";
1646
+ if (value === "disabled") return labels.statusDisabled ?? labels.disabled ?? "Inativos";
1647
+ return labels.statusAll ?? "Todos";
1648
+ };
1649
+ const readOnlyLabelFor = (value) => {
1650
+ if (value === "readOnly") return labels.readOnlyOnly ?? "Somente leitura";
1651
+ if (value === "writable") return labels.writable ?? "Grava\xE7\xE3o";
1652
+ return labels.readOnlyAll ?? "Todos";
1653
+ };
1654
+ const activeFilters = useMemo(() => {
1655
+ const chips = [];
1656
+ for (const value of selectedDialects) {
1657
+ chips.push({ type: "dialect", value, label: dialectChipItems.find((i) => i.id === value)?.name ?? value });
1658
+ }
1659
+ if (enabledFilter !== "all") {
1660
+ chips.push({ type: "enabled", value: enabledFilter, label: enabledLabelFor(enabledFilter) });
1661
+ }
1662
+ if (readOnlyFilter !== "all") {
1663
+ chips.push({ type: "readOnly", value: readOnlyFilter, label: readOnlyLabelFor(readOnlyFilter) });
1664
+ }
1665
+ return chips;
1666
+ }, [selectedDialects, enabledFilter, readOnlyFilter, dialectChipItems]);
1667
+ const filterCount = activeFilters.length;
1668
+ const removeFilter = (filter) => {
1669
+ if (filter.type === "dialect") setSelectedDialects((prev) => prev.filter((v) => v !== filter.value));
1670
+ if (filter.type === "enabled") setEnabledFilter("all");
1671
+ if (filter.type === "readOnly") setReadOnlyFilter("all");
1672
+ };
1673
+ const clearAllFilters = () => {
1674
+ setSelectedDialects([]);
1675
+ setEnabledFilter("all");
1676
+ setReadOnlyFilter("all");
1677
+ };
1288
1678
  function handleCreate(data) {
1289
1679
  if (onCreate) {
1290
1680
  onCreate(data);
@@ -1324,7 +1714,33 @@ function DatasourcesPageView({
1324
1714
  searchTerm,
1325
1715
  onSearchChange: setSearchTerm,
1326
1716
  placeholder: labels.searchPlaceholder ?? labels.title,
1327
- noBorder: true
1717
+ noBorder: true,
1718
+ activeFilters,
1719
+ onRemoveFilter: removeFilter,
1720
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
1721
+ filtersModal: {
1722
+ open: filtersOpen,
1723
+ onOpen: () => setFiltersOpen(true),
1724
+ onClose: () => setFiltersOpen(false),
1725
+ title: labels.filtersTitle ?? "Filtros",
1726
+ count: filterCount,
1727
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
1728
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
1729
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByDialect ?? labels.dialect ?? "Dialeto", children: /* @__PURE__ */ jsx(
1730
+ ChipPicker,
1731
+ {
1732
+ items: dialectChipItems,
1733
+ selectedIds: selectedDialects,
1734
+ onChange: setSelectedDialects,
1735
+ selectedLabel: labels.selected ?? "selecionados",
1736
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
1737
+ clearLabel: labels.clear ?? "Limpar"
1738
+ }
1739
+ ) }),
1740
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByStatus ?? "Status", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "enabled", "disabled"].map((value) => enabledFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setEnabledFilter(value), children: enabledLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setEnabledFilter(value), children: enabledLabelFor(value) }, value)) }) }),
1741
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByReadOnly ?? "Modo de acesso", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "readOnly", "writable"].map((value) => readOnlyFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setReadOnlyFilter(value), children: readOnlyLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setReadOnlyFilter(value), children: readOnlyLabelFor(value) }, value)) }) })
1742
+ ] })
1743
+ }
1328
1744
  }
1329
1745
  ) : void 0,
1330
1746
  actions: /* @__PURE__ */ jsx(
@@ -1332,7 +1748,7 @@ function DatasourcesPageView({
1332
1748
  {
1333
1749
  mode: "desktop",
1334
1750
  label: labels.addDatasource,
1335
- onClick: openCreate,
1751
+ onClick: () => setModalOpen(true),
1336
1752
  accent: "amber"
1337
1753
  }
1338
1754
  )
@@ -1343,7 +1759,7 @@ function DatasourcesPageView({
1343
1759
  {
1344
1760
  mode: "mobile",
1345
1761
  label: labels.addDatasource,
1346
- onClick: openCreate,
1762
+ onClick: () => setModalOpen(true),
1347
1763
  accent: "amber"
1348
1764
  }
1349
1765
  ),
@@ -1355,12 +1771,12 @@ function DatasourcesPageView({
1355
1771
  iconName: "link",
1356
1772
  customIcon: /* @__PURE__ */ jsx(CircleStackIcon, { className: "h-10 w-10 text-amber-500 dark:text-amber-400" })
1357
1773
  }
1358
- ) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: datasources.map((ds) => {
1774
+ ) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredDatasources.map((ds) => {
1359
1775
  const Icon = getDialectIcon(ds.dialect);
1360
1776
  const gradient = getDialectGradient(ds.dialect);
1361
1777
  const dialectLabel = getDialectLabel(ds.dialect);
1362
1778
  const logoSrc = getDialectLogoSrc(ds.dialect);
1363
- const iconElement = logoSrc ? /* @__PURE__ */ jsx("div", { className: "liquid-surface flex h-11 w-11 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx("img", { src: logoSrc, alt: dialectLabel, className: "h-7 w-7 object-contain" }) }) : /* @__PURE__ */ jsx("div", { className: `flex h-11 w-11 items-center justify-center rounded-lg bg-gradient-to-br ${gradient} shadow-lg`, children: /* @__PURE__ */ jsx(Icon, { className: "h-6 w-6 text-white" }) });
1779
+ const iconElement = logoSrc ? /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-white shadow-sm ring-1 ring-black/5 dark:bg-white/10 dark:ring-white/10", children: /* @__PURE__ */ jsx("img", { src: logoSrc, alt: dialectLabel, className: "h-7 w-7 object-contain" }) }) : /* @__PURE__ */ jsx("div", { className: `flex h-11 w-11 items-center justify-center rounded-lg bg-gradient-to-br ${gradient} shadow-lg`, children: /* @__PURE__ */ jsx(Icon, { className: "h-6 w-6 text-white" }) });
1364
1780
  return /* @__PURE__ */ jsx(
1365
1781
  EntityCard,
1366
1782
  {
@@ -1369,20 +1785,20 @@ function DatasourcesPageView({
1369
1785
  title: ds.name ?? ds.id,
1370
1786
  subtitle: dialectLabel,
1371
1787
  status: /* @__PURE__ */ jsx(Badge, { color: ds.enabled ? "emerald" : "zinc", children: ds.enabled ? labels.enabled : labels.disabled }),
1372
- footer: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-slate-500 dark:text-slate-400", children: [
1373
- ds.readOnly && /* @__PURE__ */ jsx(Badge, { color: "blue", size: "xs", children: "Read-only" }),
1788
+ footer: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-400 dark:text-gray-500", children: [
1789
+ ds.readOnly && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-semibold text-blue-700 dark:bg-blue-900/30 dark:text-blue-300", children: "Read-only" }),
1374
1790
  ds.timeoutMs && /* @__PURE__ */ jsxs("span", { children: [
1375
1791
  ds.timeoutMs / 1e3,
1376
1792
  "s timeout"
1377
1793
  ] })
1378
1794
  ] }),
1379
- children: /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-slate-500 dark:text-slate-400", children: [
1795
+ children: /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-gray-500 dark:text-gray-400", children: [
1380
1796
  /* @__PURE__ */ jsxs("div", { children: [
1381
1797
  labels.dialect,
1382
1798
  ": ",
1383
1799
  dialectLabel
1384
1800
  ] }),
1385
- ds.slug && /* @__PURE__ */ jsxs("div", { className: "truncate text-slate-400 dark:text-slate-500", children: [
1801
+ ds.slug && /* @__PURE__ */ jsxs("div", { className: "truncate text-gray-400", children: [
1386
1802
  "/",
1387
1803
  ds.slug
1388
1804
  ] })
@@ -1746,14 +2162,72 @@ function ConnectionsPageView({
1746
2162
  onUpdate,
1747
2163
  onDelete,
1748
2164
  onCreateSecret,
1749
- secretNamePrefix = "platform"
2165
+ secretNamePrefix = "platform",
2166
+ onAddClick
1750
2167
  }) {
2168
+ const openCreate = () => {
2169
+ if (onAddClick) onAddClick();
2170
+ else setCreateOpen(true);
2171
+ };
1751
2172
  const [createOpen, setCreateOpen] = useState(false);
1752
2173
  const [editing, setEditing] = useState(null);
1753
2174
  const [searchTerm, setSearchTerm] = useState("");
2175
+ const [filtersOpen, setFiltersOpen] = useState(false);
2176
+ const [selectedProviders, setSelectedProviders] = useState([]);
2177
+ const [selectedRegions, setSelectedRegions] = useState([]);
1754
2178
  const allConnections = connections;
1755
- const filteredConnections = searchTerm.trim() ? allConnections.filter((conn) => conn.name.toLowerCase().includes(searchTerm.trim().toLowerCase())) : allConnections;
1756
2179
  const isEmpty = allConnections.length === 0;
2180
+ const filteredConnections = useMemo(() => {
2181
+ const term = searchTerm.trim().toLowerCase();
2182
+ return allConnections.filter((conn) => {
2183
+ if (term && !conn.name.toLowerCase().includes(term)) return false;
2184
+ if (selectedProviders.length > 0) {
2185
+ const provider = conn.providerSlug ?? "";
2186
+ if (!selectedProviders.includes(provider)) return false;
2187
+ }
2188
+ if (selectedRegions.length > 0) {
2189
+ const region = conn.region ?? "";
2190
+ if (!selectedRegions.includes(region)) return false;
2191
+ }
2192
+ return true;
2193
+ });
2194
+ }, [allConnections, searchTerm, selectedProviders, selectedRegions]);
2195
+ const providerChipItems = useMemo(
2196
+ () => [...new Set(allConnections.map((c) => c.providerSlug ?? "").filter((p) => p.length > 0))].sort().map((slug) => ({
2197
+ id: slug,
2198
+ name: providerLabel(slug),
2199
+ style: { bg: "bg-sky-500/15 text-sky-700 dark:text-sky-300", text: "" }
2200
+ })),
2201
+ [allConnections]
2202
+ );
2203
+ const regionChipItems = useMemo(
2204
+ () => [...new Set(allConnections.map((c) => c.region ?? "").filter((r) => r.length > 0))].sort().map((region) => ({
2205
+ id: region,
2206
+ name: region,
2207
+ style: { bg: "bg-indigo-500/15 text-indigo-700 dark:text-indigo-300", text: "" }
2208
+ })),
2209
+ [allConnections]
2210
+ );
2211
+ const activeFilters = useMemo(() => {
2212
+ const chips = [];
2213
+ for (const value of selectedProviders) {
2214
+ const label = providerChipItems.find((i) => i.id === value)?.name ?? providerLabel(value);
2215
+ chips.push({ type: "provider", value, label });
2216
+ }
2217
+ for (const value of selectedRegions) {
2218
+ chips.push({ type: "region", value, label: value });
2219
+ }
2220
+ return chips;
2221
+ }, [selectedProviders, selectedRegions, providerChipItems]);
2222
+ const filterCount = activeFilters.length;
2223
+ const removeFilter = (filter) => {
2224
+ if (filter.type === "provider") setSelectedProviders((prev) => prev.filter((p) => p !== filter.value));
2225
+ if (filter.type === "region") setSelectedRegions((prev) => prev.filter((r) => r !== filter.value));
2226
+ };
2227
+ const clearAllFilters = () => {
2228
+ setSelectedProviders([]);
2229
+ setSelectedRegions([]);
2230
+ };
1757
2231
  const hero = /* @__PURE__ */ jsx(
1758
2232
  HeroSection,
1759
2233
  {
@@ -1768,7 +2242,42 @@ function ConnectionsPageView({
1768
2242
  searchTerm,
1769
2243
  onSearchChange: setSearchTerm,
1770
2244
  placeholder: labels.searchPlaceholder ?? labels.title,
1771
- noBorder: true
2245
+ noBorder: true,
2246
+ activeFilters,
2247
+ onRemoveFilter: removeFilter,
2248
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
2249
+ filtersModal: {
2250
+ open: filtersOpen,
2251
+ onOpen: () => setFiltersOpen(true),
2252
+ onClose: () => setFiltersOpen(false),
2253
+ title: labels.filtersTitle ?? "Filtros",
2254
+ count: filterCount,
2255
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
2256
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
2257
+ providerChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByProvider ?? "Provedor", children: /* @__PURE__ */ jsx(
2258
+ ChipPicker,
2259
+ {
2260
+ items: providerChipItems,
2261
+ selectedIds: selectedProviders,
2262
+ onChange: setSelectedProviders,
2263
+ selectedLabel: labels.selected ?? "selecionados",
2264
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
2265
+ clearLabel: labels.clear ?? "Limpar"
2266
+ }
2267
+ ) }),
2268
+ regionChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByRegion ?? "Regi\xE3o", children: /* @__PURE__ */ jsx(
2269
+ ChipPicker,
2270
+ {
2271
+ items: regionChipItems,
2272
+ selectedIds: selectedRegions,
2273
+ onChange: setSelectedRegions,
2274
+ selectedLabel: labels.selected ?? "selecionados",
2275
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
2276
+ clearLabel: labels.clear ?? "Limpar"
2277
+ }
2278
+ ) })
2279
+ ] })
2280
+ }
1772
2281
  }
1773
2282
  ) : void 0,
1774
2283
  actions: /* @__PURE__ */ jsx(
@@ -1776,7 +2285,7 @@ function ConnectionsPageView({
1776
2285
  {
1777
2286
  mode: "desktop",
1778
2287
  label: labels.add,
1779
- onClick: () => setCreateOpen(true),
2288
+ onClick: openCreate,
1780
2289
  accent: "sky"
1781
2290
  }
1782
2291
  )
@@ -2082,15 +2591,66 @@ function CredentialsPageView({ labels, credentials, loading, onCreate, onRotate,
2082
2591
  const [createOpen, setCreateOpen] = useState(false);
2083
2592
  const [rotateFor, setRotateFor] = useState(null);
2084
2593
  const [searchTerm, setSearchTerm] = useState("");
2085
- const allCredentials = credentials;
2086
- const filteredCredentials = searchTerm.trim() ? allCredentials.filter((secret) => secret.name.toLowerCase().includes(searchTerm.trim().toLowerCase())) : allCredentials;
2087
- const isEmpty = allCredentials.length === 0;
2594
+ const [filtersOpen, setFiltersOpen] = useState(false);
2595
+ const [selectedTypes, setSelectedTypes] = useState([]);
2596
+ const [disabledFilter, setDisabledFilter] = useState("all");
2597
+ const isEmpty = credentials.length === 0;
2088
2598
  const typeOptions = [
2089
2599
  { value: "generic", label: labels.typeGeneric },
2090
2600
  { value: "api_key", label: labels.typeApiKey },
2091
2601
  { value: "oauth", label: labels.typeOAuth },
2092
2602
  { value: "password", label: labels.typePassword }
2093
2603
  ];
2604
+ const typeLabelMap = {
2605
+ generic: labels.typeGeneric,
2606
+ api_key: labels.typeApiKey,
2607
+ oauth: labels.typeOAuth,
2608
+ password: labels.typePassword
2609
+ };
2610
+ const filteredCredentials = useMemo(() => {
2611
+ const term = searchTerm.trim().toLowerCase();
2612
+ return credentials.filter((secret) => {
2613
+ if (term && !secret.name.toLowerCase().includes(term)) return false;
2614
+ if (selectedTypes.length > 0 && !selectedTypes.includes(secret.secretType)) return false;
2615
+ if (disabledFilter === "active" && secret.disabled) return false;
2616
+ if (disabledFilter === "disabled" && !secret.disabled) return false;
2617
+ return true;
2618
+ });
2619
+ }, [credentials, searchTerm, selectedTypes, disabledFilter]);
2620
+ const typeChipItems = useMemo(
2621
+ () => [...new Set(credentials.map((c) => c.secretType).filter((t) => t.length > 0))].sort().map((type) => ({
2622
+ id: type,
2623
+ name: typeLabelMap[type] ?? type,
2624
+ style: { bg: "bg-rose-500/15 text-rose-700 dark:text-rose-300", text: "" }
2625
+ })),
2626
+ // typeLabelMap is derived from `labels` props — re-derive when those change
2627
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2628
+ [credentials, labels.typeGeneric, labels.typeApiKey, labels.typeOAuth, labels.typePassword]
2629
+ );
2630
+ const statusLabelFor = (value) => {
2631
+ if (value === "active") return labels.statusActive ?? "Ativo";
2632
+ if (value === "disabled") return labels.statusDisabled ?? labels.disable;
2633
+ return labels.statusAll ?? "Todos";
2634
+ };
2635
+ const activeFilters = useMemo(() => {
2636
+ const chips = [];
2637
+ for (const value of selectedTypes) {
2638
+ chips.push({ type: "secretType", value, label: typeChipItems.find((i) => i.id === value)?.name ?? value });
2639
+ }
2640
+ if (disabledFilter !== "all") {
2641
+ chips.push({ type: "disabled", value: disabledFilter, label: statusLabelFor(disabledFilter) });
2642
+ }
2643
+ return chips;
2644
+ }, [selectedTypes, disabledFilter, typeChipItems]);
2645
+ const filterCount = activeFilters.length;
2646
+ const removeFilter = (filter) => {
2647
+ if (filter.type === "secretType") setSelectedTypes((prev) => prev.filter((v) => v !== filter.value));
2648
+ if (filter.type === "disabled") setDisabledFilter("all");
2649
+ };
2650
+ const clearAllFilters = () => {
2651
+ setSelectedTypes([]);
2652
+ setDisabledFilter("all");
2653
+ };
2094
2654
  const hero = /* @__PURE__ */ jsx(
2095
2655
  HeroSection,
2096
2656
  {
@@ -2099,7 +2659,41 @@ function CredentialsPageView({ labels, credentials, loading, onCreate, onRotate,
2099
2659
  title: labels.title,
2100
2660
  subtitle: labels.subtitle,
2101
2661
  gradient: "from-rose-500 to-orange-600",
2102
- toolbar: /* @__PURE__ */ jsx(
2662
+ toolbar: !isEmpty ? /* @__PURE__ */ jsx(
2663
+ SearchBar,
2664
+ {
2665
+ searchTerm,
2666
+ onSearchChange: setSearchTerm,
2667
+ placeholder: labels.searchPlaceholder ?? labels.title,
2668
+ noBorder: true,
2669
+ activeFilters,
2670
+ onRemoveFilter: removeFilter,
2671
+ onClearAll: filterCount > 0 ? clearAllFilters : void 0,
2672
+ filtersModal: {
2673
+ open: filtersOpen,
2674
+ onOpen: () => setFiltersOpen(true),
2675
+ onClose: () => setFiltersOpen(false),
2676
+ title: labels.filtersTitle ?? "Filtros",
2677
+ count: filterCount,
2678
+ onClear: filterCount > 0 ? clearAllFilters : void 0,
2679
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
2680
+ typeChipItems.length > 0 && /* @__PURE__ */ jsx(FormSection, { title: labels.filterByType ?? "Tipo", children: /* @__PURE__ */ jsx(
2681
+ ChipPicker,
2682
+ {
2683
+ items: typeChipItems,
2684
+ selectedIds: selectedTypes,
2685
+ onChange: setSelectedTypes,
2686
+ selectedLabel: labels.selected ?? "selecionados",
2687
+ selectAllLabel: labels.selectAll ?? "Selecionar todos",
2688
+ clearLabel: labels.clear ?? "Limpar"
2689
+ }
2690
+ ) }),
2691
+ /* @__PURE__ */ jsx(FormSection, { title: labels.filterByStatus ?? "Status", children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: ["all", "active", "disabled"].map((value) => disabledFilter === value ? /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", color: "ios-glass-blue", onClick: () => setDisabledFilter(value), children: statusLabelFor(value) }, value) : /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", outline: true, onClick: () => setDisabledFilter(value), children: statusLabelFor(value) }, value)) }) })
2692
+ ] })
2693
+ }
2694
+ }
2695
+ ) : void 0,
2696
+ actions: /* @__PURE__ */ jsx(
2103
2697
  CreateActionButton,
2104
2698
  {
2105
2699
  mode: "desktop",
@@ -2119,60 +2713,50 @@ function CredentialsPageView({ labels, credentials, loading, onCreate, onRotate,
2119
2713
  accent: "rose"
2120
2714
  }
2121
2715
  );
2122
- const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2123
- /* @__PURE__ */ jsx(
2124
- SearchBar,
2125
- {
2126
- searchTerm,
2127
- onSearchChange: setSearchTerm,
2128
- placeholder: labels.searchPlaceholder ?? labels.title
2129
- }
2130
- ),
2131
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredCredentials.map((secret) => /* @__PURE__ */ jsxs(
2132
- EntityCard,
2133
- {
2134
- accentGradient: "from-rose-500 to-orange-700",
2135
- icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-rose-500/10 text-rose-600 dark:bg-rose-500/20 dark:text-rose-400", children: /* @__PURE__ */ jsx(KeyIcon, { className: "h-6 w-6" }) }),
2136
- title: secret.name,
2137
- subtitle: secret.secretType,
2138
- status: /* @__PURE__ */ jsx(Badge, { color: secret.disabled ? "zinc" : "emerald", children: secret.disabled ? labels.disable : "Active" }),
2139
- footer: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2140
- /* @__PURE__ */ jsx(CopyableId, { id: secret.secretId }),
2141
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2142
- /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", outline: true, onClick: () => setRotateFor(secret), children: [
2143
- /* @__PURE__ */ jsx(ArrowPathIcon, { className: "mr-1 h-3.5 w-3.5" }),
2144
- labels.rotate
2145
- ] }),
2146
- !secret.disabled && /* @__PURE__ */ jsxs(
2147
- Button,
2148
- {
2149
- type: "button",
2150
- size: "sm",
2151
- color: "rose",
2152
- onClick: () => {
2153
- if (window.confirm(labels.disableConfirm)) onDisable(secret);
2154
- },
2155
- children: [
2156
- /* @__PURE__ */ jsx(TrashIcon, { className: "mr-1 h-3.5 w-3.5" }),
2157
- labels.disable
2158
- ]
2159
- }
2160
- )
2161
- ] })
2162
- ] }),
2163
- children: [
2164
- secret.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: secret.description }),
2165
- /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-1 text-xs text-slate-500 dark:text-slate-400", children: [
2166
- /* @__PURE__ */ jsx("dt", { children: labels.createdAt }),
2167
- /* @__PURE__ */ jsx("dd", { className: "text-right", children: new Date(secret.createdAt).toLocaleDateString() }),
2168
- /* @__PURE__ */ jsx("dt", { children: labels.expiresAt }),
2169
- /* @__PURE__ */ jsx("dd", { className: "text-right", children: secret.expiresAt ? new Date(secret.expiresAt).toLocaleDateString() : labels.neverExpires })
2170
- ] })
2171
- ]
2172
- },
2173
- secret.secretId
2174
- )) })
2175
- ] });
2716
+ const content = loading ? /* @__PURE__ */ jsx(PageLoadingState, {}) : isEmpty ? /* @__PURE__ */ jsx(PageEmptyState, { title: labels.empty, message: labels.subtitle, iconName: "folder-open" }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: filteredCredentials.map((secret) => /* @__PURE__ */ jsxs(
2717
+ EntityCard,
2718
+ {
2719
+ accentGradient: "from-rose-500 to-orange-700",
2720
+ icon: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-11 items-center justify-center rounded-lg bg-rose-500/10 text-rose-600 dark:bg-rose-500/20 dark:text-rose-400", children: /* @__PURE__ */ jsx(KeyIcon, { className: "h-6 w-6" }) }),
2721
+ title: secret.name,
2722
+ subtitle: secret.secretType,
2723
+ status: /* @__PURE__ */ jsx(Badge, { color: secret.disabled ? "zinc" : "emerald", children: secret.disabled ? labels.disable : "Active" }),
2724
+ footer: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2725
+ /* @__PURE__ */ jsx(CopyableId, { id: secret.secretId }),
2726
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2727
+ /* @__PURE__ */ jsxs(Button, { type: "button", size: "sm", outline: true, onClick: () => setRotateFor(secret), children: [
2728
+ /* @__PURE__ */ jsx(ArrowPathIcon, { className: "mr-1 h-3.5 w-3.5" }),
2729
+ labels.rotate
2730
+ ] }),
2731
+ !secret.disabled && /* @__PURE__ */ jsxs(
2732
+ Button,
2733
+ {
2734
+ type: "button",
2735
+ size: "sm",
2736
+ color: "rose",
2737
+ onClick: () => {
2738
+ if (window.confirm(labels.disableConfirm)) onDisable(secret);
2739
+ },
2740
+ children: [
2741
+ /* @__PURE__ */ jsx(TrashIcon, { className: "mr-1 h-3.5 w-3.5" }),
2742
+ labels.disable
2743
+ ]
2744
+ }
2745
+ )
2746
+ ] })
2747
+ ] }),
2748
+ children: [
2749
+ secret.description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500 dark:text-slate-400", children: secret.description }),
2750
+ /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-1 text-xs text-slate-500 dark:text-slate-400", children: [
2751
+ /* @__PURE__ */ jsx("dt", { children: labels.createdAt }),
2752
+ /* @__PURE__ */ jsx("dd", { className: "text-right", children: new Date(secret.createdAt).toLocaleDateString() }),
2753
+ /* @__PURE__ */ jsx("dt", { children: labels.expiresAt }),
2754
+ /* @__PURE__ */ jsx("dd", { className: "text-right", children: secret.expiresAt ? new Date(secret.expiresAt).toLocaleDateString() : labels.neverExpires })
2755
+ ] })
2756
+ ]
2757
+ },
2758
+ secret.secretId
2759
+ )) });
2176
2760
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2177
2761
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
2178
2762
  hero,