@greatapps/greatagents-ui 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/greatagents-ui",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Shared agents UI components for Great platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -119,6 +119,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
119
119
  <Label htmlFor="edit-title">Nome do Agente *</Label>
120
120
  <Input
121
121
  id="edit-title"
122
+ name="title"
122
123
  value={form.title}
123
124
  onChange={(e) => {
124
125
  setForm((prev) => ({
@@ -152,6 +153,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
152
153
  <Label htmlFor="edit-delay">Delay de Digitação (s)</Label>
153
154
  <Input
154
155
  id="edit-delay"
156
+ name="delay"
155
157
  type="number"
156
158
  value={form.delayTyping}
157
159
  onChange={(e) => updateField("delayTyping", e.target.value)}
@@ -168,6 +170,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
168
170
  <Label htmlFor="edit-waiting">Tempo de Espera (s)</Label>
169
171
  <Input
170
172
  id="edit-waiting"
173
+ name="waiting"
171
174
  type="number"
172
175
  value={form.waitingTime}
173
176
  onChange={(e) => updateField("waitingTime", e.target.value)}
@@ -193,7 +196,7 @@ export function AgentEditForm({ config, agent, idAccount, open, onOpenChange }:
193
196
  </Button>
194
197
  <Button type="submit" disabled={updateAgent.isPending}>
195
198
  {updateAgent.isPending && (
196
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
199
+ <Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
197
200
  )}
198
201
  Salvar
199
202
  </Button>
@@ -96,6 +96,7 @@ export function AgentFormDialog({
96
96
  <Label htmlFor="agent-photo">Foto (URL)</Label>
97
97
  <Input
98
98
  id="agent-photo"
99
+ name="photo"
99
100
  value={photo}
100
101
  onChange={(e) => setPhoto(e.target.value)}
101
102
  placeholder="https://exemplo.com/foto.jpg"
@@ -106,6 +107,7 @@ export function AgentFormDialog({
106
107
  <Label htmlFor="agent-title">Nome do Agente *</Label>
107
108
  <Input
108
109
  id="agent-title"
110
+ name="title"
109
111
  value={title}
110
112
  onChange={(e) => setTitle(e.target.value)}
111
113
  placeholder="Ex: Assistente de Agendamento"
@@ -118,6 +120,7 @@ export function AgentFormDialog({
118
120
  <Label htmlFor="agent-delay">Delay de Digitação (ms)</Label>
119
121
  <Input
120
122
  id="agent-delay"
123
+ name="delay"
121
124
  type="number"
122
125
  value={delayTyping}
123
126
  onChange={(e) => setDelayTyping(e.target.value)}
@@ -130,6 +133,7 @@ export function AgentFormDialog({
130
133
  <Label htmlFor="agent-waiting">Tempo de Espera (ms)</Label>
131
134
  <Input
132
135
  id="agent-waiting"
136
+ name="waiting"
133
137
  type="number"
134
138
  value={waitingTime}
135
139
  onChange={(e) => setWaitingTime(e.target.value)}
@@ -150,7 +154,7 @@ export function AgentFormDialog({
150
154
  </Button>
151
155
  <Button type="submit" disabled={isPending || !title.trim()}>
152
156
  {isPending ? (
153
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
157
+ <Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
154
158
  ) : null}
155
159
  {isEditing ? "Salvar" : "Criar"}
156
160
  </Button>
@@ -238,7 +238,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
238
238
  className="flex items-center gap-3 rounded-lg border bg-card p-3"
239
239
  >
240
240
  <SortableItemHandle className="shrink-0 text-muted-foreground hover:text-foreground">
241
- <GripVertical className="h-5 w-5" />
241
+ <GripVertical aria-hidden="true" className="h-5 w-5" />
242
242
  </SortableItemHandle>
243
243
 
244
244
  <div className="flex flex-1 flex-col gap-1 min-w-0">
@@ -260,6 +260,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
260
260
  </div>
261
261
 
262
262
  <Switch
263
+ aria-label="Ativar/Desativar"
263
264
  checked={objective.active}
264
265
  onCheckedChange={(checked) =>
265
266
  handleToggleActive(objective, checked)
@@ -270,6 +271,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
270
271
  <Button
271
272
  variant="ghost"
272
273
  size="icon"
274
+ aria-label="Editar"
273
275
  className="shrink-0 text-muted-foreground hover:text-foreground"
274
276
  onClick={() => openEdit(objective)}
275
277
  >
@@ -279,6 +281,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
279
281
  <Button
280
282
  variant="ghost"
281
283
  size="icon"
284
+ aria-label="Excluir"
282
285
  className="shrink-0 text-muted-foreground hover:text-destructive"
283
286
  onClick={() => setRemoveTarget(objective)}
284
287
  >
@@ -292,7 +295,7 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
292
295
  const obj = sortedObjectives.find((o) => o.id === value);
293
296
  return (
294
297
  <div className="flex items-center gap-3 rounded-lg border bg-card p-3 shadow-lg">
295
- <GripVertical className="h-5 w-5 text-muted-foreground" />
298
+ <GripVertical aria-hidden="true" className="h-5 w-5 text-muted-foreground" />
296
299
  <span className="font-medium">{obj?.title}</span>
297
300
  </div>
298
301
  );
@@ -311,8 +314,10 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
311
314
  </DialogHeader>
312
315
  <div className="space-y-4">
313
316
  <div className="space-y-2">
314
- <Label>Título *</Label>
317
+ <Label htmlFor="objective-title">Título *</Label>
315
318
  <Input
319
+ id="objective-title"
320
+ name="title"
316
321
  value={form.title}
317
322
  onChange={(e) => {
318
323
  const title = e.target.value;
@@ -327,8 +332,10 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
327
332
  </div>
328
333
 
329
334
  <div className="space-y-2">
330
- <Label>Slug (identificador) *</Label>
335
+ <Label htmlFor="objective-slug">Slug (identificador) *</Label>
331
336
  <Input
337
+ id="objective-slug"
338
+ name="slug"
332
339
  value={form.slug}
333
340
  onChange={(e) => {
334
341
  setSlugManual(true);
@@ -343,13 +350,15 @@ export function AgentObjectivesList({ agent, config }: AgentObjectivesListProps)
343
350
  </div>
344
351
 
345
352
  <div className="space-y-2">
346
- <Label>Instruções do Objetivo</Label>
353
+ <Label htmlFor="objective-prompt">Instruções do Objetivo</Label>
347
354
  <Textarea
355
+ id="objective-prompt"
356
+ name="prompt"
348
357
  value={form.prompt}
349
358
  onChange={(e) =>
350
359
  setForm((f) => ({ ...f, prompt: e.target.value }))
351
360
  }
352
- placeholder="Instruções detalhadas que o agente seguirá quando este objetivo for ativado. Ex: passos para agendar consulta, perguntas a fazer, validações necessárias..."
361
+ placeholder="Instru\u00e7\u00f5es detalhadas que o agente seguir\u00e1 quando este objetivo for ativado\u2026"
353
362
  rows={8}
354
363
  />
355
364
  <p className="text-xs text-muted-foreground">
@@ -214,24 +214,28 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
214
214
  <div className="space-y-2">
215
215
  <textarea
216
216
  ref={textareaRef}
217
+ aria-label="Prompt do sistema"
218
+ name="prompt"
217
219
  value={promptText}
218
220
  onChange={(e) => setPromptText(e.target.value)}
219
221
  onKeyDown={handleKeyDown}
220
- placeholder="Escreva o prompt do sistema aqui..."
222
+ placeholder="Escreva o prompt do sistema aqui\u2026"
221
223
  disabled={updateAgent.isPending}
222
224
  className="w-full resize-none rounded-lg border bg-background p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
223
225
  style={{ minHeight: "300px" }}
224
226
  />
225
227
  <div className="flex items-center gap-3 text-xs text-muted-foreground">
226
- <span>{charCount.toLocaleString("pt-BR")} caracteres</span>
228
+ <span className="tabular-nums">{charCount.toLocaleString("pt-BR")} caracteres</span>
227
229
  <span>·</span>
228
- <span>~{tokenEstimate.toLocaleString("pt-BR")} tokens</span>
230
+ <span className="tabular-nums">~{tokenEstimate.toLocaleString("pt-BR")} tokens</span>
229
231
  </div>
230
232
  </div>
231
233
 
232
234
  {/* Save row */}
233
235
  <div className="flex items-center gap-3">
234
236
  <Input
237
+ aria-label="Notas da alteração"
238
+ name="changeNotes"
235
239
  value={changeNotes}
236
240
  onChange={(e) => setChangeNotes(e.target.value)}
237
241
  placeholder="O que mudou? (opcional)"
@@ -390,7 +394,7 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
390
394
  onClick={() => setCompareVersionId(isComparing ? null : version.id)}
391
395
  className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground"
392
396
  >
393
- <FileText className="h-3 w-3" />
397
+ <FileText aria-hidden="true" className="h-3 w-3" />
394
398
  {isComparing ? "Ocultar diff" : "Comparar"}
395
399
  </button>
396
400
  <button
@@ -398,7 +402,7 @@ export function AgentPromptEditor({ config, agent }: AgentPromptEditorProps) {
398
402
  onClick={() => handleRestore(version)}
399
403
  className="flex items-center gap-1 text-xs text-primary hover:underline"
400
404
  >
401
- <RotateCcw className="h-3 w-3" />
405
+ <RotateCcw aria-hidden="true" className="h-3 w-3" />
402
406
  Restaurar
403
407
  </button>
404
408
  </div>
@@ -23,19 +23,19 @@ export function AgentTabs({ agent, config, renderChatLink }: AgentTabsProps) {
23
23
  <Tabs defaultValue="prompt">
24
24
  <TabsList>
25
25
  <TabsTrigger value="prompt" className="flex items-center gap-1.5">
26
- <FileText className="h-3.5 w-3.5" />
26
+ <FileText aria-hidden="true" className="h-3.5 w-3.5" />
27
27
  Prompt
28
28
  </TabsTrigger>
29
29
  <TabsTrigger value="objetivos" className="flex items-center gap-1.5">
30
- <Target className="h-3.5 w-3.5" />
30
+ <Target aria-hidden="true" className="h-3.5 w-3.5" />
31
31
  Objetivos
32
32
  </TabsTrigger>
33
33
  <TabsTrigger value="ferramentas" className="flex items-center gap-1.5">
34
- <Wrench className="h-3.5 w-3.5" />
34
+ <Wrench aria-hidden="true" className="h-3.5 w-3.5" />
35
35
  Ferramentas
36
36
  </TabsTrigger>
37
37
  <TabsTrigger value="conversas" className="flex items-center gap-1.5">
38
- <MessageCircle className="h-3.5 w-3.5" />
38
+ <MessageCircle aria-hidden="true" className="h-3.5 w-3.5" />
39
39
  Conversas
40
40
  </TabsTrigger>
41
41
  </TabsList>
@@ -182,7 +182,9 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
182
182
  <PopoverContent className="w-72 p-0" align="end">
183
183
  <div className="p-2">
184
184
  <Input
185
- placeholder="Buscar ferramenta..."
185
+ placeholder="Buscar ferramenta\u2026"
186
+ aria-label="Buscar ferramenta"
187
+ name="search"
186
188
  value={search}
187
189
  onChange={(e) => setSearch(e.target.value)}
188
190
  className="h-8"
@@ -250,6 +252,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
250
252
  </div>
251
253
 
252
254
  <Switch
255
+ aria-label="Ativar/Desativar"
253
256
  checked={agentTool.enabled}
254
257
  onCheckedChange={(checked) =>
255
258
  handleToggleEnabled(agentTool, checked)
@@ -260,6 +263,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
260
263
  <Button
261
264
  variant="ghost"
262
265
  size="icon"
266
+ aria-label="Configurar"
263
267
  className="shrink-0 text-muted-foreground hover:text-foreground"
264
268
  onClick={() => openConfig(agentTool)}
265
269
  title="Configurar instruções"
@@ -270,6 +274,7 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
270
274
  <Button
271
275
  variant="ghost"
272
276
  size="icon"
277
+ aria-label="Remover"
273
278
  className="shrink-0 text-muted-foreground hover:text-destructive"
274
279
  onClick={() => setRemoveTarget(agentTool)}
275
280
  >
@@ -295,12 +300,12 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
295
300
  <div className="space-y-4">
296
301
  {configTarget && getToolInfo(configTarget.id_tool)?.type !== "none" && (
297
302
  <div className="space-y-2">
298
- <Label>Credencial</Label>
303
+ <Label htmlFor="tool-credential">Credencial</Label>
299
304
  <Select
300
305
  value={configCredentialId || undefined}
301
306
  onValueChange={(val) => setConfigCredentialId(val === "__none__" ? "" : val)}
302
307
  >
303
- <SelectTrigger>
308
+ <SelectTrigger id="tool-credential">
304
309
  <SelectValue placeholder="Selecione uma credencial (opcional)" />
305
310
  </SelectTrigger>
306
311
  <SelectContent>
@@ -320,11 +325,13 @@ export function AgentToolsList({ agent, config }: AgentToolsListProps) {
320
325
  </div>
321
326
  )}
322
327
  <div className="space-y-2">
323
- <Label>Instruções Personalizadas</Label>
328
+ <Label htmlFor="tool-instructions">Instruções Personalizadas</Label>
324
329
  <Textarea
330
+ id="tool-instructions"
331
+ name="instructions"
325
332
  value={configInstructions}
326
333
  onChange={(e) => setConfigInstructions(e.target.value)}
327
- placeholder="Instruções sobre como e quando o agente deve usar esta ferramenta..."
334
+ placeholder="Instru\u00e7\u00f5es sobre como e quando o agente deve usar esta ferramenta\u2026"
328
335
  rows={6}
329
336
  />
330
337
  <p className="text-xs text-muted-foreground">
@@ -78,6 +78,7 @@ function useColumns(
78
78
  variant="ghost"
79
79
  size="icon"
80
80
  className="h-8 w-8"
81
+ aria-label="Editar"
81
82
  onClick={() => onEdit(row.original)}
82
83
  >
83
84
  <Pencil className="h-4 w-4" />
@@ -91,6 +92,7 @@ function useColumns(
91
92
  variant="ghost"
92
93
  size="icon"
93
94
  className="h-8 w-8 text-destructive hover:text-destructive"
95
+ aria-label="Excluir"
94
96
  onClick={() => onDelete(row.original.id)}
95
97
  >
96
98
  <Trash2 className="h-4 w-4" />
@@ -151,9 +153,12 @@ export function AgentsTable({ config, onNavigateToAgent }: { config: GagentsHook
151
153
  <>
152
154
  <div className="flex items-center gap-3">
153
155
  <div className="relative flex-1 max-w-md">
154
- <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
156
+ <Search aria-hidden="true" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
155
157
  <Input
156
- placeholder="Buscar agentes..."
158
+ placeholder="Buscar agentes\u2026"
159
+ aria-label="Buscar agentes"
160
+ name="search"
161
+ autoComplete="off"
157
162
  value={search}
158
163
  onChange={(e) => handleSearchChange(e.target.value)}
159
164
  className="pl-9"
@@ -89,11 +89,21 @@ export function AgentConversationsTable({
89
89
  <TableRow
90
90
  key={conversation.id}
91
91
  className="cursor-pointer"
92
+ role="button"
93
+ tabIndex={0}
92
94
  onClick={() =>
93
95
  setSelectedId(
94
96
  selectedId === conversation.id ? null : conversation.id,
95
97
  )
96
98
  }
99
+ onKeyDown={(e) => {
100
+ if (e.key === "Enter" || e.key === " ") {
101
+ e.preventDefault();
102
+ setSelectedId(
103
+ selectedId === conversation.id ? null : conversation.id,
104
+ );
105
+ }
106
+ }}
97
107
  data-state={selectedId === conversation.id ? "selected" : undefined}
98
108
  >
99
109
  <TableCell className="font-mono text-xs">
@@ -111,10 +121,10 @@ export function AgentConversationsTable({
111
121
  <span className="text-xs text-muted-foreground">—</span>
112
122
  )}
113
123
  </TableCell>
114
- <TableCell className="text-right">
124
+ <TableCell className="text-right tabular-nums">
115
125
  {conversation.message_count ?? "—"}
116
126
  </TableCell>
117
- <TableCell className="text-right">
127
+ <TableCell className="text-right tabular-nums">
118
128
  {conversation.usage_tokens ?? "—"}
119
129
  </TableCell>
120
130
  <TableCell>
@@ -133,6 +143,7 @@ export function AgentConversationsTable({
133
143
  <a
134
144
  href={`/gchat/inbox/${conversation.id_external}`}
135
145
  title="Ver no Chat"
146
+ aria-label="Ver no Chat"
136
147
  onClick={(e) => e.stopPropagation()}
137
148
  className="inline-flex items-center justify-center rounded-md p-1.5 text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
138
149
  >
@@ -45,7 +45,7 @@ export function ConversationView({
45
45
  <p className="text-sm text-muted-foreground">
46
46
  Conversa não encontrada.
47
47
  </p>
48
- <Button variant="ghost" size="icon" onClick={onClose}>
48
+ <Button variant="ghost" size="icon" aria-label="Fechar" onClick={onClose}>
49
49
  <X className="h-4 w-4" />
50
50
  </Button>
51
51
  </div>
@@ -58,7 +58,7 @@ export function ConversationView({
58
58
  <div className="flex items-center justify-between">
59
59
  <h3 className="font-semibold">Detalhes da conversa #{conversation.id}</h3>
60
60
  <div className="flex items-center gap-1">
61
- <Button variant="ghost" size="icon" onClick={onClose}>
61
+ <Button variant="ghost" size="icon" aria-label="Fechar" onClick={onClose}>
62
62
  <X className="h-4 w-4" />
63
63
  </Button>
64
64
  </div>
@@ -130,6 +130,7 @@ function useColumns(
130
130
  variant="ghost"
131
131
  size="icon"
132
132
  className="h-8 w-8"
133
+ aria-label="Vincular"
133
134
  disabled
134
135
  >
135
136
  <Link className="h-4 w-4" />
@@ -144,6 +145,7 @@ function useColumns(
144
145
  variant="ghost"
145
146
  size="icon"
146
147
  className="h-8 w-8"
148
+ aria-label="Editar"
147
149
  onClick={() => onEdit(row.original)}
148
150
  >
149
151
  <Pencil className="h-4 w-4" />
@@ -157,6 +159,7 @@ function useColumns(
157
159
  variant="ghost"
158
160
  size="icon"
159
161
  className="h-8 w-8 text-destructive hover:text-destructive"
162
+ aria-label="Excluir"
160
163
  onClick={() => onRemove(row.original)}
161
164
  >
162
165
  <Trash2 className="h-4 w-4" />
@@ -328,9 +331,12 @@ export function ToolCredentialsForm({
328
331
  <div className="space-y-4">
329
332
  <div className="flex items-center gap-3">
330
333
  <div className="relative flex-1 max-w-md">
331
- <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
334
+ <Search aria-hidden="true" className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
332
335
  <Input
333
- placeholder="Buscar credenciais..."
336
+ placeholder="Buscar credenciais\u2026"
337
+ aria-label="Buscar credenciais"
338
+ name="search"
339
+ autoComplete="off"
334
340
  value={search}
335
341
  onChange={(e) => setSearch(e.target.value)}
336
342
  className="pl-9"
@@ -353,7 +359,7 @@ export function ToolCredentialsForm({
353
359
  </DialogHeader>
354
360
  <div className="space-y-4">
355
361
  <div>
356
- <label className="mb-1 block text-sm font-medium">
362
+ <label htmlFor="cred-tool" className="mb-1 block text-sm font-medium">
357
363
  Ferramenta *
358
364
  </label>
359
365
  <Select
@@ -362,7 +368,7 @@ export function ToolCredentialsForm({
362
368
  setCreateForm((f) => ({ ...f, id_tool: val }))
363
369
  }
364
370
  >
365
- <SelectTrigger>
371
+ <SelectTrigger id="cred-tool">
366
372
  <SelectValue placeholder="Selecione a ferramenta" />
367
373
  </SelectTrigger>
368
374
  <SelectContent>
@@ -375,10 +381,12 @@ export function ToolCredentialsForm({
375
381
  </Select>
376
382
  </div>
377
383
  <div>
378
- <label className="mb-1 block text-sm font-medium">
384
+ <label htmlFor="cred-label" className="mb-1 block text-sm font-medium">
379
385
  Label *
380
386
  </label>
381
387
  <Input
388
+ id="cred-label"
389
+ name="label"
382
390
  value={createForm.label}
383
391
  onChange={(e) =>
384
392
  setCreateForm((f) => ({ ...f, label: e.target.value }))
@@ -387,10 +395,13 @@ export function ToolCredentialsForm({
387
395
  />
388
396
  </div>
389
397
  <div>
390
- <label className="mb-1 block text-sm font-medium">
398
+ <label htmlFor="cred-credential" className="mb-1 block text-sm font-medium">
391
399
  Credencial *
392
400
  </label>
393
401
  <Input
402
+ id="cred-credential"
403
+ name="credential"
404
+ autoComplete="off"
394
405
  type="password"
395
406
  value={createForm.credentials_encrypted}
396
407
  onChange={(e) =>
@@ -403,10 +414,12 @@ export function ToolCredentialsForm({
403
414
  />
404
415
  </div>
405
416
  <div>
406
- <label className="mb-1 block text-sm font-medium">
417
+ <label htmlFor="cred-expires" className="mb-1 block text-sm font-medium">
407
418
  Data de Expiração (opcional)
408
419
  </label>
409
420
  <Input
421
+ id="cred-expires"
422
+ name="expires"
410
423
  type="date"
411
424
  value={createForm.expires_at}
412
425
  onChange={(e) =>
@@ -448,7 +461,7 @@ export function ToolCredentialsForm({
448
461
  </DialogHeader>
449
462
  <div className="space-y-4">
450
463
  <div>
451
- <label className="mb-1 block text-sm font-medium">
464
+ <label htmlFor="edit-cred-tool" className="mb-1 block text-sm font-medium">
452
465
  Ferramenta *
453
466
  </label>
454
467
  <Select
@@ -457,7 +470,7 @@ export function ToolCredentialsForm({
457
470
  setEditForm((f) => ({ ...f, id_tool: val }))
458
471
  }
459
472
  >
460
- <SelectTrigger>
473
+ <SelectTrigger id="edit-cred-tool">
461
474
  <SelectValue placeholder="Selecione a ferramenta" />
462
475
  </SelectTrigger>
463
476
  <SelectContent>
@@ -470,10 +483,12 @@ export function ToolCredentialsForm({
470
483
  </Select>
471
484
  </div>
472
485
  <div>
473
- <label className="mb-1 block text-sm font-medium">
486
+ <label htmlFor="edit-cred-label" className="mb-1 block text-sm font-medium">
474
487
  Label
475
488
  </label>
476
489
  <Input
490
+ id="edit-cred-label"
491
+ name="label"
477
492
  value={editForm.label}
478
493
  onChange={(e) =>
479
494
  setEditForm((f) => ({ ...f, label: e.target.value }))
@@ -482,10 +497,13 @@ export function ToolCredentialsForm({
482
497
  />
483
498
  </div>
484
499
  <div>
485
- <label className="mb-1 block text-sm font-medium">
500
+ <label htmlFor="edit-cred-credential" className="mb-1 block text-sm font-medium">
486
501
  Nova Credencial (vazio = manter atual)
487
502
  </label>
488
503
  <Input
504
+ id="edit-cred-credential"
505
+ name="credential"
506
+ autoComplete="off"
489
507
  type="password"
490
508
  value={editForm.credentials_encrypted}
491
509
  onChange={(e) =>
@@ -498,10 +516,12 @@ export function ToolCredentialsForm({
498
516
  />
499
517
  </div>
500
518
  <div>
501
- <label className="mb-1 block text-sm font-medium">
519
+ <label htmlFor="edit-cred-expires" className="mb-1 block text-sm font-medium">
502
520
  Data de Expiração
503
521
  </label>
504
522
  <Input
523
+ id="edit-cred-expires"
524
+ name="expires"
505
525
  type="date"
506
526
  value={editForm.expires_at}
507
527
  onChange={(e) =>
@@ -510,7 +530,7 @@ export function ToolCredentialsForm({
510
530
  />
511
531
  </div>
512
532
  <div>
513
- <label className="mb-1 block text-sm font-medium">Status</label>
533
+ <label htmlFor="edit-cred-status" className="mb-1 block text-sm font-medium">Status</label>
514
534
  <Select
515
535
  value={editForm.status || undefined}
516
536
  onValueChange={(val) =>
@@ -520,7 +540,7 @@ export function ToolCredentialsForm({
520
540
  }))
521
541
  }
522
542
  >
523
- <SelectTrigger>
543
+ <SelectTrigger id="edit-cred-status">
524
544
  <SelectValue />
525
545
  </SelectTrigger>
526
546
  <SelectContent>
@@ -189,6 +189,7 @@ export function ToolFormDialog({
189
189
  <Label htmlFor="tool-name">Nome *</Label>
190
190
  <Input
191
191
  id="tool-name"
192
+ name="name"
192
193
  value={form.name}
193
194
  onChange={(e) => {
194
195
  const name = e.target.value;
@@ -213,6 +214,7 @@ export function ToolFormDialog({
213
214
  <Label htmlFor="tool-slug">Slug (identificador único) *</Label>
214
215
  <Input
215
216
  id="tool-slug"
217
+ name="slug"
216
218
  value={form.slug}
217
219
  onChange={(e) => {
218
220
  setSlugManuallyEdited(true);
@@ -269,11 +271,12 @@ export function ToolFormDialog({
269
271
  <Label htmlFor="tool-description">Descrição</Label>
270
272
  <Textarea
271
273
  id="tool-description"
274
+ name="description"
272
275
  value={form.description}
273
276
  onChange={(e) =>
274
277
  setForm((prev) => ({ ...prev, description: e.target.value }))
275
278
  }
276
- placeholder="Descrição da ferramenta..."
279
+ placeholder="Descri\u00e7\u00e3o da ferramenta\u2026"
277
280
  rows={3}
278
281
  disabled={isPending}
279
282
  />
@@ -285,6 +288,7 @@ export function ToolFormDialog({
285
288
  </Label>
286
289
  <Textarea
287
290
  id="tool-function-defs"
291
+ name="functionDefs"
288
292
  value={form.functionDefinitions}
289
293
  onChange={(e) => {
290
294
  setForm((prev) => ({
@@ -298,11 +302,11 @@ export function ToolFormDialog({
298
302
  "type": "function",
299
303
  "function": {
300
304
  "name": "nome_da_funcao",
301
- "description": "O que a função faz",
305
+ "description": "O que a fun\u00e7\u00e3o faz",
302
306
  "parameters": {
303
307
  "type": "object",
304
- "properties": { ... },
305
- "required": [...]
308
+ "properties": { \u2026 },
309
+ "required": [\u2026]
306
310
  }
307
311
  }
308
312
  }
@@ -330,7 +334,7 @@ export function ToolFormDialog({
330
334
  </Button>
331
335
  <Button type="submit" disabled={isPending}>
332
336
  {isPending ? (
333
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
337
+ <Loader2 aria-hidden="true" className="mr-2 h-4 w-4 animate-spin" />
334
338
  ) : null}
335
339
  {isEditing ? "Salvar" : "Criar"}
336
340
  </Button>