@hed-hog/ticket 0.0.285

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 (134) hide show
  1. package/README.md +480 -0
  2. package/dist/dto/assign-ticket-owner.dto.d.ts +4 -0
  3. package/dist/dto/assign-ticket-owner.dto.d.ts.map +1 -0
  4. package/dist/dto/assign-ticket-owner.dto.js +7 -0
  5. package/dist/dto/assign-ticket-owner.dto.js.map +1 -0
  6. package/dist/dto/create-ticket-channel-account.dto.d.ts +10 -0
  7. package/dist/dto/create-ticket-channel-account.dto.d.ts.map +1 -0
  8. package/dist/dto/create-ticket-channel-account.dto.js +7 -0
  9. package/dist/dto/create-ticket-channel-account.dto.js.map +1 -0
  10. package/dist/dto/create-ticket-channel.dto.d.ts +7 -0
  11. package/dist/dto/create-ticket-channel.dto.d.ts.map +1 -0
  12. package/dist/dto/create-ticket-channel.dto.js +7 -0
  13. package/dist/dto/create-ticket-channel.dto.js.map +1 -0
  14. package/dist/dto/create-ticket-internal-note.dto.d.ts +4 -0
  15. package/dist/dto/create-ticket-internal-note.dto.d.ts.map +1 -0
  16. package/dist/dto/create-ticket-internal-note.dto.js +7 -0
  17. package/dist/dto/create-ticket-internal-note.dto.js.map +1 -0
  18. package/dist/dto/create-ticket-reply.dto.d.ts +5 -0
  19. package/dist/dto/create-ticket-reply.dto.d.ts.map +1 -0
  20. package/dist/dto/create-ticket-reply.dto.js +7 -0
  21. package/dist/dto/create-ticket-reply.dto.js.map +1 -0
  22. package/dist/dto/update-ticket-channel-account.dto.d.ts +8 -0
  23. package/dist/dto/update-ticket-channel-account.dto.d.ts.map +1 -0
  24. package/dist/dto/update-ticket-channel-account.dto.js +7 -0
  25. package/dist/dto/update-ticket-channel-account.dto.js.map +1 -0
  26. package/dist/dto/update-ticket-channel.dto.d.ts +5 -0
  27. package/dist/dto/update-ticket-channel.dto.d.ts.map +1 -0
  28. package/dist/dto/update-ticket-channel.dto.js +7 -0
  29. package/dist/dto/update-ticket-channel.dto.js.map +1 -0
  30. package/dist/dto/update-ticket-priority.dto.d.ts +4 -0
  31. package/dist/dto/update-ticket-priority.dto.d.ts.map +1 -0
  32. package/dist/dto/update-ticket-priority.dto.js +7 -0
  33. package/dist/dto/update-ticket-priority.dto.js.map +1 -0
  34. package/dist/dto/update-ticket-settings.dto.d.ts +13 -0
  35. package/dist/dto/update-ticket-settings.dto.d.ts.map +1 -0
  36. package/dist/dto/update-ticket-settings.dto.js +7 -0
  37. package/dist/dto/update-ticket-settings.dto.js.map +1 -0
  38. package/dist/dto/update-ticket-status.dto.d.ts +4 -0
  39. package/dist/dto/update-ticket-status.dto.d.ts.map +1 -0
  40. package/dist/dto/update-ticket-status.dto.js +7 -0
  41. package/dist/dto/update-ticket-status.dto.js.map +1 -0
  42. package/dist/index.d.ts +19 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +35 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/ticket-ai-review.controller.d.ts +9 -0
  47. package/dist/ticket-ai-review.controller.d.ts.map +1 -0
  48. package/dist/ticket-ai-review.controller.js +36 -0
  49. package/dist/ticket-ai-review.controller.js.map +1 -0
  50. package/dist/ticket-channel-accounts.controller.d.ts +24 -0
  51. package/dist/ticket-channel-accounts.controller.d.ts.map +1 -0
  52. package/dist/ticket-channel-accounts.controller.js +72 -0
  53. package/dist/ticket-channel-accounts.controller.js.map +1 -0
  54. package/dist/ticket-channels.controller.d.ts +21 -0
  55. package/dist/ticket-channels.controller.d.ts.map +1 -0
  56. package/dist/ticket-channels.controller.js +72 -0
  57. package/dist/ticket-channels.controller.js.map +1 -0
  58. package/dist/ticket-data.controller.d.ts +24 -0
  59. package/dist/ticket-data.controller.d.ts.map +1 -0
  60. package/dist/ticket-data.controller.js +81 -0
  61. package/dist/ticket-data.controller.js.map +1 -0
  62. package/dist/ticket-items.controller.d.ts +35 -0
  63. package/dist/ticket-items.controller.d.ts.map +1 -0
  64. package/dist/ticket-items.controller.js +100 -0
  65. package/dist/ticket-items.controller.js.map +1 -0
  66. package/dist/ticket-settings.controller.d.ts +11 -0
  67. package/dist/ticket-settings.controller.d.ts.map +1 -0
  68. package/dist/ticket-settings.controller.js +50 -0
  69. package/dist/ticket-settings.controller.js.map +1 -0
  70. package/dist/ticket.module.d.ts +3 -0
  71. package/dist/ticket.module.d.ts.map +1 -0
  72. package/dist/ticket.module.js +45 -0
  73. package/dist/ticket.module.js.map +1 -0
  74. package/dist/ticket.service.d.ts +88 -0
  75. package/dist/ticket.service.d.ts.map +1 -0
  76. package/dist/ticket.service.js +83 -0
  77. package/dist/ticket.service.js.map +1 -0
  78. package/hedhog/data/menu.yaml +164 -0
  79. package/hedhog/data/role.yaml +23 -0
  80. package/hedhog/data/route.yaml +225 -0
  81. package/hedhog/data/setting_group.yaml +574 -0
  82. package/hedhog/frontend/app/[id]/page.tsx.ejs +360 -0
  83. package/hedhog/frontend/app/_components/ai-draft-editor-dialog.tsx.ejs +67 -0
  84. package/hedhog/frontend/app/_components/ticket-badges.tsx.ejs +65 -0
  85. package/hedhog/frontend/app/_components/ticket-context-card.tsx.ejs +80 -0
  86. package/hedhog/frontend/app/_components/ticket-timeline.tsx.ejs +59 -0
  87. package/hedhog/frontend/app/_lib/mock-data.ts.ejs +474 -0
  88. package/hedhog/frontend/app/_lib/types.ts.ejs +134 -0
  89. package/hedhog/frontend/app/ai-review/page.tsx.ejs +205 -0
  90. package/hedhog/frontend/app/channel-accounts/page.tsx.ejs +476 -0
  91. package/hedhog/frontend/app/channels/page.tsx.ejs +406 -0
  92. package/hedhog/frontend/app/dashboard/page.tsx.ejs +178 -0
  93. package/hedhog/frontend/app/inbox/page.tsx.ejs +500 -0
  94. package/hedhog/frontend/app/page.tsx.ejs +5 -0
  95. package/hedhog/frontend/app/reports/page.tsx.ejs +151 -0
  96. package/hedhog/frontend/app/roles/page.tsx.ejs +110 -0
  97. package/hedhog/frontend/app/settings/page.tsx.ejs +484 -0
  98. package/hedhog/frontend/messages/en.json +412 -0
  99. package/hedhog/frontend/messages/pt.json +412 -0
  100. package/hedhog/table/channel.yaml +23 -0
  101. package/hedhog/table/channel_account.yaml +34 -0
  102. package/hedhog/table/ticket.yaml +73 -0
  103. package/hedhog/table/ticket_ai_draft.yaml +51 -0
  104. package/hedhog/table/ticket_attachment.yaml +40 -0
  105. package/hedhog/table/ticket_category.yaml +22 -0
  106. package/hedhog/table/ticket_context.yaml +64 -0
  107. package/hedhog/table/ticket_message.yaml +49 -0
  108. package/hedhog/table/ticket_message_source.yaml +50 -0
  109. package/hedhog/table/ticket_metadata.yaml +21 -0
  110. package/hedhog/table/ticket_participant.yaml +25 -0
  111. package/hedhog/table/ticket_source.yaml +43 -0
  112. package/hedhog/table/ticket_tag.yaml +22 -0
  113. package/package.json +40 -0
  114. package/src/dto/assign-ticket-owner.dto.ts +3 -0
  115. package/src/dto/create-ticket-channel-account.dto.ts +9 -0
  116. package/src/dto/create-ticket-channel.dto.ts +6 -0
  117. package/src/dto/create-ticket-internal-note.dto.ts +3 -0
  118. package/src/dto/create-ticket-reply.dto.ts +4 -0
  119. package/src/dto/update-ticket-channel-account.dto.ts +7 -0
  120. package/src/dto/update-ticket-channel.dto.ts +4 -0
  121. package/src/dto/update-ticket-priority.dto.ts +3 -0
  122. package/src/dto/update-ticket-settings.dto.ts +12 -0
  123. package/src/dto/update-ticket-status.dto.ts +3 -0
  124. package/src/index.ts +19 -0
  125. package/src/language/en.json +8 -0
  126. package/src/language/pt.json +8 -0
  127. package/src/ticket-ai-review.controller.ts +14 -0
  128. package/src/ticket-channel-accounts.controller.ts +34 -0
  129. package/src/ticket-channels.controller.ts +31 -0
  130. package/src/ticket-data.controller.ts +39 -0
  131. package/src/ticket-items.controller.ts +56 -0
  132. package/src/ticket-settings.controller.ts +20 -0
  133. package/src/ticket.module.ts +32 -0
  134. package/src/ticket.service.ts +102 -0
@@ -0,0 +1,110 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@/components/ui/table';
14
+ import { useTranslations } from 'next-intl';
15
+ import { ticketRolePermissions } from '../_lib/mock-data';
16
+
17
+ function Mark({ value }: { value: boolean }) {
18
+ const t = useTranslations('ticket.TicketModule');
19
+
20
+ return (
21
+ <Badge variant={value ? 'default' : 'secondary'}>
22
+ {value ? t('common.yes') : t('common.no')}
23
+ </Badge>
24
+ );
25
+ }
26
+
27
+ export default function TicketRolesPage() {
28
+ const t = useTranslations('ticket.TicketModule');
29
+
30
+ return (
31
+ <Page>
32
+ <PageHeader
33
+ title={t('roles.title')}
34
+ description={t('roles.description')}
35
+ breadcrumbs={[
36
+ { label: t('common.home'), href: '/' },
37
+ { label: t('common.tickets'), href: '/ticket' },
38
+ { label: t('roles.breadcrumb') },
39
+ ]}
40
+ />
41
+
42
+ <div className="grid gap-4 lg:grid-cols-3">
43
+ <Card>
44
+ <CardHeader>
45
+ <CardTitle className="text-base">{t('roles.cards.adminTitle')}</CardTitle>
46
+ </CardHeader>
47
+ <CardContent className="text-sm text-muted-foreground">
48
+ {t('roles.cards.adminDesc')}
49
+ </CardContent>
50
+ </Card>
51
+
52
+ <Card>
53
+ <CardHeader>
54
+ <CardTitle className="text-base">
55
+ {t('roles.cards.operatorTitle')}
56
+ </CardTitle>
57
+ </CardHeader>
58
+ <CardContent className="text-sm text-muted-foreground">
59
+ {t('roles.cards.operatorDesc')}
60
+ </CardContent>
61
+ </Card>
62
+
63
+ <Card>
64
+ <CardHeader>
65
+ <CardTitle className="text-base">
66
+ {t('roles.cards.analystTitle')}
67
+ </CardTitle>
68
+ </CardHeader>
69
+ <CardContent className="text-sm text-muted-foreground">
70
+ {t('roles.cards.analystDesc')}
71
+ </CardContent>
72
+ </Card>
73
+ </div>
74
+
75
+ <Card>
76
+ <CardHeader>
77
+ <CardTitle className="text-base">{t('roles.tableTitle')}</CardTitle>
78
+ </CardHeader>
79
+ <CardContent>
80
+ <Table>
81
+ <TableHeader>
82
+ <TableRow>
83
+ <TableHead>{t('roles.columns.permission')}</TableHead>
84
+ <TableHead>{t('roles.columns.admin')}</TableHead>
85
+ <TableHead>{t('roles.columns.operator')}</TableHead>
86
+ <TableHead>{t('roles.columns.analyst')}</TableHead>
87
+ </TableRow>
88
+ </TableHeader>
89
+ <TableBody>
90
+ {ticketRolePermissions.map((item) => (
91
+ <TableRow key={item.permission}>
92
+ <TableCell>{item.permission}</TableCell>
93
+ <TableCell>
94
+ <Mark value={item.ticketAdmin} />
95
+ </TableCell>
96
+ <TableCell>
97
+ <Mark value={item.ticketOperator} />
98
+ </TableCell>
99
+ <TableCell>
100
+ <Mark value={item.ticketAnalyst} />
101
+ </TableCell>
102
+ </TableRow>
103
+ ))}
104
+ </TableBody>
105
+ </Table>
106
+ </CardContent>
107
+ </Card>
108
+ </Page>
109
+ );
110
+ }
@@ -0,0 +1,484 @@
1
+ 'use client';
2
+
3
+ import { Page, PageHeader } from '@/components/entity-list';
4
+ import { Badge } from '@/components/ui/badge';
5
+ import { Button } from '@/components/ui/button';
6
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
7
+ import {
8
+ Form,
9
+ FormControl,
10
+ FormField,
11
+ FormItem,
12
+ FormLabel,
13
+ FormMessage,
14
+ } from '@/components/ui/form';
15
+ import { Input } from '@/components/ui/input';
16
+ import {
17
+ Select,
18
+ SelectContent,
19
+ SelectItem,
20
+ SelectTrigger,
21
+ SelectValue,
22
+ } from '@/components/ui/select';
23
+ import { Switch } from '@/components/ui/switch';
24
+ import { zodResolver } from '@hookform/resolvers/zod';
25
+ import { useTranslations } from 'next-intl';
26
+ import { ReactNode, useMemo } from 'react';
27
+ import { useForm } from 'react-hook-form';
28
+ import { toast } from 'sonner';
29
+ import { z } from 'zod';
30
+
31
+ function SettingRow({
32
+ title,
33
+ description,
34
+ control,
35
+ }: {
36
+ title: string;
37
+ description: string;
38
+ control: ReactNode;
39
+ }) {
40
+ return (
41
+ <div className="grid gap-2 rounded border p-3 md:grid-cols-[1fr_auto] md:items-center">
42
+ <div>
43
+ <div className="text-sm font-semibold">{title}</div>
44
+ <div className="text-xs text-muted-foreground">{description}</div>
45
+ </div>
46
+ <div>{control}</div>
47
+ </div>
48
+ );
49
+ }
50
+
51
+ export default function TicketSettingsPage() {
52
+ const t = useTranslations('ticket.TicketModule');
53
+
54
+ const settingsSchema = useMemo(
55
+ () =>
56
+ z.object({
57
+ enabled: z.boolean(),
58
+ codePrefix: z
59
+ .string()
60
+ .trim()
61
+ .min(1, t('settings.validation.codePrefixRequired')),
62
+ aiMode: z.enum(['disabled', 'optional', 'required']),
63
+ visibility: z.enum(['private', 'public']),
64
+ autoAssign: z.boolean(),
65
+ owner: z
66
+ .string()
67
+ .trim()
68
+ .min(1, t('settings.validation.ownerRequired'))
69
+ .regex(/^\d+$/, t('settings.validation.ownerNumeric')),
70
+ publicReply: z.boolean(),
71
+ internalNotes: z.boolean(),
72
+ reopen: z.boolean(),
73
+ closeAfter: z
74
+ .string()
75
+ .trim()
76
+ .min(1, t('settings.validation.closeAfterRequired'))
77
+ .regex(/^\d+$/, t('settings.validation.closeAfterNumeric')),
78
+ }),
79
+ [t]
80
+ );
81
+
82
+ type SettingsForm = z.infer<typeof settingsSchema>;
83
+
84
+ const form = useForm<SettingsForm>({
85
+ resolver: zodResolver(settingsSchema),
86
+ defaultValues: {
87
+ enabled: true,
88
+ codePrefix: 'TCK',
89
+ aiMode: 'optional',
90
+ visibility: 'private',
91
+ autoAssign: false,
92
+ owner: '101',
93
+ publicReply: true,
94
+ internalNotes: true,
95
+ reopen: true,
96
+ closeAfter: '15',
97
+ },
98
+ });
99
+
100
+ const handleSubmit = async (values: SettingsForm) => {
101
+ console.log('ticket settings payload', values);
102
+ toast.success(t('settings.messages.saved'));
103
+ };
104
+
105
+ return (
106
+ <Page>
107
+ <PageHeader
108
+ title={t('settings.title')}
109
+ description={t('settings.description')}
110
+ breadcrumbs={[
111
+ { label: t('common.home'), href: '/' },
112
+ { label: t('common.tickets'), href: '/ticket' },
113
+ { label: t('settings.breadcrumb') },
114
+ ]}
115
+ />
116
+
117
+ <Form {...form}>
118
+ <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
119
+ <Card>
120
+ <CardHeader>
121
+ <CardTitle className="text-base">
122
+ {t('settings.general')}
123
+ </CardTitle>
124
+ </CardHeader>
125
+ <CardContent className="space-y-3">
126
+ <FormField
127
+ control={form.control}
128
+ name="enabled"
129
+ render={({ field }) => (
130
+ <SettingRow
131
+ title={t('settings.rows.enabledTitle')}
132
+ description={t('settings.rows.enabledDesc')}
133
+ control={
134
+ <FormItem>
135
+ <FormLabel className="sr-only">enabled</FormLabel>
136
+ <FormControl>
137
+ <Switch
138
+ checked={field.value}
139
+ onCheckedChange={field.onChange}
140
+ />
141
+ </FormControl>
142
+ <FormMessage />
143
+ </FormItem>
144
+ }
145
+ />
146
+ )}
147
+ />
148
+
149
+ <FormField
150
+ control={form.control}
151
+ name="codePrefix"
152
+ render={({ field }) => (
153
+ <SettingRow
154
+ title={t('settings.rows.codePrefixTitle')}
155
+ description={t('settings.rows.codePrefixDesc')}
156
+ control={
157
+ <FormItem>
158
+ <FormLabel className="sr-only">codePrefix</FormLabel>
159
+ <FormControl>
160
+ <Input {...field} className="w-[180px]" />
161
+ </FormControl>
162
+ <FormMessage />
163
+ </FormItem>
164
+ }
165
+ />
166
+ )}
167
+ />
168
+
169
+ <FormField
170
+ control={form.control}
171
+ name="aiMode"
172
+ render={({ field }) => (
173
+ <SettingRow
174
+ title={t('settings.rows.aiModeTitle')}
175
+ description={t('settings.rows.aiModeDesc')}
176
+ control={
177
+ <FormItem>
178
+ <FormLabel className="sr-only">aiMode</FormLabel>
179
+ <FormControl>
180
+ <Select
181
+ value={field.value}
182
+ onValueChange={field.onChange}
183
+ >
184
+ <SelectTrigger className="w-[180px]">
185
+ <SelectValue />
186
+ </SelectTrigger>
187
+ <SelectContent>
188
+ <SelectItem value="disabled">
189
+ {t('settings.values.disabled')}
190
+ </SelectItem>
191
+ <SelectItem value="optional">
192
+ {t('settings.values.optional')}
193
+ </SelectItem>
194
+ <SelectItem value="required">
195
+ {t('settings.values.required')}
196
+ </SelectItem>
197
+ </SelectContent>
198
+ </Select>
199
+ </FormControl>
200
+ <FormMessage />
201
+ </FormItem>
202
+ }
203
+ />
204
+ )}
205
+ />
206
+
207
+ <FormField
208
+ control={form.control}
209
+ name="visibility"
210
+ render={({ field }) => (
211
+ <SettingRow
212
+ title={t('settings.rows.visibilityTitle')}
213
+ description={t('settings.rows.visibilityDesc')}
214
+ control={
215
+ <FormItem>
216
+ <FormLabel className="sr-only">visibility</FormLabel>
217
+ <FormControl>
218
+ <Select
219
+ value={field.value}
220
+ onValueChange={field.onChange}
221
+ >
222
+ <SelectTrigger className="w-[180px]">
223
+ <SelectValue />
224
+ </SelectTrigger>
225
+ <SelectContent>
226
+ <SelectItem value="private">
227
+ {t('settings.values.private')}
228
+ </SelectItem>
229
+ <SelectItem value="public">
230
+ {t('settings.values.public')}
231
+ </SelectItem>
232
+ </SelectContent>
233
+ </Select>
234
+ </FormControl>
235
+ <FormMessage />
236
+ </FormItem>
237
+ }
238
+ />
239
+ )}
240
+ />
241
+
242
+ <FormField
243
+ control={form.control}
244
+ name="autoAssign"
245
+ render={({ field }) => (
246
+ <SettingRow
247
+ title={t('settings.rows.autoAssignTitle')}
248
+ description={t('settings.rows.autoAssignDesc')}
249
+ control={
250
+ <FormItem>
251
+ <FormLabel className="sr-only">autoAssign</FormLabel>
252
+ <FormControl>
253
+ <Switch
254
+ checked={field.value}
255
+ onCheckedChange={field.onChange}
256
+ />
257
+ </FormControl>
258
+ <FormMessage />
259
+ </FormItem>
260
+ }
261
+ />
262
+ )}
263
+ />
264
+
265
+ <FormField
266
+ control={form.control}
267
+ name="owner"
268
+ render={({ field }) => (
269
+ <SettingRow
270
+ title={t('settings.rows.ownerTitle')}
271
+ description={t('settings.rows.ownerDesc')}
272
+ control={
273
+ <FormItem>
274
+ <FormLabel className="sr-only">owner</FormLabel>
275
+ <FormControl>
276
+ <Input {...field} className="w-[180px]" />
277
+ </FormControl>
278
+ <FormMessage />
279
+ </FormItem>
280
+ }
281
+ />
282
+ )}
283
+ />
284
+
285
+ <FormField
286
+ control={form.control}
287
+ name="publicReply"
288
+ render={({ field }) => (
289
+ <SettingRow
290
+ title={t('settings.rows.publicReplyTitle')}
291
+ description={t('settings.rows.publicReplyDesc')}
292
+ control={
293
+ <FormItem>
294
+ <FormLabel className="sr-only">publicReply</FormLabel>
295
+ <FormControl>
296
+ <Switch
297
+ checked={field.value}
298
+ onCheckedChange={field.onChange}
299
+ />
300
+ </FormControl>
301
+ <FormMessage />
302
+ </FormItem>
303
+ }
304
+ />
305
+ )}
306
+ />
307
+
308
+ <FormField
309
+ control={form.control}
310
+ name="internalNotes"
311
+ render={({ field }) => (
312
+ <SettingRow
313
+ title={t('settings.rows.internalNotesTitle')}
314
+ description={t('settings.rows.internalNotesDesc')}
315
+ control={
316
+ <FormItem>
317
+ <FormLabel className="sr-only">internalNotes</FormLabel>
318
+ <FormControl>
319
+ <Switch
320
+ checked={field.value}
321
+ onCheckedChange={field.onChange}
322
+ />
323
+ </FormControl>
324
+ <FormMessage />
325
+ </FormItem>
326
+ }
327
+ />
328
+ )}
329
+ />
330
+
331
+ <FormField
332
+ control={form.control}
333
+ name="reopen"
334
+ render={({ field }) => (
335
+ <SettingRow
336
+ title={t('settings.rows.reopenTitle')}
337
+ description={t('settings.rows.reopenDesc')}
338
+ control={
339
+ <FormItem>
340
+ <FormLabel className="sr-only">reopen</FormLabel>
341
+ <FormControl>
342
+ <Switch
343
+ checked={field.value}
344
+ onCheckedChange={field.onChange}
345
+ />
346
+ </FormControl>
347
+ <FormMessage />
348
+ </FormItem>
349
+ }
350
+ />
351
+ )}
352
+ />
353
+
354
+ <FormField
355
+ control={form.control}
356
+ name="closeAfter"
357
+ render={({ field }) => (
358
+ <SettingRow
359
+ title={t('settings.rows.closeAfterTitle')}
360
+ description={t('settings.rows.closeAfterDesc')}
361
+ control={
362
+ <FormItem>
363
+ <FormLabel className="sr-only">closeAfter</FormLabel>
364
+ <FormControl>
365
+ <Input {...field} className="w-[180px]" />
366
+ </FormControl>
367
+ <FormMessage />
368
+ </FormItem>
369
+ }
370
+ />
371
+ )}
372
+ />
373
+
374
+ <Button type="submit">{t('settings.actions.save')}</Button>
375
+ </CardContent>
376
+ </Card>
377
+ </form>
378
+ </Form>
379
+
380
+ <div className="grid gap-4 lg:grid-cols-2">
381
+ <Card>
382
+ <CardHeader>
383
+ <CardTitle className="text-base">
384
+ {t('settings.groups.email')}
385
+ </CardTitle>
386
+ </CardHeader>
387
+ <CardContent className="space-y-2 text-sm">
388
+ <Badge variant="outline">ticket.channel.email.*</Badge>
389
+ <p className="text-muted-foreground">
390
+ {t('settings.descriptions.emailControls')}
391
+ </p>
392
+ </CardContent>
393
+ </Card>
394
+
395
+ <Card>
396
+ <CardHeader>
397
+ <CardTitle className="text-base">
398
+ {t('settings.groups.whatsapp')}
399
+ </CardTitle>
400
+ </CardHeader>
401
+ <CardContent className="space-y-2 text-sm">
402
+ <Badge variant="outline">ticket.channel.whatsapp.*</Badge>
403
+ <p className="text-muted-foreground">
404
+ {t('settings.descriptions.whatsappControls')}
405
+ </p>
406
+ </CardContent>
407
+ </Card>
408
+
409
+ <Card>
410
+ <CardHeader>
411
+ <CardTitle className="text-base">
412
+ {t('settings.groups.portal')}
413
+ </CardTitle>
414
+ </CardHeader>
415
+ <CardContent className="space-y-2 text-sm">
416
+ <Badge variant="outline">ticket.channel.portal.*</Badge>
417
+ <p className="text-muted-foreground">
418
+ {t('settings.descriptions.portalControls')}
419
+ </p>
420
+ </CardContent>
421
+ </Card>
422
+
423
+ <Card>
424
+ <CardHeader>
425
+ <CardTitle className="text-base">
426
+ {t('settings.groups.marketplace')}
427
+ </CardTitle>
428
+ </CardHeader>
429
+ <CardContent className="space-y-2 text-sm">
430
+ <Badge variant="outline">ticket.channel.marketplace.*</Badge>
431
+ <p className="text-muted-foreground">
432
+ {t('settings.descriptions.marketplaceControls')}
433
+ </p>
434
+ </CardContent>
435
+ </Card>
436
+
437
+ <Card>
438
+ <CardHeader>
439
+ <CardTitle className="text-base">
440
+ {t('settings.groups.social')}
441
+ </CardTitle>
442
+ </CardHeader>
443
+ <CardContent className="space-y-2 text-sm">
444
+ <Badge variant="outline">ticket.channel.social.*</Badge>
445
+ <p className="text-muted-foreground">
446
+ {t('settings.descriptions.socialControls')}
447
+ </p>
448
+ </CardContent>
449
+ </Card>
450
+
451
+ <Card>
452
+ <CardHeader>
453
+ <CardTitle className="text-base">
454
+ {t('settings.groups.accounts')}
455
+ </CardTitle>
456
+ </CardHeader>
457
+ <CardContent className="space-y-2 text-sm">
458
+ <Badge variant="outline">
459
+ ticket.channel_account.suporte_hcode_com_br.*
460
+ </Badge>
461
+ <Badge variant="outline">
462
+ ticket.channel_account.whatsapp_hcode.*
463
+ </Badge>
464
+ <p className="text-muted-foreground">
465
+ {t('settings.descriptions.accountsControls')}
466
+ </p>
467
+ </CardContent>
468
+ </Card>
469
+ </div>
470
+
471
+ <Card>
472
+ <CardHeader>
473
+ <CardTitle className="text-base">
474
+ {t('settings.groups.help')}
475
+ </CardTitle>
476
+ </CardHeader>
477
+ <CardContent className="space-y-2 text-sm text-muted-foreground">
478
+ <p>{t('settings.descriptions.helpLine1')}</p>
479
+ <p>{t('settings.descriptions.helpLine2')}</p>
480
+ </CardContent>
481
+ </Card>
482
+ </Page>
483
+ );
484
+ }