@hed-hog/operations 0.0.303 → 0.0.304

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 (166) hide show
  1. package/README.md +200 -43
  2. package/dist/controllers/operations-approvals.controller.d.ts +9 -0
  3. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -0
  4. package/dist/controllers/operations-approvals.controller.js +64 -0
  5. package/dist/controllers/operations-approvals.controller.js.map +1 -0
  6. package/dist/controllers/operations-collaborators.controller.d.ts +223 -0
  7. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -0
  8. package/dist/controllers/operations-collaborators.controller.js +96 -0
  9. package/dist/controllers/operations-collaborators.controller.js.map +1 -0
  10. package/dist/controllers/operations-contracts.controller.d.ts +683 -0
  11. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -0
  12. package/dist/controllers/operations-contracts.controller.js +198 -0
  13. package/dist/controllers/operations-contracts.controller.js.map +1 -0
  14. package/dist/controllers/operations-org-structure.controller.d.ts +108 -0
  15. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -0
  16. package/dist/controllers/operations-org-structure.controller.js +143 -0
  17. package/dist/controllers/operations-org-structure.controller.js.map +1 -0
  18. package/dist/controllers/operations-projects.controller.d.ts +169 -0
  19. package/dist/controllers/operations-projects.controller.d.ts.map +1 -0
  20. package/dist/controllers/operations-projects.controller.js +87 -0
  21. package/dist/controllers/operations-projects.controller.js.map +1 -0
  22. package/dist/controllers/operations-tasks.controller.d.ts +54 -0
  23. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -0
  24. package/dist/controllers/operations-tasks.controller.js +79 -0
  25. package/dist/controllers/operations-tasks.controller.js.map +1 -0
  26. package/dist/controllers/operations-timesheets.controller.d.ts +99 -0
  27. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -0
  28. package/dist/controllers/operations-timesheets.controller.js +154 -0
  29. package/dist/controllers/operations-timesheets.controller.js.map +1 -0
  30. package/dist/dto/create-collaborator-type.dto.d.ts +10 -0
  31. package/dist/dto/create-collaborator-type.dto.d.ts.map +1 -0
  32. package/dist/dto/create-collaborator-type.dto.js +56 -0
  33. package/dist/dto/create-collaborator-type.dto.js.map +1 -0
  34. package/dist/dto/create-collaborator.dto.d.ts +42 -0
  35. package/dist/dto/create-collaborator.dto.d.ts.map +1 -0
  36. package/dist/dto/create-collaborator.dto.js +228 -0
  37. package/dist/dto/create-collaborator.dto.js.map +1 -0
  38. package/dist/dto/create-schedule-adjustment-request.dto.d.ts +17 -0
  39. package/dist/dto/create-schedule-adjustment-request.dto.d.ts.map +1 -0
  40. package/dist/dto/create-schedule-adjustment-request.dto.js +89 -0
  41. package/dist/dto/create-schedule-adjustment-request.dto.js.map +1 -0
  42. package/dist/dto/create-task.dto.d.ts +8 -0
  43. package/dist/dto/create-task.dto.d.ts.map +1 -0
  44. package/dist/dto/create-task.dto.js +50 -0
  45. package/dist/dto/create-task.dto.js.map +1 -0
  46. package/dist/dto/create-time-off-request.dto.d.ts +9 -0
  47. package/dist/dto/create-time-off-request.dto.d.ts.map +1 -0
  48. package/dist/dto/create-time-off-request.dto.js +54 -0
  49. package/dist/dto/create-time-off-request.dto.js.map +1 -0
  50. package/dist/dto/create-timesheet-entry.dto.d.ts +12 -0
  51. package/dist/dto/create-timesheet-entry.dto.d.ts.map +1 -0
  52. package/dist/dto/create-timesheet-entry.dto.js +75 -0
  53. package/dist/dto/create-timesheet-entry.dto.js.map +1 -0
  54. package/dist/dto/list-collaborator-types.dto.d.ts +4 -0
  55. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -0
  56. package/dist/dto/list-collaborator-types.dto.js +29 -0
  57. package/dist/dto/list-collaborator-types.dto.js.map +1 -0
  58. package/dist/dto/list-collaborators.dto.d.ts +8 -0
  59. package/dist/dto/list-collaborators.dto.d.ts.map +1 -0
  60. package/dist/dto/list-collaborators.dto.js +42 -0
  61. package/dist/dto/list-collaborators.dto.js.map +1 -0
  62. package/dist/dto/list-project-options.dto.d.ts +4 -0
  63. package/dist/dto/list-project-options.dto.d.ts.map +1 -0
  64. package/dist/dto/list-project-options.dto.js +8 -0
  65. package/dist/dto/list-project-options.dto.js.map +1 -0
  66. package/dist/dto/list-tasks.dto.d.ts +7 -0
  67. package/dist/dto/list-tasks.dto.d.ts.map +1 -0
  68. package/dist/dto/list-tasks.dto.js +38 -0
  69. package/dist/dto/list-tasks.dto.js.map +1 -0
  70. package/dist/dto/list-timesheet-entries.dto.d.ts +10 -0
  71. package/dist/dto/list-timesheet-entries.dto.d.ts.map +1 -0
  72. package/dist/dto/list-timesheet-entries.dto.js +54 -0
  73. package/dist/dto/list-timesheet-entries.dto.js.map +1 -0
  74. package/dist/dto/update-collaborator-type.dto.d.ts +4 -0
  75. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -0
  76. package/dist/dto/update-collaborator-type.dto.js +8 -0
  77. package/dist/dto/update-collaborator-type.dto.js.map +1 -0
  78. package/dist/dto/update-collaborator.dto.d.ts +4 -0
  79. package/dist/dto/update-collaborator.dto.d.ts.map +1 -0
  80. package/dist/dto/update-collaborator.dto.js +8 -0
  81. package/dist/dto/update-collaborator.dto.js.map +1 -0
  82. package/dist/dto/update-task.dto.d.ts +8 -0
  83. package/dist/dto/update-task.dto.d.ts.map +1 -0
  84. package/dist/dto/update-task.dto.js +51 -0
  85. package/dist/dto/update-task.dto.js.map +1 -0
  86. package/dist/operations.controller.d.ts +0 -1045
  87. package/dist/operations.controller.d.ts.map +1 -1
  88. package/dist/operations.controller.js +0 -429
  89. package/dist/operations.controller.js.map +1 -1
  90. package/dist/operations.module.d.ts.map +1 -1
  91. package/dist/operations.module.js +23 -2
  92. package/dist/operations.module.js.map +1 -1
  93. package/dist/operations.service.d.ts +373 -8
  94. package/dist/operations.service.d.ts.map +1 -1
  95. package/dist/operations.service.js +1598 -111
  96. package/dist/operations.service.js.map +1 -1
  97. package/dist/operations.service.spec.js +315 -1
  98. package/dist/operations.service.spec.js.map +1 -1
  99. package/dist/services/shared/operations-access.service.d.ts +16 -0
  100. package/dist/services/shared/operations-access.service.d.ts.map +1 -0
  101. package/dist/services/shared/operations-access.service.js +48 -0
  102. package/dist/services/shared/operations-access.service.js.map +1 -0
  103. package/hedhog/data/dashboard.yaml +20 -0
  104. package/hedhog/data/dashboard_component.yaml +274 -0
  105. package/hedhog/data/dashboard_component_role.yaml +174 -0
  106. package/hedhog/data/dashboard_item.yaml +299 -0
  107. package/hedhog/data/dashboard_role.yaml +20 -0
  108. package/hedhog/data/menu.yaml +30 -13
  109. package/hedhog/data/operations_collaborator_type.yaml +76 -0
  110. package/hedhog/data/route.yaml +183 -0
  111. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +231 -0
  112. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +134 -49
  113. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +772 -93
  114. package/hedhog/frontend/app/_components/department-select-with-create.tsx.ejs +38 -16
  115. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +875 -632
  116. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +213 -0
  117. package/hedhog/frontend/app/_lib/api.ts.ejs +30 -1
  118. package/hedhog/frontend/app/_lib/types.ts.ejs +142 -39
  119. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +33 -2
  120. package/hedhog/frontend/app/approvals/page.tsx.ejs +116 -98
  121. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -0
  122. package/hedhog/frontend/app/collaborators/page.tsx.ejs +109 -68
  123. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +11 -9
  124. package/hedhog/frontend/app/departments/page.tsx.ejs +1 -1
  125. package/hedhog/frontend/app/projects/page.tsx.ejs +5 -1
  126. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +244 -120
  127. package/hedhog/frontend/app/team/page.tsx.ejs +15 -2
  128. package/hedhog/frontend/app/time-off/page.tsx.ejs +158 -82
  129. package/hedhog/frontend/app/timesheets/page.tsx.ejs +814 -357
  130. package/hedhog/frontend/messages/en.json +243 -51
  131. package/hedhog/frontend/messages/pt.json +458 -268
  132. package/hedhog/table/operations_collaborator.yaml +26 -13
  133. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -0
  134. package/hedhog/table/operations_collaborator_type.yaml +33 -0
  135. package/hedhog/table/operations_job_title.yaml +24 -0
  136. package/hedhog/table/operations_project_assignment.yaml +9 -0
  137. package/hedhog/table/operations_project_role.yaml +39 -0
  138. package/hedhog/table/operations_task.yaml +30 -0
  139. package/hedhog/table/operations_timesheet_entry.yaml +12 -0
  140. package/package.json +6 -6
  141. package/src/controllers/operations-approvals.controller.ts +24 -0
  142. package/src/controllers/operations-collaborators.controller.ts +60 -0
  143. package/src/controllers/operations-contracts.controller.ts +138 -0
  144. package/src/controllers/operations-org-structure.controller.ts +92 -0
  145. package/src/controllers/operations-projects.controller.ts +50 -0
  146. package/src/controllers/operations-tasks.controller.ts +52 -0
  147. package/src/controllers/operations-timesheets.controller.ts +100 -0
  148. package/src/dto/create-collaborator-type.dto.ts +43 -0
  149. package/src/dto/create-collaborator.dto.ts +223 -0
  150. package/src/dto/create-schedule-adjustment-request.dto.ts +91 -0
  151. package/src/dto/create-task.dto.ts +35 -0
  152. package/src/dto/create-time-off-request.dto.ts +53 -0
  153. package/src/dto/create-timesheet-entry.dto.ts +67 -0
  154. package/src/dto/list-collaborator-types.dto.ts +15 -0
  155. package/src/dto/list-collaborators.dto.ts +30 -0
  156. package/src/dto/list-project-options.dto.ts +3 -0
  157. package/src/dto/list-tasks.dto.ts +25 -0
  158. package/src/dto/list-timesheet-entries.dto.ts +40 -0
  159. package/src/dto/update-collaborator-type.dto.ts +3 -0
  160. package/src/dto/update-collaborator.dto.ts +3 -0
  161. package/src/dto/update-task.dto.ts +36 -0
  162. package/src/operations.controller.ts +1 -278
  163. package/src/operations.module.ts +23 -2
  164. package/src/operations.service.spec.ts +450 -0
  165. package/src/operations.service.ts +4641 -2163
  166. package/src/services/shared/operations-access.service.ts +52 -0
@@ -47,12 +47,18 @@ import { useTranslations } from 'next-intl';
47
47
  import Link from 'next/link';
48
48
  import { useRouter } from 'next/navigation';
49
49
  import { useEffect, useMemo, useState } from 'react';
50
- import { fetchOperations, mutateOperations } from '../_lib/api';
50
+ import {
51
+ fetchOperations,
52
+ getOperationsErrorMessage,
53
+ mutateOperations,
54
+ } from '../_lib/api';
51
55
  import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
52
56
  import type {
53
57
  OperationsCollaborator,
54
58
  OperationsCollaboratorDetails,
59
+ OperationsCollaboratorType,
55
60
  OperationsDepartment,
61
+ OperationsJobTitle,
56
62
  OperationsWeeklyScheduleDay,
57
63
  } from '../_lib/types';
58
64
  import {
@@ -60,6 +66,7 @@ import {
60
66
  formatDateRange,
61
67
  formatEnumLabel,
62
68
  formatHours,
69
+ formatWeekdayLabel,
63
70
  getStatusBadgeClass,
64
71
  } from '../_lib/utils/format';
65
72
  import { parseNumberInput, trimToNull } from '../_lib/utils/forms';
@@ -79,6 +86,50 @@ const weekdays = [
79
86
  ] as const;
80
87
 
81
88
  const SUPERVISOR_PAGE_SIZE = 10;
89
+ const COLLABORATOR_LEVEL_OPTIONS = [
90
+ 'junior',
91
+ 'mid',
92
+ 'senior',
93
+ 'coordinator',
94
+ 'manager',
95
+ ] as const;
96
+
97
+ type CollaboratorLevel = (typeof COLLABORATOR_LEVEL_OPTIONS)[number];
98
+
99
+ type SystemUserOption = {
100
+ id: number;
101
+ name: string;
102
+ };
103
+
104
+ function normalizeCollaboratorLevel(
105
+ value?: string | null
106
+ ): CollaboratorLevel | '' {
107
+ const normalizedValue = String(value ?? '')
108
+ .trim()
109
+ .toLowerCase()
110
+ .normalize('NFD')
111
+ .replace(/[\u0300-\u036f]/g, '');
112
+
113
+ switch (normalizedValue) {
114
+ case 'junior':
115
+ case 'junior level':
116
+ return 'junior';
117
+ case 'mid':
118
+ case 'middle':
119
+ case 'pleno':
120
+ return 'mid';
121
+ case 'senior':
122
+ return 'senior';
123
+ case 'coordinator':
124
+ case 'coordenador':
125
+ return 'coordinator';
126
+ case 'manager':
127
+ case 'gerente':
128
+ return 'manager';
129
+ default:
130
+ return '';
131
+ }
132
+ }
82
133
 
83
134
  function getPersonAvatarUrl(avatarId?: number | null) {
84
135
  return typeof avatarId === 'number' && avatarId > 0
@@ -120,14 +171,24 @@ function normalizeTimeValue(value?: string | null, fallback = '') {
120
171
  return fallback || String(value);
121
172
  }
122
173
 
174
+ const EQUITY_ENABLED_TYPE_SLUGS = new Set([
175
+ 'socio',
176
+ 'acionista',
177
+ 'administrador',
178
+ 'diretor',
179
+ 'conselheiro',
180
+ 'parceiro',
181
+ ]);
182
+
123
183
  type CollaboratorFormState = {
124
184
  userId: string;
125
185
  personId: string;
126
186
  code: string;
127
187
  displayName: string;
128
- collaboratorType: string;
188
+ collaboratorTypeId: string;
129
189
  departmentId: string;
130
190
  department: string;
191
+ jobTitleId: string;
131
192
  title: string;
132
193
  levelLabel: string;
133
194
  weeklyCapacityHours: string;
@@ -139,6 +200,14 @@ type CollaboratorFormState = {
139
200
  contractDescription: string;
140
201
  autoGenerateContractDraft: boolean;
141
202
  notes: string;
203
+ equityParticipation: {
204
+ participationType: string;
205
+ percentage: string;
206
+ votingPower: string;
207
+ startDate: string;
208
+ endDate: string;
209
+ notes: string;
210
+ };
142
211
  weeklySchedule: Array<{
143
212
  weekday: string;
144
213
  isWorkingDay: boolean;
@@ -164,9 +233,10 @@ function buildEmptyForm(): CollaboratorFormState {
164
233
  personId: '',
165
234
  code: '',
166
235
  displayName: '',
167
- collaboratorType: 'clt',
236
+ collaboratorTypeId: '',
168
237
  departmentId: '',
169
238
  department: '',
239
+ jobTitleId: '',
170
240
  title: '',
171
241
  levelLabel: '',
172
242
  weeklyCapacityHours: '40',
@@ -178,6 +248,14 @@ function buildEmptyForm(): CollaboratorFormState {
178
248
  contractDescription: '',
179
249
  autoGenerateContractDraft: true,
180
250
  notes: '',
251
+ equityParticipation: {
252
+ participationType: 'partner_quota_holder',
253
+ percentage: '',
254
+ votingPower: '',
255
+ startDate: '',
256
+ endDate: '',
257
+ notes: '',
258
+ },
181
259
  weeklySchedule: defaultSchedule(),
182
260
  };
183
261
  }
@@ -214,13 +292,16 @@ function toFormState(
214
292
  personId: collaborator.personId ? String(collaborator.personId) : '',
215
293
  code: collaborator.code ?? '',
216
294
  displayName: collaborator.displayName ?? '',
217
- collaboratorType: collaborator.collaboratorType ?? 'other',
295
+ collaboratorTypeId: collaborator.collaboratorTypeId
296
+ ? String(collaborator.collaboratorTypeId)
297
+ : '',
218
298
  departmentId: collaborator.departmentId
219
299
  ? String(collaborator.departmentId)
220
300
  : '',
221
301
  department: collaborator.department ?? '',
302
+ jobTitleId: collaborator.jobTitleId ? String(collaborator.jobTitleId) : '',
222
303
  title: collaborator.title ?? '',
223
- levelLabel: collaborator.levelLabel ?? '',
304
+ levelLabel: normalizeCollaboratorLevel(collaborator.levelLabel),
224
305
  weeklyCapacityHours:
225
306
  collaborator.weeklyCapacityHours !== null &&
226
307
  collaborator.weeklyCapacityHours !== undefined
@@ -240,6 +321,24 @@ function toFormState(
240
321
  contractDescription: collaborator.relatedContracts?.[0]?.description ?? '',
241
322
  autoGenerateContractDraft: true,
242
323
  notes: collaborator.notes ?? '',
324
+ equityParticipation: {
325
+ participationType:
326
+ collaborator.equityParticipation?.participationType ??
327
+ 'partner_quota_holder',
328
+ percentage:
329
+ collaborator.equityParticipation?.percentage !== null &&
330
+ collaborator.equityParticipation?.percentage !== undefined
331
+ ? String(collaborator.equityParticipation.percentage)
332
+ : '',
333
+ votingPower:
334
+ collaborator.equityParticipation?.votingPower !== null &&
335
+ collaborator.equityParticipation?.votingPower !== undefined
336
+ ? String(collaborator.equityParticipation.votingPower)
337
+ : '',
338
+ startDate: collaborator.equityParticipation?.startDate ?? '',
339
+ endDate: collaborator.equityParticipation?.endDate ?? '',
340
+ notes: collaborator.equityParticipation?.notes ?? '',
341
+ },
243
342
  weeklySchedule: normalizeSchedule(collaborator.weeklySchedule),
244
343
  };
245
344
  }
@@ -397,6 +496,166 @@ function SupervisorAutocomplete({
397
496
  );
398
497
  }
399
498
 
499
+ type SystemUserAutocompleteProps = {
500
+ label: string;
501
+ value: string;
502
+ options: SystemUserOption[];
503
+ placeholder: string;
504
+ emptyLabel: string;
505
+ onChange: (value: string) => void;
506
+ };
507
+
508
+ function SystemUserAutocomplete({
509
+ label,
510
+ value,
511
+ options,
512
+ placeholder,
513
+ emptyLabel,
514
+ onChange,
515
+ }: SystemUserAutocompleteProps) {
516
+ const commonT = useTranslations('operations.Common');
517
+ const [open, setOpen] = useState(false);
518
+ const [search, setSearch] = useState('');
519
+ const [visibleCount, setVisibleCount] = useState(SUPERVISOR_PAGE_SIZE);
520
+
521
+ const filteredOptions = useMemo(() => {
522
+ const normalizedSearch = search.trim().toLowerCase();
523
+
524
+ if (!normalizedSearch) {
525
+ return options;
526
+ }
527
+
528
+ return options.filter((option) =>
529
+ [option.name, `#${option.id}`]
530
+ .filter(Boolean)
531
+ .some((field) => String(field).toLowerCase().includes(normalizedSearch))
532
+ );
533
+ }, [options, search]);
534
+
535
+ const selectedOption =
536
+ options.find((option) => String(option.id) === value) ??
537
+ (value
538
+ ? {
539
+ id: Number(value),
540
+ name: `#${value}`,
541
+ }
542
+ : null);
543
+ const visibleOptions = filteredOptions.slice(0, visibleCount);
544
+
545
+ return (
546
+ <div className="grid gap-2">
547
+ <Label>{label}</Label>
548
+
549
+ <Popover
550
+ open={open}
551
+ onOpenChange={(nextOpen) => {
552
+ setOpen(nextOpen);
553
+ setVisibleCount(SUPERVISOR_PAGE_SIZE);
554
+
555
+ if (!nextOpen) {
556
+ setSearch('');
557
+ }
558
+ }}
559
+ >
560
+ <PopoverTrigger asChild>
561
+ <Button
562
+ type="button"
563
+ variant="outline"
564
+ role="combobox"
565
+ className="w-full justify-between overflow-hidden"
566
+ >
567
+ <span className="truncate text-left">
568
+ {selectedOption
569
+ ? `${selectedOption.name} (#${selectedOption.id})`
570
+ : emptyLabel}
571
+ </span>
572
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
573
+ </Button>
574
+ </PopoverTrigger>
575
+ <PopoverContent
576
+ className="p-0"
577
+ style={{ width: 'var(--radix-popover-trigger-width)' }}
578
+ >
579
+ <Command shouldFilter={false}>
580
+ <CommandInput
581
+ placeholder={placeholder}
582
+ value={search}
583
+ onValueChange={(nextValue) => {
584
+ setSearch(nextValue);
585
+ setVisibleCount(SUPERVISOR_PAGE_SIZE);
586
+ }}
587
+ />
588
+ <CommandList>
589
+ <CommandEmpty>
590
+ <div className="p-2 text-sm text-muted-foreground">
591
+ {commonT('states.emptyDescription')}
592
+ </div>
593
+ </CommandEmpty>
594
+
595
+ <CommandGroup>
596
+ <CommandItem
597
+ value="none"
598
+ onSelect={() => {
599
+ onChange('');
600
+ setOpen(false);
601
+ }}
602
+ >
603
+ {!value ? (
604
+ <Check className="mr-2 h-4 w-4" />
605
+ ) : (
606
+ <span className="mr-2 h-4 w-4" />
607
+ )}
608
+ {emptyLabel}
609
+ </CommandItem>
610
+
611
+ {visibleOptions.map((option) => (
612
+ <CommandItem
613
+ key={option.id}
614
+ value={`${option.name} #${option.id}`}
615
+ onSelect={() => {
616
+ onChange(String(option.id));
617
+ setOpen(false);
618
+ }}
619
+ >
620
+ {String(option.id) === value ? (
621
+ <Check className="mr-2 h-4 w-4" />
622
+ ) : (
623
+ <span className="mr-2 h-4 w-4" />
624
+ )}
625
+ <div className="min-w-0">
626
+ <div className="truncate">{option.name}</div>
627
+ <div className="truncate text-xs text-muted-foreground">
628
+ #{option.id}
629
+ </div>
630
+ </div>
631
+ </CommandItem>
632
+ ))}
633
+ </CommandGroup>
634
+
635
+ {filteredOptions.length > visibleCount ? (
636
+ <div className="border-t p-2">
637
+ <Button
638
+ type="button"
639
+ variant="ghost"
640
+ className="w-full"
641
+ onClick={() =>
642
+ setVisibleCount(
643
+ (current) => current + SUPERVISOR_PAGE_SIZE
644
+ )
645
+ }
646
+ >
647
+ {commonT('actions.loadMore')}
648
+ </Button>
649
+ </div>
650
+ ) : null}
651
+ </CommandList>
652
+ </Command>
653
+ </PopoverContent>
654
+ </Popover>
655
+ </div>
656
+ );
657
+ }
658
+
400
659
  type CollaboratorFormScreenProps = {
401
660
  collaboratorId?: number;
402
661
  onSaved?: (
@@ -418,23 +677,7 @@ export function CollaboratorFormScreen({
418
677
  const router = useRouter();
419
678
  const [form, setForm] = useState<CollaboratorFormState>(buildEmptyForm());
420
679
  const isSheetMode = Boolean(onCancel);
421
-
422
- const getCollaboratorTypeLabel = (value?: string | null) => {
423
- switch (value) {
424
- case 'clt':
425
- return t('options.collaboratorTypes.clt');
426
- case 'pj':
427
- return t('options.collaboratorTypes.pj');
428
- case 'freelancer':
429
- return t('options.collaboratorTypes.freelancer');
430
- case 'intern':
431
- return t('options.collaboratorTypes.intern');
432
- case 'other':
433
- return t('options.collaboratorTypes.other');
434
- default:
435
- return formatEnumLabel(value);
436
- }
437
- };
680
+ const isCreateMode = !collaboratorId;
438
681
 
439
682
  const getStatusLabel = (value?: string | null) => {
440
683
  switch (value) {
@@ -451,24 +694,72 @@ export function CollaboratorFormScreen({
451
694
  }
452
695
  };
453
696
 
454
- const { data: collaborator, isLoading: isLoadingCollaborator } =
455
- useQuery<OperationsCollaboratorDetails>({
456
- queryKey: [
457
- 'operations-collaborator-form',
458
- currentLocaleCode,
459
- collaboratorId,
460
- ],
461
- enabled: Boolean(collaboratorId),
462
- queryFn: () =>
463
- fetchOperations<OperationsCollaboratorDetails>(
464
- request,
465
- `/operations/collaborators/${collaboratorId}`
466
- ),
467
- });
468
-
469
- const { data: collaborators = [] } = useQuery<OperationsCollaborator[]>({
697
+ const {
698
+ data: collaborator,
699
+ isLoading: isLoadingCollaborator,
700
+ refetch: refetchCollaborator,
701
+ } = useQuery<OperationsCollaboratorDetails>({
702
+ queryKey: [
703
+ 'operations-collaborator-form',
704
+ currentLocaleCode,
705
+ collaboratorId,
706
+ ],
707
+ enabled: Boolean(collaboratorId),
708
+ staleTime: 0,
709
+ refetchOnMount: 'always',
710
+ queryFn: () =>
711
+ fetchOperations<OperationsCollaboratorDetails>(
712
+ request,
713
+ `/operations/collaborators/${collaboratorId}`
714
+ ),
715
+ });
716
+
717
+ const { data: systemUsers = [] } = useQuery<SystemUserOption[]>({
718
+ queryKey: ['operations-collaborator-form-system-users', currentLocaleCode],
719
+ enabled: access.isDirector,
720
+ staleTime: 0,
721
+ refetchOnMount: 'always',
722
+ queryFn: async () => {
723
+ const response = await request<
724
+ SystemUserOption[] | { data?: SystemUserOption[] }
725
+ >({
726
+ url: '/person/owner-options',
727
+ method: 'GET',
728
+ });
729
+
730
+ const options = Array.isArray(response.data)
731
+ ? response.data
732
+ : response.data?.data || [];
733
+
734
+ return [...options].sort((left, right) =>
735
+ left.name.localeCompare(right.name)
736
+ );
737
+ },
738
+ });
739
+
740
+ const {
741
+ data: collaboratorTypes = [],
742
+ refetch: refetchCollaboratorTypes,
743
+ isLoading: isLoadingCollaboratorTypes,
744
+ } = useQuery<OperationsCollaboratorType[]>({
745
+ queryKey: ['operations-collaborator-types', currentLocaleCode],
746
+ enabled: access.isDirector,
747
+ staleTime: 0,
748
+ refetchOnMount: 'always',
749
+ queryFn: () =>
750
+ fetchOperations<OperationsCollaboratorType[]>(
751
+ request,
752
+ '/operations/collaborator-types?active=true'
753
+ ),
754
+ });
755
+
756
+ const { data: collaborators = [], refetch: refetchCollaborators } = useQuery<
757
+ OperationsCollaborator[]
758
+ >({
470
759
  queryKey: ['operations-collaborator-form-supervisors', currentLocaleCode],
471
760
  enabled: access.isDirector,
761
+ staleTime: 0,
762
+ refetchOnMount: 'always',
472
763
  queryFn: () =>
473
764
  fetchOperations<OperationsCollaborator[]>(
474
765
  request,
@@ -476,9 +767,13 @@ export function CollaboratorFormScreen({
476
767
  ),
477
768
  });
478
769
 
479
- const { data: departments = [] } = useQuery<OperationsDepartment[]>({
770
+ const { data: departments = [], refetch: refetchDepartments } = useQuery<
771
+ OperationsDepartment[]
772
+ >({
480
773
  queryKey: ['operations-collaborator-form-departments', currentLocaleCode],
481
774
  enabled: access.isDirector,
775
+ staleTime: 0,
776
+ refetchOnMount: 'always',
482
777
  queryFn: () =>
483
778
  fetchOperations<OperationsDepartment[]>(
484
779
  request,
@@ -486,6 +781,17 @@ export function CollaboratorFormScreen({
486
781
  ),
487
782
  });
488
783
 
784
+ const { data: jobTitles = [], refetch: refetchJobTitles } = useQuery<
785
+ OperationsJobTitle[]
786
+ >({
787
+ queryKey: ['operations-collaborator-form-job-titles', currentLocaleCode],
788
+ enabled: access.isDirector,
789
+ staleTime: 0,
790
+ refetchOnMount: 'always',
791
+ queryFn: () =>
792
+ fetchOperations<OperationsJobTitle[]>(request, '/operations/job-titles'),
793
+ });
794
+
489
795
  useEffect(() => {
490
796
  if (collaborator) {
491
797
  // eslint-disable-next-line react-hooks/set-state-in-effect
@@ -493,6 +799,67 @@ export function CollaboratorFormScreen({
493
799
  }
494
800
  }, [collaborator]);
495
801
 
802
+ useEffect(() => {
803
+ if (
804
+ collaboratorId ||
805
+ form.collaboratorTypeId ||
806
+ !collaboratorTypes.length
807
+ ) {
808
+ return;
809
+ }
810
+
811
+ const defaultType =
812
+ collaboratorTypes.find((item) => item.slug === 'clt') ??
813
+ collaboratorTypes[0];
814
+
815
+ if (!defaultType) {
816
+ return;
817
+ }
818
+
819
+ // eslint-disable-next-line react-hooks/set-state-in-effect
820
+ setForm((current) => ({
821
+ ...current,
822
+ collaboratorTypeId: String(defaultType.id),
823
+ }));
824
+ }, [collaboratorId, collaboratorTypes, form.collaboratorTypeId]);
825
+
826
+ const selectedCollaboratorType = useMemo(
827
+ () =>
828
+ collaboratorTypes.find(
829
+ (item) => String(item.id) === String(form.collaboratorTypeId)
830
+ ) ?? null,
831
+ [collaboratorTypes, form.collaboratorTypeId]
832
+ );
833
+
834
+ const shouldShowEquitySection =
835
+ !isCreateMode &&
836
+ Boolean(
837
+ selectedCollaboratorType?.slug &&
838
+ EQUITY_ENABLED_TYPE_SLUGS.has(selectedCollaboratorType.slug)
839
+ );
840
+
841
+ const getCollaboratorTypeLabel = (
842
+ value?: string | null,
843
+ fallbackName?: string | null
844
+ ) => {
845
+ const normalizedValue = String(value ?? '').trim();
846
+
847
+ if (fallbackName) {
848
+ return fallbackName;
849
+ }
850
+
851
+ const matchedType = collaboratorTypes.find(
852
+ (item) =>
853
+ item.slug === normalizedValue || String(item.id) === normalizedValue
854
+ );
855
+
856
+ if (matchedType?.name) {
857
+ return matchedType.name;
858
+ }
859
+
860
+ return formatEnumLabel(normalizedValue);
861
+ };
862
+
496
863
  const departmentOptions = useMemo(() => {
497
864
  const selectedDepartment = trimToNull(form.department);
498
865
  const options: Array<{
@@ -529,6 +896,68 @@ export function CollaboratorFormScreen({
529
896
  return options.sort((left, right) => left.name.localeCompare(right.name));
530
897
  }, [departments, form.department, form.departmentId]);
531
898
 
899
+ const titleOptions = useMemo(() => {
900
+ const selectedTitle = trimToNull(form.title);
901
+ const options: Array<{
902
+ id?: number | null;
903
+ name: string;
904
+ code?: string | null;
905
+ description?: string | null;
906
+ }> = jobTitles
907
+ .filter((item) => item.status === 'active' || item.name === selectedTitle)
908
+ .map((item) => ({
909
+ id: item.id,
910
+ name: item.name,
911
+ code: item.code ?? null,
912
+ description: item.description ?? null,
913
+ }));
914
+
915
+ if (selectedTitle) {
916
+ const alreadyIncluded = options.some(
917
+ (item) => item.name === selectedTitle
918
+ );
919
+
920
+ if (!alreadyIncluded) {
921
+ options.push({
922
+ id: form.jobTitleId ? Number(form.jobTitleId) : undefined,
923
+ name: selectedTitle,
924
+ code: null,
925
+ description: null,
926
+ });
927
+ }
928
+ }
929
+
930
+ return options.sort((left, right) => left.name.localeCompare(right.name));
931
+ }, [jobTitles, form.title, form.jobTitleId]);
932
+
933
+ const createJobTitle = async (jobTitleName: string) => {
934
+ try {
935
+ const createdJobTitle = await mutateOperations<OperationsJobTitle>(
936
+ request,
937
+ '/operations/job-titles',
938
+ 'POST',
939
+ {
940
+ name: jobTitleName,
941
+ }
942
+ );
943
+
944
+ await refetchJobTitles();
945
+
946
+ return {
947
+ id: createdJobTitle.id,
948
+ name: createdJobTitle.name,
949
+ code: createdJobTitle.code ?? null,
950
+ description: createdJobTitle.description ?? null,
951
+ };
952
+ } catch (error) {
953
+ showToastHandler?.(
954
+ 'error',
955
+ getOperationsErrorMessage(error, 'Unable to create the job title.')
956
+ );
957
+ return null;
958
+ }
959
+ };
960
+
532
961
  const updateScheduleDay = (
533
962
  weekday: string,
534
963
  patch: Partial<CollaboratorFormState['weeklySchedule'][number]>
@@ -572,15 +1001,23 @@ export function CollaboratorFormScreen({
572
1001
  }
573
1002
 
574
1003
  const departmentId = parseNumberInput(form.departmentId);
1004
+ const jobTitleId = parseNumberInput(form.jobTitleId);
1005
+ const collaboratorTypeId = parseNumberInput(form.collaboratorTypeId);
1006
+
1007
+ if (!collaboratorTypeId) {
1008
+ showToastHandler?.('error', t('messages.collaboratorTypeRequired'));
1009
+ return;
1010
+ }
575
1011
 
576
1012
  const payload = {
577
1013
  userId: userId ?? undefined,
578
1014
  personId: personId ?? undefined,
579
1015
  code: trimToNull(form.code) ?? undefined,
580
1016
  displayName: trimToNull(form.displayName),
581
- collaboratorType: form.collaboratorType,
1017
+ collaboratorTypeId,
582
1018
  departmentId: departmentId ?? undefined,
583
1019
  department: departmentId ? undefined : trimToNull(form.department),
1020
+ jobTitleId: jobTitleId ?? undefined,
584
1021
  title: trimToNull(form.title),
585
1022
  levelLabel: trimToNull(form.levelLabel),
586
1023
  weeklyCapacityHours: parseNumberInput(form.weeklyCapacityHours),
@@ -595,6 +1032,16 @@ export function CollaboratorFormScreen({
595
1032
  contractDescription: trimToNull(form.contractDescription),
596
1033
  autoGenerateContractDraft: form.autoGenerateContractDraft,
597
1034
  notes: trimToNull(form.notes),
1035
+ equityParticipation: shouldShowEquitySection
1036
+ ? {
1037
+ participationType: form.equityParticipation.participationType,
1038
+ percentage: parseNumberInput(form.equityParticipation.percentage),
1039
+ votingPower: parseNumberInput(form.equityParticipation.votingPower),
1040
+ startDate: trimToNull(form.equityParticipation.startDate),
1041
+ endDate: trimToNull(form.equityParticipation.endDate),
1042
+ notes: trimToNull(form.equityParticipation.notes),
1043
+ }
1044
+ : null,
598
1045
  weeklySchedule: form.weeklySchedule.map((day) => ({
599
1046
  weekday: day.weekday,
600
1047
  isWorkingDay: day.isWorkingDay,
@@ -623,6 +1070,16 @@ export function CollaboratorFormScreen({
623
1070
  payload
624
1071
  );
625
1072
 
1073
+ setForm(toFormState(response));
1074
+
1075
+ await Promise.all([
1076
+ refetchCollaborator(),
1077
+ refetchCollaboratorTypes(),
1078
+ refetchDepartments(),
1079
+ refetchCollaborators(),
1080
+ refetchJobTitles(),
1081
+ ]);
1082
+
626
1083
  showToastHandler?.(
627
1084
  'success',
628
1085
  collaboratorId
@@ -636,10 +1093,13 @@ export function CollaboratorFormScreen({
636
1093
  }
637
1094
 
638
1095
  router.push(`/operations/collaborators/${response.id}`);
639
- } catch {
1096
+ } catch (error) {
640
1097
  showToastHandler?.(
641
1098
  'error',
642
- collaboratorId ? t('messages.updateError') : t('messages.createError')
1099
+ getOperationsErrorMessage(
1100
+ error,
1101
+ collaboratorId ? t('messages.updateError') : t('messages.createError')
1102
+ )
643
1103
  );
644
1104
  }
645
1105
  };
@@ -690,7 +1150,11 @@ export function CollaboratorFormScreen({
690
1150
  className={getStatusBadgeClass(collaborator.status)}
691
1151
  />
692
1152
  <span className="inline-flex items-center rounded-full bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary">
693
- {getCollaboratorTypeLabel(collaborator.collaboratorType)}
1153
+ {getCollaboratorTypeLabel(
1154
+ collaborator.collaboratorTypeSlug ??
1155
+ collaborator.collaboratorType,
1156
+ collaborator.collaboratorTypeName
1157
+ )}
694
1158
  </span>
695
1159
  </div>
696
1160
  </div>
@@ -760,7 +1224,7 @@ export function CollaboratorFormScreen({
760
1224
  </p>
761
1225
  </div>
762
1226
  <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
763
- <div className="space-y-2 md:col-span-2 xl:col-span-4">
1227
+ <div className="space-y-2 md:col-span-2">
764
1228
  <PersonSelectWithCreate
765
1229
  label={t('fields.person')}
766
1230
  entityLabel={t('fields.personEntityLabel')}
@@ -776,33 +1240,63 @@ export function CollaboratorFormScreen({
776
1240
  }
777
1241
  />
778
1242
  </div>
779
- <div className="space-y-2">
780
- <Label>{t('fields.code')}</Label>
781
- <Input
782
- className="h-10"
783
- value={form.code}
784
- placeholder="COL-001"
785
- onChange={(event) =>
1243
+ <div className="space-y-2 md:col-span-2 xl:col-span-2">
1244
+ <SystemUserAutocomplete
1245
+ label={t('fields.userIdOptional')}
1246
+ value={form.userId}
1247
+ options={systemUsers}
1248
+ placeholder={t('placeholders.userIdOptional')}
1249
+ emptyLabel={commonT('labels.notAssigned')}
1250
+ onChange={(value) =>
786
1251
  setForm((current) => ({
787
1252
  ...current,
788
- code: event.target.value,
1253
+ userId: value,
789
1254
  }))
790
1255
  }
791
1256
  />
1257
+ <p className="text-xs text-muted-foreground">
1258
+ {t('fields.userIdDescription')}
1259
+ </p>
792
1260
  </div>
793
1261
  <div className="space-y-2">
794
- <Label>{t('fields.levelLabel')}</Label>
1262
+ <Label>{t('fields.code')}</Label>
795
1263
  <Input
796
1264
  className="h-10"
797
- value={form.levelLabel}
1265
+ value={form.code}
1266
+ placeholder="COL-001"
798
1267
  onChange={(event) =>
799
1268
  setForm((current) => ({
800
1269
  ...current,
801
- levelLabel: event.target.value,
1270
+ code: event.target.value,
802
1271
  }))
803
1272
  }
804
1273
  />
805
1274
  </div>
1275
+ {!isCreateMode ? (
1276
+ <div className="space-y-2">
1277
+ <Label>{t('fields.levelLabel')}</Label>
1278
+ <Select
1279
+ value={form.levelLabel}
1280
+ onValueChange={(value) =>
1281
+ setForm((current) => ({
1282
+ ...current,
1283
+ levelLabel: value,
1284
+ }))
1285
+ }
1286
+ >
1287
+ <SelectTrigger className="w-full">
1288
+ <SelectValue placeholder={t('placeholders.levelLabel')} />
1289
+ </SelectTrigger>
1290
+ <SelectContent>
1291
+ {COLLABORATOR_LEVEL_OPTIONS.map((level) => (
1292
+ <SelectItem key={level} value={level}>
1293
+ {t(`options.levels.${level}`)}
1294
+ </SelectItem>
1295
+ ))}
1296
+ </SelectContent>
1297
+ </Select>
1298
+ </div>
1299
+ ) : null}
806
1300
  <div className="space-y-2 xl:col-span-1">
807
1301
  <DepartmentSelectWithCreate
808
1302
  label={t('fields.department')}
@@ -821,14 +1315,19 @@ export function CollaboratorFormScreen({
821
1315
  />
822
1316
  </div>
823
1317
  <div className="space-y-2 xl:col-span-1">
824
- <Label>{t('fields.title')}</Label>
825
- <Input
826
- className="h-10"
1318
+ <DepartmentSelectWithCreate
1319
+ label={t('fields.title')}
827
1320
  value={form.title}
828
- onChange={(event) =>
1321
+ options={titleOptions}
1322
+ onCreate={createJobTitle}
1323
+ selectPlaceholder={t('placeholders.title')}
1324
+ createDescription={t('fields.titleDescription')}
1325
+ createPlaceholder={t('placeholders.titleCreate')}
1326
+ onChange={(titleOption) =>
829
1327
  setForm((current) => ({
830
1328
  ...current,
831
- title: event.target.value,
1329
+ jobTitleId: titleOption.id ? String(titleOption.id) : '',
1330
+ title: titleOption.name,
832
1331
  }))
833
1332
  }
834
1333
  />
@@ -844,42 +1343,44 @@ export function CollaboratorFormScreen({
844
1343
  {t('sections.employmentInfo')}
845
1344
  </h3>
846
1345
  <p className="text-[11px] text-muted-foreground/80">
847
- {t('sections.employmentInfoDescription')}
1346
+ {isCreateMode
1347
+ ? t('sections.employmentInfoCreateDescription')
1348
+ : t('sections.employmentInfoDescription')}
848
1349
  </p>
849
1350
  </div>
850
1351
  <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
851
- <div className="space-y-2">
1352
+ <div className="space-y-2 xl:col-span-1">
852
1353
  <label className="text-sm font-medium">
853
1354
  {t('fields.collaboratorType')}
854
1355
  </label>
1356
+
855
1357
  <Select
856
- value={form.collaboratorType}
1358
+ value={form.collaboratorTypeId}
857
1359
  onValueChange={(value) =>
858
1360
  setForm((current) => ({
859
1361
  ...current,
860
- collaboratorType: value,
1362
+ collaboratorTypeId: value,
861
1363
  }))
862
1364
  }
863
1365
  >
864
1366
  <SelectTrigger className="w-full">
865
- <SelectValue />
1367
+ <SelectValue
1368
+ placeholder={
1369
+ isLoadingCollaboratorTypes
1370
+ ? t('states.loadingCollaboratorTypes')
1371
+ : t('placeholders.collaboratorType')
1372
+ }
1373
+ />
866
1374
  </SelectTrigger>
867
1375
  <SelectContent>
868
- <SelectItem value="clt">
869
- {t('options.collaboratorTypes.clt')}
870
- </SelectItem>
871
- <SelectItem value="pj">
872
- {t('options.collaboratorTypes.pj')}
873
- </SelectItem>
874
- <SelectItem value="freelancer">
875
- {t('options.collaboratorTypes.freelancer')}
876
- </SelectItem>
877
- <SelectItem value="intern">
878
- {t('options.collaboratorTypes.intern')}
879
- </SelectItem>
880
- <SelectItem value="other">
881
- {t('options.collaboratorTypes.other')}
882
- </SelectItem>
1376
+ {collaboratorTypes.map((collaboratorType) => (
1377
+ <SelectItem
1378
+ key={collaboratorType.id}
1379
+ value={String(collaboratorType.id)}
1380
+ >
1381
+ {collaboratorType.name}
1382
+ </SelectItem>
1383
+ ))}
883
1384
  </SelectContent>
884
1385
  </Select>
885
1386
  </div>
@@ -942,10 +1443,187 @@ export function CollaboratorFormScreen({
942
1443
  }
943
1444
  />
944
1445
  </div>
1446
+ {isCreateMode ? (
1447
+ <>
1448
+ <div className="space-y-2">
1449
+ <label className="text-sm font-medium">
1450
+ {t('fields.weeklyCapacityHours')}
1451
+ </label>
1452
+ <Input
1453
+ type="number"
1454
+ step="0.5"
1455
+ value={form.weeklyCapacityHours}
1456
+ onChange={(event) =>
1457
+ setForm((current) => ({
1458
+ ...current,
1459
+ weeklyCapacityHours: event.target.value,
1460
+ }))
1461
+ }
1462
+ />
1463
+ </div>
1464
+ <div className="space-y-2">
1465
+ <label className="text-sm font-medium">
1466
+ {t('fields.compensationAmount')}
1467
+ </label>
1468
+ <InputMoney
1469
+ step="0.01"
1470
+ value={form.compensationAmount}
1471
+ onChange={(event) =>
1472
+ setForm((current) => ({
1473
+ ...current,
1474
+ compensationAmount: event.target.value,
1475
+ }))
1476
+ }
1477
+ />
1478
+ </div>
1479
+ </>
1480
+ ) : null}
945
1481
  </div>
946
1482
  </div>
947
1483
  );
948
1484
 
1485
+ const equitySection = shouldShowEquitySection ? (
1486
+ <div className="space-y-2">
1487
+ <div className="space-y-0.5">
1488
+ <h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
1489
+ {t('sections.equity')}
1490
+ </h3>
1491
+ <p className="text-[11px] text-muted-foreground/80">
1492
+ {t('sections.equityDescription')}
1493
+ </p>
1494
+ </div>
1495
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
1496
+ <div className="space-y-2 xl:col-span-2">
1497
+ <label className="text-sm font-medium">
1498
+ {t('fields.equityParticipationType')}
1499
+ </label>
1500
+ <Select
1501
+ value={form.equityParticipation.participationType}
1502
+ onValueChange={(value) =>
1503
+ setForm((current) => ({
1504
+ ...current,
1505
+ equityParticipation: {
1506
+ ...current.equityParticipation,
1507
+ participationType: value,
1508
+ },
1509
+ }))
1510
+ }
1511
+ >
1512
+ <SelectTrigger className="w-full">
1513
+ <SelectValue />
1514
+ </SelectTrigger>
1515
+ <SelectContent>
1516
+ <SelectItem value="partner_quota_holder">
1517
+ {t('options.equityParticipationTypes.partner_quota_holder')}
1518
+ </SelectItem>
1519
+ <SelectItem value="common_shareholder">
1520
+ {t('options.equityParticipationTypes.common_shareholder')}
1521
+ </SelectItem>
1522
+ <SelectItem value="preferred_shareholder">
1523
+ {t('options.equityParticipationTypes.preferred_shareholder')}
1524
+ </SelectItem>
1525
+ <SelectItem value="administrator">
1526
+ {t('options.equityParticipationTypes.administrator')}
1527
+ </SelectItem>
1528
+ <SelectItem value="other">
1529
+ {t('options.equityParticipationTypes.other')}
1530
+ </SelectItem>
1531
+ </SelectContent>
1532
+ </Select>
1533
+ </div>
1534
+ <div className="space-y-2">
1535
+ <Label>{t('fields.equityPercentage')}</Label>
1536
+ <Input
1537
+ type="number"
1538
+ min="0"
1539
+ max="100"
1540
+ step="0.0001"
1541
+ placeholder={t('placeholders.equityPercentage')}
1542
+ value={form.equityParticipation.percentage}
1543
+ onChange={(event) =>
1544
+ setForm((current) => ({
1545
+ ...current,
1546
+ equityParticipation: {
1547
+ ...current.equityParticipation,
1548
+ percentage: event.target.value,
1549
+ },
1550
+ }))
1551
+ }
1552
+ />
1553
+ </div>
1554
+ <div className="space-y-2">
1555
+ <Label>{t('fields.votingPower')}</Label>
1556
+ <Input
1557
+ type="number"
1558
+ min="0"
1559
+ max="100"
1560
+ step="0.0001"
1561
+ placeholder={t('placeholders.votingPower')}
1562
+ value={form.equityParticipation.votingPower}
1563
+ onChange={(event) =>
1564
+ setForm((current) => ({
1565
+ ...current,
1566
+ equityParticipation: {
1567
+ ...current.equityParticipation,
1568
+ votingPower: event.target.value,
1569
+ },
1570
+ }))
1571
+ }
1572
+ />
1573
+ </div>
1574
+ <div className="space-y-2">
1575
+ <Label>{commonT('labels.startDate')}</Label>
1576
+ <Input
1577
+ type="date"
1578
+ value={form.equityParticipation.startDate}
1579
+ onChange={(event) =>
1580
+ setForm((current) => ({
1581
+ ...current,
1582
+ equityParticipation: {
1583
+ ...current.equityParticipation,
1584
+ startDate: event.target.value,
1585
+ },
1586
+ }))
1587
+ }
1588
+ />
1589
+ </div>
1590
+ <div className="space-y-2">
1591
+ <Label>{commonT('labels.endDate')}</Label>
1592
+ <Input
1593
+ type="date"
1594
+ value={form.equityParticipation.endDate}
1595
+ onChange={(event) =>
1596
+ setForm((current) => ({
1597
+ ...current,
1598
+ equityParticipation: {
1599
+ ...current.equityParticipation,
1600
+ endDate: event.target.value,
1601
+ },
1602
+ }))
1603
+ }
1604
+ />
1605
+ </div>
1606
+ <div className="space-y-2 md:col-span-2 xl:col-span-4">
1607
+ <Label>{t('fields.equityNotes')}</Label>
1608
+ <Textarea
1609
+ rows={4}
1610
+ placeholder={t('placeholders.equityNotes')}
1611
+ value={form.equityParticipation.notes}
1612
+ onChange={(event) =>
1613
+ setForm((current) => ({
1614
+ ...current,
1615
+ equityParticipation: {
1616
+ ...current.equityParticipation,
1617
+ notes: event.target.value,
1618
+ },
1619
+ }))
1620
+ }
1621
+ />
1622
+ </div>
1623
+ </div>
1624
+ </div>
1625
+ ) : null;
1626
+
949
1627
  const supervisorSection = (
950
1628
  <div className="space-y-2">
951
1629
  <div className="space-y-0.5">
@@ -1070,7 +1748,7 @@ export function CollaboratorFormScreen({
1070
1748
  >
1071
1749
  <div className="space-y-0.5 md:pr-1">
1072
1750
  <div className="text-sm font-medium leading-none">
1073
- {formatEnumLabel(day.weekday)}
1751
+ {formatWeekdayLabel(day.weekday, currentLocaleCode)}
1074
1752
  </div>
1075
1753
  <div className="text-[10px] leading-none text-muted-foreground">
1076
1754
  {day.isWorkingDay
@@ -1309,13 +1987,13 @@ export function CollaboratorFormScreen({
1309
1987
  href={`/operations/contracts?edit=${contract.id}`}
1310
1988
  className="flex cursor-pointer items-center justify-between gap-3 rounded-lg border px-3 py-2.5 transition-colors hover:bg-muted/20"
1311
1989
  >
1312
- <div className="min-w-0">
1313
- <div className="truncate font-medium">
1314
- {contract.name || contract.code}
1315
- </div>
1316
- <div className="truncate text-xs text-muted-foreground">
1317
- {[
1318
- contract.code,
1990
+ <div className="min-w-0">
1991
+ <div className="truncate font-medium">
1992
+ {contract.name || contract.code}
1993
+ </div>
1994
+ <div className="truncate text-xs text-muted-foreground">
1995
+ {[
1996
+ contract.code,
1319
1997
  formatEnumLabel(contract.contractCategory),
1320
1998
  ]
1321
1999
  .filter(Boolean)
@@ -1388,6 +2066,7 @@ export function CollaboratorFormScreen({
1388
2066
  <div className="space-y-4">
1389
2067
  {basicInfoSection}
1390
2068
  {employmentInfoSection}
2069
+ {!isCreateMode ? equitySection : null}
1391
2070
  {supervisorSection}
1392
2071
  </div>
1393
2072
  );
@@ -1395,15 +2074,15 @@ export function CollaboratorFormScreen({
1395
2074
  const formContent = isSheetMode ? (
1396
2075
  <div className="space-y-4 px-4">
1397
2076
  {profileContent}
1398
- {contractSection}
1399
- {scheduleSection}
2077
+ {!isCreateMode ? contractSection : null}
2078
+ {!isCreateMode ? scheduleSection : null}
1400
2079
  {activitySection}
1401
2080
  </div>
1402
2081
  ) : (
1403
2082
  <div className="space-y-4 px-4">
1404
2083
  {profileContent}
1405
- {contractSection}
1406
- {scheduleSection}
2084
+ {!isCreateMode ? contractSection : null}
2085
+ {!isCreateMode ? scheduleSection : null}
1407
2086
  </div>
1408
2087
  );
1409
2088
 
@@ -1436,7 +2115,7 @@ export function CollaboratorFormScreen({
1436
2115
  <Page>
1437
2116
  <OperationsHeader
1438
2117
  title={t(collaboratorId ? 'editTitle' : 'newTitle')}
1439
- description={t('description')}
2118
+ description={isCreateMode ? t('descriptionCreate') : t('description')}
1440
2119
  current={t('breadcrumb')}
1441
2120
  actions={
1442
2121
  <div className="flex gap-2">