@hed-hog/operations 0.0.306 → 0.0.310

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 (123) hide show
  1. package/dist/controllers/operations-approvals.controller.d.ts +114 -1
  2. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-approvals.controller.js +16 -3
  4. package/dist/controllers/operations-approvals.controller.js.map +1 -1
  5. package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +16 -3
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-contracts.controller.d.ts +14 -453
  10. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-contracts.controller.js +11 -112
  12. package/dist/controllers/operations-contracts.controller.js.map +1 -1
  13. package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
  14. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
  15. package/dist/controllers/operations-org-structure.controller.js +18 -5
  16. package/dist/controllers/operations-org-structure.controller.js.map +1 -1
  17. package/dist/controllers/operations-projects.controller.d.ts +28 -4
  18. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-projects.controller.js +17 -5
  20. package/dist/controllers/operations-projects.controller.js.map +1 -1
  21. package/dist/controllers/operations-timesheets.controller.d.ts +31 -4
  22. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  23. package/dist/controllers/operations-timesheets.controller.js +16 -11
  24. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  25. package/dist/dto/list-approvals.dto.d.ts +6 -0
  26. package/dist/dto/list-approvals.dto.d.ts.map +1 -0
  27. package/dist/dto/list-approvals.dto.js +28 -0
  28. package/dist/dto/list-approvals.dto.js.map +1 -0
  29. package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
  30. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
  31. package/dist/dto/list-collaborator-types.dto.js +7 -1
  32. package/dist/dto/list-collaborator-types.dto.js.map +1 -1
  33. package/dist/dto/list-collaborators.dto.d.ts +1 -0
  34. package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
  35. package/dist/dto/list-collaborators.dto.js +5 -0
  36. package/dist/dto/list-collaborators.dto.js.map +1 -1
  37. package/dist/dto/list-contracts.dto.d.ts +8 -0
  38. package/dist/dto/list-contracts.dto.d.ts.map +1 -0
  39. package/dist/dto/list-contracts.dto.js +38 -0
  40. package/dist/dto/list-contracts.dto.js.map +1 -0
  41. package/dist/dto/list-departments.dto.d.ts +5 -0
  42. package/dist/dto/list-departments.dto.d.ts.map +1 -0
  43. package/dist/dto/list-departments.dto.js +23 -0
  44. package/dist/dto/list-departments.dto.js.map +1 -0
  45. package/dist/dto/list-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-projects.dto.js +23 -0
  48. package/dist/dto/list-projects.dto.js.map +1 -0
  49. package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
  50. package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
  51. package/dist/dto/list-schedule-adjustments.dto.js +23 -0
  52. package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
  53. package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
  54. package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
  55. package/dist/dto/list-time-off-requests.dto.js +23 -0
  56. package/dist/dto/list-time-off-requests.dto.js.map +1 -0
  57. package/dist/dto/list-timesheets.dto.d.ts +5 -0
  58. package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
  59. package/dist/dto/list-timesheets.dto.js +23 -0
  60. package/dist/dto/list-timesheets.dto.js.map +1 -0
  61. package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
  62. package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
  63. package/dist/dto/reorder-collaborator-types.dto.js +25 -0
  64. package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
  65. package/dist/operations.service.d.ts +340 -271
  66. package/dist/operations.service.d.ts.map +1 -1
  67. package/dist/operations.service.js +1007 -1043
  68. package/dist/operations.service.js.map +1 -1
  69. package/dist/operations.service.spec.js +0 -22
  70. package/dist/operations.service.spec.js.map +1 -1
  71. package/hedhog/data/menu.yaml +0 -36
  72. package/hedhog/data/route.yaml +42 -73
  73. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
  74. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
  75. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
  76. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
  77. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
  78. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
  79. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
  80. package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
  81. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
  82. package/hedhog/frontend/app/approvals/page.tsx.ejs +842 -150
  83. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +445 -153
  84. package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
  85. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
  86. package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
  87. package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
  88. package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
  89. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +412 -147
  90. package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
  91. package/hedhog/frontend/app/timesheets/page.tsx.ejs +460 -365
  92. package/hedhog/frontend/messages/en.json +143 -14
  93. package/hedhog/frontend/messages/pt.json +192 -54
  94. package/hedhog/table/operations_contract.yaml +0 -9
  95. package/package.json +4 -4
  96. package/src/controllers/operations-approvals.controller.ts +9 -3
  97. package/src/controllers/operations-collaborators.controller.ts +15 -2
  98. package/src/controllers/operations-contracts.controller.ts +8 -92
  99. package/src/controllers/operations-org-structure.controller.ts +17 -4
  100. package/src/controllers/operations-projects.controller.ts +10 -4
  101. package/src/controllers/operations-timesheets.controller.ts +17 -8
  102. package/src/dto/list-approvals.dto.ts +12 -0
  103. package/src/dto/list-collaborator-types.dto.ts +7 -2
  104. package/src/dto/list-collaborators.dto.ts +4 -0
  105. package/src/dto/list-contracts.dto.ts +20 -0
  106. package/src/dto/list-departments.dto.ts +8 -0
  107. package/src/dto/list-projects.dto.ts +8 -0
  108. package/src/dto/list-schedule-adjustments.dto.ts +8 -0
  109. package/src/dto/list-time-off-requests.dto.ts +8 -0
  110. package/src/dto/list-timesheets.dto.ts +8 -0
  111. package/src/dto/reorder-collaborator-types.dto.ts +10 -0
  112. package/src/operations.service.spec.ts +0 -30
  113. package/src/operations.service.ts +1557 -1806
  114. package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
  115. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
  116. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
  117. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
  118. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
  119. package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
  120. package/hedhog/table/operations_contract_financial_term.yaml +0 -40
  121. package/hedhog/table/operations_contract_revision.yaml +0 -38
  122. package/hedhog/table/operations_contract_signature.yaml +0 -38
  123. package/hedhog/table/operations_contract_template.yaml +0 -58
@@ -1,526 +0,0 @@
1
- 'use client';
2
-
3
- import { EmptyState, Page } from '@/components/entity-list';
4
- import { Button } from '@/components/ui/button';
5
- import { FormActions } from '@/components/ui/form-actions';
6
- import { Input } from '@/components/ui/input';
7
- import { Label } from '@/components/ui/label';
8
- import {
9
- Select,
10
- SelectContent,
11
- SelectItem,
12
- SelectTrigger,
13
- SelectValue,
14
- } from '@/components/ui/select';
15
- import { Switch } from '@/components/ui/switch';
16
- import { Textarea } from '@/components/ui/textarea';
17
- import { useApp, useQuery } from '@hed-hog/next-app-provider';
18
- import { ArrowLeft, Files, Save } from 'lucide-react';
19
- import { useTranslations } from 'next-intl';
20
- import Link from 'next/link';
21
- import { useRouter } from 'next/navigation';
22
- import { useEffect, useState } from 'react';
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';
30
-
31
- type ContractTemplateFormState = {
32
- code: string;
33
- name: string;
34
- description: string;
35
- contractCategory: string;
36
- contractType: string;
37
- billingModel: string;
38
- signatureStatus: string;
39
- isActive: boolean;
40
- status: string;
41
- contentHtml: string;
42
- };
43
-
44
- function buildEmptyForm(): ContractTemplateFormState {
45
- return {
46
- code: '',
47
- name: '',
48
- description: '',
49
- contractCategory: 'client',
50
- contractType: 'service_agreement',
51
- billingModel: 'time_and_material',
52
- signatureStatus: 'not_started',
53
- isActive: true,
54
- status: 'active',
55
- contentHtml: '',
56
- };
57
- }
58
-
59
- function toFormState(
60
- template: OperationsContractTemplate
61
- ): ContractTemplateFormState {
62
- return {
63
- code: template.code ?? '',
64
- name: template.name ?? '',
65
- description: template.description ?? '',
66
- contractCategory: template.contractCategory ?? 'client',
67
- contractType: template.contractType ?? 'service_agreement',
68
- billingModel: template.billingModel ?? 'time_and_material',
69
- signatureStatus: template.signatureStatus ?? 'not_started',
70
- isActive: template.isActive ?? true,
71
- status: template.status ?? 'active',
72
- contentHtml: template.contentHtml ?? '',
73
- };
74
- }
75
-
76
- type ContractTemplateFormScreenProps = {
77
- templateId?: number;
78
- onSaved?: (template: OperationsContractTemplate) => void | Promise<void>;
79
- onCancel?: () => void;
80
- };
81
-
82
- export function ContractTemplateFormScreen({
83
- templateId,
84
- onSaved,
85
- onCancel,
86
- }: ContractTemplateFormScreenProps) {
87
- const t = useTranslations('operations.ContractTemplateFormPage');
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';
98
-
99
- const { data: template, isLoading } = useQuery<OperationsContractTemplate>({
100
- queryKey: [
101
- 'operations-contract-template-form',
102
- currentLocaleCode,
103
- templateId,
104
- ],
105
- enabled: Boolean(templateId) && access.isDirector,
106
- queryFn: () =>
107
- fetchOperations<OperationsContractTemplate>(
108
- request,
109
- `/operations/contract-templates/${templateId}`
110
- ),
111
- });
112
-
113
- useEffect(() => {
114
- if (template) {
115
- // eslint-disable-next-line react-hooks/set-state-in-effect
116
- setForm(toFormState(template));
117
- return;
118
- }
119
-
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
- };
138
-
139
- const onSubmit = async () => {
140
- if (!form.name.trim()) {
141
- showToastHandler?.('error', t('messages.requiredFields'));
142
- return;
143
- }
144
-
145
- const normalizedStatus = form.isActive
146
- ? form.status === 'inactive' || form.status === 'archived'
147
- ? 'active'
148
- : form.status
149
- : form.status === 'archived'
150
- ? 'archived'
151
- : 'inactive';
152
-
153
- const payload = {
154
- code: trimToNull(form.code)?.toUpperCase() ?? null,
155
- name: form.name.trim(),
156
- description: trimToNull(form.description),
157
- contractCategory: form.contractCategory,
158
- contractType: form.contractType,
159
- billingModel: form.billingModel,
160
- signatureStatus: form.signatureStatus,
161
- isActive: form.isActive,
162
- status: normalizedStatus,
163
- contentHtml: trimToNull(form.contentHtml),
164
- };
165
-
166
- try {
167
- const response = templateId
168
- ? await mutateOperations<OperationsContractTemplate>(
169
- request,
170
- `/operations/contract-templates/${templateId}`,
171
- 'PATCH',
172
- payload
173
- )
174
- : await mutateOperations<OperationsContractTemplate>(
175
- request,
176
- '/operations/contract-templates',
177
- 'POST',
178
- payload
179
- );
180
-
181
- showToastHandler?.(
182
- 'success',
183
- templateId ? t('messages.updateSuccess') : t('messages.createSuccess')
184
- );
185
-
186
- if (onSaved) {
187
- await onSaved(response);
188
- return;
189
- }
190
-
191
- router.push('/operations/contracts/templates');
192
- } catch {
193
- showToastHandler?.(
194
- 'error',
195
- templateId ? t('messages.updateError') : t('messages.createError')
196
- );
197
- }
198
- };
199
-
200
- const noAccessState = (
201
- <EmptyState
202
- icon={<Files className="size-12" />}
203
- title={commonT('states.noAccessTitle')}
204
- description={t('noAccessDescription')}
205
- actionLabel={commonT('actions.refresh')}
206
- onAction={() => router.refresh()}
207
- />
208
- );
209
-
210
- if (!access.isDirector && !access.isLoading) {
211
- if (isSheetMode) {
212
- return <div className="pt-4">{noAccessState}</div>;
213
- }
214
-
215
- return (
216
- <Page>
217
- <OperationsHeader
218
- title={t(templateId ? 'editTitle' : 'newTitle')}
219
- description={t('description')}
220
- current={t('breadcrumb')}
221
- />
222
- {noAccessState}
223
- </Page>
224
- );
225
- }
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>
493
- );
494
- }
495
-
496
- return (
497
- <Page>
498
- <OperationsHeader
499
- title={t(templateId ? 'editTitle' : 'newTitle')}
500
- description={t('description')}
501
- current={t('breadcrumb')}
502
- actions={
503
- <Button variant="outline" size="sm" asChild>
504
- <Link href="/operations/contracts/templates">
505
- <ArrowLeft className="size-4" />
506
- {commonT('actions.back')}
507
- </Link>
508
- </Button>
509
- }
510
- />
511
-
512
- {formBody}
513
-
514
- <div className="pt-2">
515
- <FormActions
516
- cancelLabel={commonT('actions.back')}
517
- onCancel={() => router.push('/operations/contracts/templates')}
518
- onSubmit={() => void onSubmit()}
519
- submitIcon={<Save className="size-4" />}
520
- submitLabel={commonT('actions.save')}
521
- submitSize="lg"
522
- />
523
- </div>
524
- </Page>
525
- );
526
- }