@hed-hog/operations 0.0.304 → 0.0.306

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 (81) hide show
  1. package/dist/controllers/operations-projects.controller.d.ts +15 -0
  2. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-tasks.controller.d.ts +41 -10
  4. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  5. package/dist/controllers/operations-tasks.controller.js +11 -0
  6. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  7. package/dist/controllers/operations-timesheets.controller.d.ts +21 -0
  8. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  9. package/dist/controllers/operations-timesheets.controller.js +12 -0
  10. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  11. package/dist/dto/create-task.dto.d.ts +7 -1
  12. package/dist/dto/create-task.dto.d.ts.map +1 -1
  13. package/dist/dto/create-task.dto.js +38 -5
  14. package/dist/dto/create-task.dto.js.map +1 -1
  15. package/dist/dto/list-tasks.dto.d.ts +1 -1
  16. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  17. package/dist/dto/list-tasks.dto.js +2 -2
  18. package/dist/dto/list-tasks.dto.js.map +1 -1
  19. package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
  20. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
  21. package/dist/dto/update-collaborator-type.dto.js +2 -1
  22. package/dist/dto/update-collaborator-type.dto.js.map +1 -1
  23. package/dist/dto/update-task.dto.d.ts +7 -1
  24. package/dist/dto/update-task.dto.d.ts.map +1 -1
  25. package/dist/dto/update-task.dto.js +38 -5
  26. package/dist/dto/update-task.dto.js.map +1 -1
  27. package/dist/operations.service.d.ts +90 -12
  28. package/dist/operations.service.d.ts.map +1 -1
  29. package/dist/operations.service.js +560 -148
  30. package/dist/operations.service.js.map +1 -1
  31. package/dist/operations.service.spec.js +73 -0
  32. package/dist/operations.service.spec.js.map +1 -1
  33. package/hedhog/data/menu.yaml +26 -26
  34. package/hedhog/data/operations_collaborator_type.yaml +76 -76
  35. package/hedhog/data/route.yaml +26 -0
  36. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
  37. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
  38. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
  39. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  40. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  41. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  42. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  43. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  44. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  45. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
  46. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  47. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  48. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
  49. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
  50. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
  51. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  52. package/hedhog/frontend/app/approvals/page.tsx.ejs +2 -2
  53. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +513 -502
  54. package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
  55. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  56. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  57. package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
  58. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +235 -72
  59. package/hedhog/frontend/app/timesheets/page.tsx.ejs +344 -134
  60. package/hedhog/frontend/messages/en.json +32 -4
  61. package/hedhog/frontend/messages/pt.json +34 -6
  62. package/hedhog/table/operations_collaborator.yaml +18 -18
  63. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
  64. package/hedhog/table/operations_collaborator_type.yaml +33 -33
  65. package/hedhog/table/operations_contract_document.yaml +33 -33
  66. package/hedhog/table/operations_project.yaml +9 -0
  67. package/hedhog/table/operations_task.yaml +43 -4
  68. package/package.json +6 -6
  69. package/src/controllers/operations-tasks.controller.ts +11 -0
  70. package/src/controllers/operations-timesheets.controller.ts +13 -0
  71. package/src/dto/create-collaborator-type.dto.ts +43 -43
  72. package/src/dto/create-collaborator.dto.ts +223 -223
  73. package/src/dto/create-task.dto.ts +47 -7
  74. package/src/dto/list-collaborator-types.dto.ts +15 -15
  75. package/src/dto/list-collaborators.dto.ts +30 -30
  76. package/src/dto/list-tasks.dto.ts +3 -3
  77. package/src/dto/update-collaborator-type.dto.ts +4 -3
  78. package/src/dto/update-collaborator.dto.ts +3 -3
  79. package/src/dto/update-task.dto.ts +47 -7
  80. package/src/operations.service.spec.ts +96 -0
  81. package/src/operations.service.ts +813 -135
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { EmptyState, Page } from '@/components/entity-list';
4
- import { Button } from '@/components/ui/button';
5
- import { FormActions } from '@/components/ui/form-actions';
3
+ import { EmptyState, Page } from '@/components/entity-list';
4
+ import { Button } from '@/components/ui/button';
5
+ import { FormActions } from '@/components/ui/form-actions';
6
6
  import { Input } from '@/components/ui/input';
7
7
  import { Label } from '@/components/ui/label';
8
8
  import {
@@ -21,12 +21,12 @@ import Link from 'next/link';
21
21
  import { useRouter } from 'next/navigation';
22
22
  import { useEffect, useState } from 'react';
23
23
  import { fetchOperations, mutateOperations } from '../_lib/api';
24
- import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
25
- import type { OperationsContractTemplate } from '../_lib/types';
26
- import { formatEnumLabel } from '../_lib/utils/format';
27
- import { trimToNull } from '../_lib/utils/forms';
28
- import { ContractContentEditor } from './contract-content-editor';
29
- import { OperationsHeader } from './operations-header';
24
+ import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
25
+ import type { OperationsContractTemplate } from '../_lib/types';
26
+ import { formatEnumLabel } from '../_lib/utils/format';
27
+ import { trimToNull } from '../_lib/utils/forms';
28
+ import { ContractContentEditor } from './contract-content-editor';
29
+ import { OperationsHeader } from './operations-header';
30
30
 
31
31
  type ContractTemplateFormState = {
32
32
  code: string;
@@ -86,15 +86,15 @@ export function ContractTemplateFormScreen({
86
86
  }: ContractTemplateFormScreenProps) {
87
87
  const t = useTranslations('operations.ContractTemplateFormPage');
88
88
  const commonT = useTranslations('operations.Common');
89
- const { request, showToastHandler, currentLocaleCode } = useApp();
90
- const access = useOperationsAccess();
91
- const router = useRouter();
92
- const [form, setForm] = useState<ContractTemplateFormState>(buildEmptyForm());
93
- const isSheetMode = Boolean(onCancel);
94
- const [sheetStep, setSheetStep] = useState(0);
95
- const nextLabel = currentLocaleCode?.toLowerCase().startsWith('pt')
96
- ? 'Proximo'
97
- : 'Next';
89
+ const { request, showToastHandler, currentLocaleCode } = useApp();
90
+ const access = useOperationsAccess();
91
+ const router = useRouter();
92
+ const [form, setForm] = useState<ContractTemplateFormState>(buildEmptyForm());
93
+ const isSheetMode = Boolean(onCancel);
94
+ const [sheetStep, setSheetStep] = useState(0);
95
+ const nextLabel = currentLocaleCode?.toLowerCase().startsWith('pt')
96
+ ? 'Proximo'
97
+ : 'Next';
98
98
 
99
99
  const { data: template, isLoading } = useQuery<OperationsContractTemplate>({
100
100
  queryKey: [
@@ -110,31 +110,31 @@ export function ContractTemplateFormScreen({
110
110
  ),
111
111
  });
112
112
 
113
- useEffect(() => {
114
- if (template) {
115
- // eslint-disable-next-line react-hooks/set-state-in-effect
116
- setForm(toFormState(template));
117
- return;
113
+ useEffect(() => {
114
+ if (template) {
115
+ // eslint-disable-next-line react-hooks/set-state-in-effect
116
+ setForm(toFormState(template));
117
+ return;
118
118
  }
119
119
 
120
120
  if (!templateId) {
121
- setForm(buildEmptyForm());
122
- }
123
- }, [template, templateId]);
124
-
125
- useEffect(() => {
126
- if (!isSheetMode) return;
127
- setSheetStep(0);
128
- }, [isSheetMode, templateId]);
129
-
130
- const validateOverviewStep = () => {
131
- if (!form.name.trim()) {
132
- showToastHandler?.('error', t('messages.requiredFields'));
133
- return false;
134
- }
135
-
136
- return true;
137
- };
121
+ setForm(buildEmptyForm());
122
+ }
123
+ }, [template, templateId]);
124
+
125
+ useEffect(() => {
126
+ if (!isSheetMode) return;
127
+ setSheetStep(0);
128
+ }, [isSheetMode, templateId]);
129
+
130
+ const validateOverviewStep = () => {
131
+ if (!form.name.trim()) {
132
+ showToastHandler?.('error', t('messages.requiredFields'));
133
+ return false;
134
+ }
135
+
136
+ return true;
137
+ };
138
138
 
139
139
  const onSubmit = async () => {
140
140
  if (!form.name.trim()) {
@@ -224,272 +224,272 @@ export function ContractTemplateFormScreen({
224
224
  );
225
225
  }
226
226
 
227
- const overviewSection = (
228
- <section className="space-y-6">
229
- <div className="space-y-1">
230
- <h2 className="text-xl font-semibold">{t('sections.overview')}</h2>
231
- <p className="text-sm text-muted-foreground">
232
- {t('sections.overviewDescription')}
233
- </p>
234
- </div>
235
-
236
- <div className="grid gap-4 md:grid-cols-12">
237
- <div className="space-y-2 md:col-span-3">
238
- <Label htmlFor="contract-template-code">{t('fields.code')}</Label>
239
- <Input
240
- id="contract-template-code"
241
- value={form.code}
242
- placeholder={t('placeholders.code')}
243
- onChange={(event) =>
244
- setForm((current) => ({ ...current, code: event.target.value }))
245
- }
246
- />
247
- </div>
248
- <div className="space-y-2 md:col-span-6">
249
- <Label htmlFor="contract-template-name">{t('fields.name')}</Label>
250
- <Input
251
- id="contract-template-name"
252
- value={form.name}
253
- placeholder={t('placeholders.name')}
254
- onChange={(event) =>
255
- setForm((current) => ({ ...current, name: event.target.value }))
256
- }
257
- />
258
- </div>
259
- <div className="space-y-2 md:col-span-3">
260
- <Label>{commonT('labels.status')}</Label>
261
- <Select
262
- value={form.status}
263
- onValueChange={(value) =>
264
- setForm((current) => ({ ...current, status: value }))
265
- }
266
- >
267
- <SelectTrigger className="w-full">
268
- <SelectValue />
269
- </SelectTrigger>
270
- <SelectContent>
271
- {['active', 'draft', 'inactive', 'archived'].map((value) => (
272
- <SelectItem key={value} value={value}>
273
- {formatEnumLabel(value)}
274
- </SelectItem>
275
- ))}
276
- </SelectContent>
277
- </Select>
278
- </div>
279
-
280
- <div className="space-y-2 md:col-span-3">
281
- <Label>{t('fields.contractCategory')}</Label>
282
- <Select
283
- value={form.contractCategory}
284
- onValueChange={(value) =>
285
- setForm((current) => ({ ...current, contractCategory: value }))
286
- }
287
- >
288
- <SelectTrigger className="w-full">
289
- <SelectValue />
290
- </SelectTrigger>
291
- <SelectContent>
292
- {[
293
- 'employee',
294
- 'contractor',
295
- 'client',
296
- 'supplier',
297
- 'vendor',
298
- 'partner',
299
- 'internal',
300
- 'other',
301
- ].map((value) => (
302
- <SelectItem key={value} value={value}>
303
- {formatEnumLabel(value)}
304
- </SelectItem>
305
- ))}
306
- </SelectContent>
307
- </Select>
308
- </div>
309
- <div className="space-y-2 md:col-span-3">
310
- <Label>{t('fields.contractType')}</Label>
311
- <Select
312
- value={form.contractType}
313
- onValueChange={(value) =>
314
- setForm((current) => ({ ...current, contractType: value }))
315
- }
316
- >
317
- <SelectTrigger className="w-full">
318
- <SelectValue />
319
- </SelectTrigger>
320
- <SelectContent>
321
- {[
322
- 'clt',
323
- 'pj',
324
- 'freelancer_agreement',
325
- 'service_agreement',
326
- 'fixed_term',
327
- 'recurring_service',
328
- 'nda',
329
- 'amendment',
330
- 'addendum',
331
- 'other',
332
- ].map((value) => (
333
- <SelectItem key={value} value={value}>
334
- {formatEnumLabel(value)}
335
- </SelectItem>
336
- ))}
337
- </SelectContent>
338
- </Select>
339
- </div>
340
- <div className="space-y-2 md:col-span-3">
341
- <Label>{commonT('labels.billingModel')}</Label>
342
- <Select
343
- value={form.billingModel}
344
- onValueChange={(value) =>
345
- setForm((current) => ({ ...current, billingModel: value }))
346
- }
347
- >
348
- <SelectTrigger className="w-full">
349
- <SelectValue />
350
- </SelectTrigger>
351
- <SelectContent>
352
- {['time_and_material', 'monthly_retainer', 'fixed_price'].map(
353
- (value) => (
354
- <SelectItem key={value} value={value}>
355
- {formatEnumLabel(value)}
356
- </SelectItem>
357
- )
358
- )}
359
- </SelectContent>
360
- </Select>
361
- </div>
362
- <div className="space-y-2 md:col-span-3">
363
- <Label>{t('fields.signatureStatus')}</Label>
364
- <Select
365
- value={form.signatureStatus}
366
- onValueChange={(value) =>
367
- setForm((current) => ({ ...current, signatureStatus: value }))
368
- }
369
- >
370
- <SelectTrigger className="w-full">
371
- <SelectValue />
372
- </SelectTrigger>
373
- <SelectContent>
374
- {[
375
- 'not_started',
376
- 'pending',
377
- 'partially_signed',
378
- 'signed',
379
- 'expired',
380
- ].map((value) => (
381
- <SelectItem key={value} value={value}>
382
- {formatEnumLabel(value)}
383
- </SelectItem>
384
- ))}
385
- </SelectContent>
386
- </Select>
387
- </div>
388
-
389
- <div className="space-y-2 md:col-span-12">
390
- <Label htmlFor="contract-template-description">
391
- {t('fields.description')}
392
- </Label>
393
- <Textarea
394
- id="contract-template-description"
395
- rows={3}
396
- value={form.description}
397
- placeholder={t('placeholders.description')}
398
- onChange={(event) =>
399
- setForm((current) => ({
400
- ...current,
401
- description: event.target.value,
402
- }))
403
- }
404
- />
405
- </div>
406
-
407
- <div className="flex items-center justify-between rounded-lg border px-4 py-4 md:col-span-12">
408
- <div className="space-y-1">
409
- <div className="text-sm font-medium">{t('fields.isActive')}</div>
410
- <div className="text-xs text-muted-foreground">
411
- {t('fields.isActiveDescription')}
412
- </div>
413
- </div>
414
- <Switch
415
- checked={form.isActive}
416
- onCheckedChange={(checked) =>
417
- setForm((current) => ({ ...current, isActive: checked }))
418
- }
419
- />
420
- </div>
421
- </div>
422
- </section>
423
- );
424
-
425
- const contentSection = (
426
- <section className="space-y-6">
427
- <ContractContentEditor
428
- value={form.contentHtml}
429
- onChange={(value) =>
430
- setForm((current) => ({ ...current, contentHtml: value }))
431
- }
432
- editorTitle={t('sections.editor')}
433
- editorDescription={t('sections.editorDescription')}
434
- previewTitle={t('sections.preview')}
435
- previewDescription={t('sections.previewDescription')}
436
- promptContext={{
437
- name: form.name,
438
- code: form.code,
439
- description: form.description,
440
- contract_type: form.contractType,
441
- billing_model: form.billingModel,
442
- }}
443
- showPreview={false}
444
- chrome="plain"
445
- />
446
- </section>
447
- );
448
-
449
- const formBody = isSheetMode ? (
450
- <div className="space-y-6">
451
- {sheetStep === 0 ? overviewSection : contentSection}
452
- {templateId && isLoading ? (
453
- <div className="text-sm text-muted-foreground">{t('loading')}</div>
454
- ) : null}
455
- </div>
456
- ) : (
457
- <div className="space-y-8">
458
- {overviewSection}
459
- {contentSection}
460
- {templateId && isLoading ? (
461
- <div className="text-sm text-muted-foreground">{t('loading')}</div>
462
- ) : null}
463
- </div>
464
- );
465
-
466
- if (isSheetMode) {
467
- return (
468
- <div className="mt-4 space-y-6 pb-6">
469
- {formBody}
470
-
471
- <FormActions
472
- sheet
473
- cancelLabel={
474
- sheetStep === 0 ? commonT('actions.cancel') : commonT('actions.back')
475
- }
476
- onCancel={
477
- sheetStep === 0 ? onCancel : () => setSheetStep((current) => current - 1)
478
- }
479
- onSubmit={() => {
480
- if (sheetStep === 0) {
481
- if (!validateOverviewStep()) return;
482
- setSheetStep(1);
483
- return;
484
- }
485
-
486
- void onSubmit();
487
- }}
488
- submitIcon={sheetStep === 0 ? undefined : <Save className="size-4" />}
489
- submitLabel={sheetStep === 0 ? nextLabel : commonT('actions.save')}
490
- submitSize="lg"
491
- />
492
- </div>
227
+ const overviewSection = (
228
+ <section className="space-y-6">
229
+ <div className="space-y-1">
230
+ <h2 className="text-xl font-semibold">{t('sections.overview')}</h2>
231
+ <p className="text-sm text-muted-foreground">
232
+ {t('sections.overviewDescription')}
233
+ </p>
234
+ </div>
235
+
236
+ <div className="grid gap-4 md:grid-cols-12">
237
+ <div className="space-y-2 md:col-span-3">
238
+ <Label htmlFor="contract-template-code">{t('fields.code')}</Label>
239
+ <Input
240
+ id="contract-template-code"
241
+ value={form.code}
242
+ placeholder={t('placeholders.code')}
243
+ onChange={(event) =>
244
+ setForm((current) => ({ ...current, code: event.target.value }))
245
+ }
246
+ />
247
+ </div>
248
+ <div className="space-y-2 md:col-span-6">
249
+ <Label htmlFor="contract-template-name">{t('fields.name')}</Label>
250
+ <Input
251
+ id="contract-template-name"
252
+ value={form.name}
253
+ placeholder={t('placeholders.name')}
254
+ onChange={(event) =>
255
+ setForm((current) => ({ ...current, name: event.target.value }))
256
+ }
257
+ />
258
+ </div>
259
+ <div className="space-y-2 md:col-span-3">
260
+ <Label>{commonT('labels.status')}</Label>
261
+ <Select
262
+ value={form.status}
263
+ onValueChange={(value) =>
264
+ setForm((current) => ({ ...current, status: value }))
265
+ }
266
+ >
267
+ <SelectTrigger className="w-full">
268
+ <SelectValue />
269
+ </SelectTrigger>
270
+ <SelectContent>
271
+ {['active', 'draft', 'inactive', 'archived'].map((value) => (
272
+ <SelectItem key={value} value={value}>
273
+ {formatEnumLabel(value)}
274
+ </SelectItem>
275
+ ))}
276
+ </SelectContent>
277
+ </Select>
278
+ </div>
279
+
280
+ <div className="space-y-2 md:col-span-3">
281
+ <Label>{t('fields.contractCategory')}</Label>
282
+ <Select
283
+ value={form.contractCategory}
284
+ onValueChange={(value) =>
285
+ setForm((current) => ({ ...current, contractCategory: value }))
286
+ }
287
+ >
288
+ <SelectTrigger className="w-full">
289
+ <SelectValue />
290
+ </SelectTrigger>
291
+ <SelectContent>
292
+ {[
293
+ 'employee',
294
+ 'contractor',
295
+ 'client',
296
+ 'supplier',
297
+ 'vendor',
298
+ 'partner',
299
+ 'internal',
300
+ 'other',
301
+ ].map((value) => (
302
+ <SelectItem key={value} value={value}>
303
+ {formatEnumLabel(value)}
304
+ </SelectItem>
305
+ ))}
306
+ </SelectContent>
307
+ </Select>
308
+ </div>
309
+ <div className="space-y-2 md:col-span-3">
310
+ <Label>{t('fields.contractType')}</Label>
311
+ <Select
312
+ value={form.contractType}
313
+ onValueChange={(value) =>
314
+ setForm((current) => ({ ...current, contractType: value }))
315
+ }
316
+ >
317
+ <SelectTrigger className="w-full">
318
+ <SelectValue />
319
+ </SelectTrigger>
320
+ <SelectContent>
321
+ {[
322
+ 'clt',
323
+ 'pj',
324
+ 'freelancer_agreement',
325
+ 'service_agreement',
326
+ 'fixed_term',
327
+ 'recurring_service',
328
+ 'nda',
329
+ 'amendment',
330
+ 'addendum',
331
+ 'other',
332
+ ].map((value) => (
333
+ <SelectItem key={value} value={value}>
334
+ {formatEnumLabel(value)}
335
+ </SelectItem>
336
+ ))}
337
+ </SelectContent>
338
+ </Select>
339
+ </div>
340
+ <div className="space-y-2 md:col-span-3">
341
+ <Label>{commonT('labels.billingModel')}</Label>
342
+ <Select
343
+ value={form.billingModel}
344
+ onValueChange={(value) =>
345
+ setForm((current) => ({ ...current, billingModel: value }))
346
+ }
347
+ >
348
+ <SelectTrigger className="w-full">
349
+ <SelectValue />
350
+ </SelectTrigger>
351
+ <SelectContent>
352
+ {['time_and_material', 'monthly_retainer', 'fixed_price'].map(
353
+ (value) => (
354
+ <SelectItem key={value} value={value}>
355
+ {formatEnumLabel(value)}
356
+ </SelectItem>
357
+ )
358
+ )}
359
+ </SelectContent>
360
+ </Select>
361
+ </div>
362
+ <div className="space-y-2 md:col-span-3">
363
+ <Label>{t('fields.signatureStatus')}</Label>
364
+ <Select
365
+ value={form.signatureStatus}
366
+ onValueChange={(value) =>
367
+ setForm((current) => ({ ...current, signatureStatus: value }))
368
+ }
369
+ >
370
+ <SelectTrigger className="w-full">
371
+ <SelectValue />
372
+ </SelectTrigger>
373
+ <SelectContent>
374
+ {[
375
+ 'not_started',
376
+ 'pending',
377
+ 'partially_signed',
378
+ 'signed',
379
+ 'expired',
380
+ ].map((value) => (
381
+ <SelectItem key={value} value={value}>
382
+ {formatEnumLabel(value)}
383
+ </SelectItem>
384
+ ))}
385
+ </SelectContent>
386
+ </Select>
387
+ </div>
388
+
389
+ <div className="space-y-2 md:col-span-12">
390
+ <Label htmlFor="contract-template-description">
391
+ {t('fields.description')}
392
+ </Label>
393
+ <Textarea
394
+ id="contract-template-description"
395
+ rows={3}
396
+ value={form.description}
397
+ placeholder={t('placeholders.description')}
398
+ onChange={(event) =>
399
+ setForm((current) => ({
400
+ ...current,
401
+ description: event.target.value,
402
+ }))
403
+ }
404
+ />
405
+ </div>
406
+
407
+ <div className="flex items-center justify-between rounded-lg border px-4 py-4 md:col-span-12">
408
+ <div className="space-y-1">
409
+ <div className="text-sm font-medium">{t('fields.isActive')}</div>
410
+ <div className="text-xs text-muted-foreground">
411
+ {t('fields.isActiveDescription')}
412
+ </div>
413
+ </div>
414
+ <Switch
415
+ checked={form.isActive}
416
+ onCheckedChange={(checked) =>
417
+ setForm((current) => ({ ...current, isActive: checked }))
418
+ }
419
+ />
420
+ </div>
421
+ </div>
422
+ </section>
423
+ );
424
+
425
+ const contentSection = (
426
+ <section className="space-y-6">
427
+ <ContractContentEditor
428
+ value={form.contentHtml}
429
+ onChange={(value) =>
430
+ setForm((current) => ({ ...current, contentHtml: value }))
431
+ }
432
+ editorTitle={t('sections.editor')}
433
+ editorDescription={t('sections.editorDescription')}
434
+ previewTitle={t('sections.preview')}
435
+ previewDescription={t('sections.previewDescription')}
436
+ promptContext={{
437
+ name: form.name,
438
+ code: form.code,
439
+ description: form.description,
440
+ contract_type: form.contractType,
441
+ billing_model: form.billingModel,
442
+ }}
443
+ showPreview={false}
444
+ chrome="plain"
445
+ />
446
+ </section>
447
+ );
448
+
449
+ const formBody = isSheetMode ? (
450
+ <div className="space-y-6">
451
+ {sheetStep === 0 ? overviewSection : contentSection}
452
+ {templateId && isLoading ? (
453
+ <div className="text-sm text-muted-foreground">{t('loading')}</div>
454
+ ) : null}
455
+ </div>
456
+ ) : (
457
+ <div className="space-y-8">
458
+ {overviewSection}
459
+ {contentSection}
460
+ {templateId && isLoading ? (
461
+ <div className="text-sm text-muted-foreground">{t('loading')}</div>
462
+ ) : null}
463
+ </div>
464
+ );
465
+
466
+ if (isSheetMode) {
467
+ return (
468
+ <div className="mt-4 space-y-6 pb-6">
469
+ {formBody}
470
+
471
+ <FormActions
472
+ sheet
473
+ cancelLabel={
474
+ sheetStep === 0 ? commonT('actions.cancel') : commonT('actions.back')
475
+ }
476
+ onCancel={
477
+ sheetStep === 0 ? onCancel : () => setSheetStep((current) => current - 1)
478
+ }
479
+ onSubmit={() => {
480
+ if (sheetStep === 0) {
481
+ if (!validateOverviewStep()) return;
482
+ setSheetStep(1);
483
+ return;
484
+ }
485
+
486
+ void onSubmit();
487
+ }}
488
+ submitIcon={sheetStep === 0 ? undefined : <Save className="size-4" />}
489
+ submitLabel={sheetStep === 0 ? nextLabel : commonT('actions.save')}
490
+ submitSize="lg"
491
+ />
492
+ </div>
493
493
  );
494
494
  }
495
495