@hed-hog/operations 0.0.303 → 0.0.305

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 (178) 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 +184 -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 +85 -0
  23. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -0
  24. package/dist/controllers/operations-tasks.controller.js +90 -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 +14 -0
  43. package/dist/dto/create-task.dto.d.ts.map +1 -0
  44. package/dist/dto/create-task.dto.js +83 -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 +14 -0
  83. package/dist/dto/update-task.dto.d.ts.map +1 -0
  84. package/dist/dto/update-task.dto.js +84 -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 +429 -8
  94. package/dist/operations.service.d.ts.map +1 -1
  95. package/dist/operations.service.js +1931 -165
  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 +196 -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 +125 -40
  113. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +740 -106
  114. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  115. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  116. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  117. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  118. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  119. package/hedhog/frontend/app/_components/department-select-with-create.tsx.ejs +38 -16
  120. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  121. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +1017 -649
  122. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  123. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  124. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +213 -0
  125. package/hedhog/frontend/app/_lib/api.ts.ejs +30 -1
  126. package/hedhog/frontend/app/_lib/types.ts.ejs +147 -39
  127. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +40 -9
  128. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  129. package/hedhog/frontend/app/approvals/page.tsx.ejs +116 -98
  130. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -0
  131. package/hedhog/frontend/app/collaborators/page.tsx.ejs +116 -72
  132. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  133. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +11 -9
  134. package/hedhog/frontend/app/departments/page.tsx.ejs +1 -1
  135. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  136. package/hedhog/frontend/app/projects/page.tsx.ejs +364 -133
  137. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +244 -120
  138. package/hedhog/frontend/app/team/page.tsx.ejs +15 -2
  139. package/hedhog/frontend/app/time-off/page.tsx.ejs +158 -82
  140. package/hedhog/frontend/app/timesheets/page.tsx.ejs +814 -357
  141. package/hedhog/frontend/messages/en.json +268 -53
  142. package/hedhog/frontend/messages/pt.json +484 -271
  143. package/hedhog/table/operations_collaborator.yaml +26 -13
  144. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -0
  145. package/hedhog/table/operations_collaborator_type.yaml +33 -0
  146. package/hedhog/table/operations_job_title.yaml +24 -0
  147. package/hedhog/table/operations_project.yaml +9 -0
  148. package/hedhog/table/operations_project_assignment.yaml +9 -0
  149. package/hedhog/table/operations_project_role.yaml +39 -0
  150. package/hedhog/table/operations_task.yaml +69 -0
  151. package/hedhog/table/operations_timesheet_entry.yaml +12 -0
  152. package/package.json +6 -6
  153. package/src/controllers/operations-approvals.controller.ts +24 -0
  154. package/src/controllers/operations-collaborators.controller.ts +60 -0
  155. package/src/controllers/operations-contracts.controller.ts +138 -0
  156. package/src/controllers/operations-org-structure.controller.ts +92 -0
  157. package/src/controllers/operations-projects.controller.ts +50 -0
  158. package/src/controllers/operations-tasks.controller.ts +63 -0
  159. package/src/controllers/operations-timesheets.controller.ts +100 -0
  160. package/src/dto/create-collaborator-type.dto.ts +43 -0
  161. package/src/dto/create-collaborator.dto.ts +223 -0
  162. package/src/dto/create-schedule-adjustment-request.dto.ts +91 -0
  163. package/src/dto/create-task.dto.ts +75 -0
  164. package/src/dto/create-time-off-request.dto.ts +53 -0
  165. package/src/dto/create-timesheet-entry.dto.ts +67 -0
  166. package/src/dto/list-collaborator-types.dto.ts +15 -0
  167. package/src/dto/list-collaborators.dto.ts +30 -0
  168. package/src/dto/list-project-options.dto.ts +3 -0
  169. package/src/dto/list-tasks.dto.ts +25 -0
  170. package/src/dto/list-timesheet-entries.dto.ts +40 -0
  171. package/src/dto/update-collaborator-type.dto.ts +3 -0
  172. package/src/dto/update-collaborator.dto.ts +3 -0
  173. package/src/dto/update-task.dto.ts +76 -0
  174. package/src/operations.controller.ts +1 -278
  175. package/src/operations.module.ts +23 -2
  176. package/src/operations.service.spec.ts +450 -0
  177. package/src/operations.service.ts +4507 -1561
  178. package/src/services/shared/operations-access.service.ts +52 -0
@@ -0,0 +1,231 @@
1
+ 'use client';
2
+
3
+ import { useMemo, useState } from 'react';
4
+
5
+ import { Button } from '@/components/ui/button';
6
+ import {
7
+ Command,
8
+ CommandEmpty,
9
+ CommandGroup,
10
+ CommandInput,
11
+ CommandItem,
12
+ CommandList,
13
+ } from '@/components/ui/command';
14
+ import {
15
+ Popover,
16
+ PopoverContent,
17
+ PopoverTrigger,
18
+ } from '@/components/ui/popover';
19
+ import { cn } from '@/lib/utils';
20
+ import { Check, ChevronsUpDown, Loader2, Plus, X } from 'lucide-react';
21
+
22
+ export type AsyncOptionComboboxOption = {
23
+ id: number | string;
24
+ label: string;
25
+ description?: string | null;
26
+ };
27
+
28
+ type AsyncOptionsComboboxProps<T extends AsyncOptionComboboxOption> = {
29
+ value?: number | string;
30
+ selectedOption?: T | null;
31
+ options: T[];
32
+ onSelect: (option: T | null) => void;
33
+ searchValue: string;
34
+ onSearchValueChange: (value: string) => void;
35
+ placeholder: string;
36
+ searchPlaceholder?: string;
37
+ emptyLabel: string;
38
+ loadingLabel?: string;
39
+ loadMoreLabel?: string;
40
+ clearLabel?: string;
41
+ createLabel?: string;
42
+ onCreateClick?: () => void;
43
+ isLoading?: boolean;
44
+ hasMore?: boolean;
45
+ onLoadMore?: () => void;
46
+ disabled?: boolean;
47
+ className?: string;
48
+ };
49
+
50
+ export function AsyncOptionsCombobox<T extends AsyncOptionComboboxOption>({
51
+ value,
52
+ selectedOption,
53
+ options,
54
+ onSelect,
55
+ searchValue,
56
+ onSearchValueChange,
57
+ placeholder,
58
+ searchPlaceholder,
59
+ emptyLabel,
60
+ loadingLabel = 'Loading...',
61
+ loadMoreLabel = 'Load more',
62
+ clearLabel = 'Clear selection',
63
+ createLabel,
64
+ onCreateClick,
65
+ isLoading = false,
66
+ hasMore = false,
67
+ onLoadMore,
68
+ disabled = false,
69
+ className,
70
+ }: AsyncOptionsComboboxProps<T>) {
71
+ const [open, setOpen] = useState(false);
72
+
73
+ const normalizedOptions = useMemo(() => {
74
+ const unique = new Map<string, T>();
75
+
76
+ options.forEach((option) => {
77
+ unique.set(String(option.id), option);
78
+ });
79
+
80
+ return Array.from(unique.values());
81
+ }, [options]);
82
+
83
+ const normalizedValue =
84
+ value === undefined || value === null ? null : String(value);
85
+ const resolvedOption =
86
+ selectedOption ??
87
+ normalizedOptions.find((option) => String(option.id) === normalizedValue) ??
88
+ null;
89
+
90
+ return (
91
+ <div className={cn('flex items-start gap-2', className)}>
92
+ <Popover open={open} onOpenChange={setOpen}>
93
+ <PopoverTrigger asChild>
94
+ <Button
95
+ type="button"
96
+ variant="outline"
97
+ role="combobox"
98
+ aria-expanded={open}
99
+ disabled={disabled}
100
+ className="w-full justify-between"
101
+ >
102
+ <span className="truncate text-left">
103
+ {resolvedOption?.label ?? placeholder}
104
+ </span>
105
+ <ChevronsUpDown className="ml-2 size-4 shrink-0 opacity-50" />
106
+ </Button>
107
+ </PopoverTrigger>
108
+
109
+ <PopoverContent
110
+ align="start"
111
+ className="w-(--radix-popover-trigger-width) p-0"
112
+ >
113
+ <Command shouldFilter={false}>
114
+ <CommandInput
115
+ value={searchValue}
116
+ onValueChange={onSearchValueChange}
117
+ placeholder={searchPlaceholder ?? placeholder}
118
+ />
119
+
120
+ <CommandList>
121
+ <CommandEmpty className="py-6 text-center text-sm">
122
+ {isLoading ? (
123
+ <div className="flex items-center justify-center gap-2 text-muted-foreground">
124
+ <Loader2 className="size-4 animate-spin" />
125
+ <span>{loadingLabel}</span>
126
+ </div>
127
+ ) : (
128
+ <div className="space-y-2">
129
+ <p className="text-muted-foreground">{emptyLabel}</p>
130
+ {onCreateClick && createLabel ? (
131
+ <Button
132
+ type="button"
133
+ variant="outline"
134
+ size="sm"
135
+ onClick={() => {
136
+ setOpen(false);
137
+ onCreateClick();
138
+ }}
139
+ >
140
+ <Plus className="size-4" />
141
+ {createLabel}
142
+ </Button>
143
+ ) : null}
144
+ </div>
145
+ )}
146
+ </CommandEmpty>
147
+
148
+ {normalizedOptions.length > 0 ? (
149
+ <CommandGroup>
150
+ {normalizedOptions.map((option) => {
151
+ const isSelected = String(option.id) === normalizedValue;
152
+
153
+ return (
154
+ <CommandItem
155
+ key={String(option.id)}
156
+ value={`${option.label} ${option.description ?? ''}`}
157
+ onSelect={() => {
158
+ onSelect(option);
159
+ setOpen(false);
160
+ }}
161
+ >
162
+ <Check
163
+ className={cn(
164
+ 'mr-2 size-4',
165
+ isSelected ? 'opacity-100' : 'opacity-0'
166
+ )}
167
+ />
168
+
169
+ <div className="flex min-w-0 flex-col">
170
+ <span className="truncate">{option.label}</span>
171
+ {option.description ? (
172
+ <span className="truncate text-xs text-muted-foreground">
173
+ {option.description}
174
+ </span>
175
+ ) : null}
176
+ </div>
177
+ </CommandItem>
178
+ );
179
+ })}
180
+ </CommandGroup>
181
+ ) : null}
182
+
183
+ {hasMore && onLoadMore ? (
184
+ <div className="border-t p-2">
185
+ <Button
186
+ type="button"
187
+ variant="ghost"
188
+ className="w-full justify-center"
189
+ onClick={onLoadMore}
190
+ disabled={isLoading}
191
+ >
192
+ {isLoading ? (
193
+ <Loader2 className="size-4 animate-spin" />
194
+ ) : null}
195
+ {loadMoreLabel}
196
+ </Button>
197
+ </div>
198
+ ) : null}
199
+ </CommandList>
200
+ </Command>
201
+ </PopoverContent>
202
+ </Popover>
203
+
204
+ {normalizedValue ? (
205
+ <Button
206
+ type="button"
207
+ variant="ghost"
208
+ size="icon"
209
+ aria-label={clearLabel}
210
+ onClick={() => onSelect(null)}
211
+ disabled={disabled}
212
+ >
213
+ <X className="size-4" />
214
+ </Button>
215
+ ) : null}
216
+
217
+ {onCreateClick ? (
218
+ <Button
219
+ type="button"
220
+ variant="outline"
221
+ size="icon"
222
+ aria-label={createLabel ?? 'Create'}
223
+ onClick={onCreateClick}
224
+ disabled={disabled}
225
+ >
226
+ <Plus className="size-4" />
227
+ </Button>
228
+ ) : null}
229
+ </div>
230
+ );
231
+ }
@@ -40,14 +40,19 @@ export function CollaboratorDetailsScreen({
40
40
  const { request, currentLocaleCode } = useApp();
41
41
  const access = useOperationsAccess();
42
42
 
43
- const { data: collaborator, refetch } = useQuery<OperationsCollaboratorDetails>({
44
- queryKey: ['operations-collaborator-details', currentLocaleCode, collaboratorId],
45
- queryFn: () =>
46
- fetchOperations<OperationsCollaboratorDetails>(
47
- request,
48
- `/operations/collaborators/${collaboratorId}`
49
- ),
50
- });
43
+ const { data: collaborator, refetch } =
44
+ useQuery<OperationsCollaboratorDetails>({
45
+ queryKey: [
46
+ 'operations-collaborator-details',
47
+ currentLocaleCode,
48
+ collaboratorId,
49
+ ],
50
+ queryFn: () =>
51
+ fetchOperations<OperationsCollaboratorDetails>(
52
+ request,
53
+ `/operations/collaborators/${collaboratorId}`
54
+ ),
55
+ });
51
56
 
52
57
  if (!collaborator) {
53
58
  return (
@@ -121,45 +126,67 @@ export function CollaboratorDetailsScreen({
121
126
  <SectionCard title={t('sections.profile')} className="xl:col-span-2">
122
127
  <dl className="grid gap-3 md:grid-cols-2 text-sm">
123
128
  <div>
124
- <dt className="text-muted-foreground">{commonT('labels.collaborator')}</dt>
129
+ <dt className="text-muted-foreground">
130
+ {commonT('labels.collaborator')}
131
+ </dt>
125
132
  <dd className="font-medium">{collaborator.displayName}</dd>
126
133
  </div>
127
134
  <div>
128
- <dt className="text-muted-foreground">{commonT('labels.collaboratorType')}</dt>
135
+ <dt className="text-muted-foreground">
136
+ {commonT('labels.collaboratorType')}
137
+ </dt>
129
138
  <dd className="font-medium">
130
- {formatEnumLabel(collaborator.collaboratorType)}
139
+ {collaborator.collaboratorTypeName ||
140
+ formatEnumLabel(
141
+ collaborator.collaboratorTypeSlug ??
142
+ collaborator.collaboratorType
143
+ )}
131
144
  </dd>
132
145
  </div>
133
146
  <div>
134
- <dt className="text-muted-foreground">{commonT('labels.department')}</dt>
147
+ <dt className="text-muted-foreground">
148
+ {commonT('labels.department')}
149
+ </dt>
135
150
  <dd className="font-medium">
136
151
  {collaborator.department || commonT('labels.notAvailable')}
137
152
  </dd>
138
153
  </div>
139
154
  <div>
140
- <dt className="text-muted-foreground">{commonT('labels.title')}</dt>
155
+ <dt className="text-muted-foreground">
156
+ {commonT('labels.title')}
157
+ </dt>
141
158
  <dd className="font-medium">
142
159
  {collaborator.title || commonT('labels.notAvailable')}
143
160
  </dd>
144
161
  </div>
145
162
  <div>
146
- <dt className="text-muted-foreground">{commonT('labels.supervisor')}</dt>
163
+ <dt className="text-muted-foreground">
164
+ {commonT('labels.supervisor')}
165
+ </dt>
147
166
  <dd className="font-medium">
148
167
  {collaborator.supervisorName || commonT('labels.notAssigned')}
149
168
  </dd>
150
169
  </div>
151
170
  <div>
152
- <dt className="text-muted-foreground">{commonT('labels.weeklyCapacity')}</dt>
171
+ <dt className="text-muted-foreground">
172
+ {commonT('labels.weeklyCapacity')}
173
+ </dt>
153
174
  <dd className="font-medium">
154
175
  {formatHours(collaborator.weeklyCapacityHours)}
155
176
  </dd>
156
177
  </div>
157
178
  <div>
158
- <dt className="text-muted-foreground">{commonT('labels.startDate')}</dt>
159
- <dd className="font-medium">{formatDate(collaborator.joinedAt)}</dd>
179
+ <dt className="text-muted-foreground">
180
+ {commonT('labels.startDate')}
181
+ </dt>
182
+ <dd className="font-medium">
183
+ {formatDate(collaborator.joinedAt)}
184
+ </dd>
160
185
  </div>
161
186
  <div>
162
- <dt className="text-muted-foreground">{commonT('labels.status')}</dt>
187
+ <dt className="text-muted-foreground">
188
+ {commonT('labels.status')}
189
+ </dt>
163
190
  <dd className="font-medium">
164
191
  <StatusBadge
165
192
  label={formatEnumLabel(collaborator.status)}
@@ -167,24 +194,52 @@ export function CollaboratorDetailsScreen({
167
194
  />
168
195
  </dd>
169
196
  </div>
197
+ {collaborator.equityParticipation ? (
198
+ <>
199
+ <div>
200
+ <dt className="text-muted-foreground">
201
+ {commonT('labels.equityParticipation')}
202
+ </dt>
203
+ <dd className="font-medium">
204
+ {formatEnumLabel(
205
+ collaborator.equityParticipation.participationType
206
+ )}
207
+ </dd>
208
+ </div>
209
+ <div>
210
+ <dt className="text-muted-foreground">
211
+ {commonT('labels.votingPower')}
212
+ </dt>
213
+ <dd className="font-medium">
214
+ {collaborator.equityParticipation.votingPower != null
215
+ ? `${collaborator.equityParticipation.votingPower}%`
216
+ : commonT('labels.notAvailable')}
217
+ </dd>
218
+ </div>
219
+ </>
220
+ ) : null}
170
221
  </dl>
171
222
  </SectionCard>
172
223
 
173
- <SectionCard title={t('sections.primaryContract')} className="xl:col-span-2">
224
+ <SectionCard
225
+ title={t('sections.primaryContract')}
226
+ className="xl:col-span-2"
227
+ >
174
228
  {collaborator.relatedContracts.length > 0 ? (
175
229
  <div className="space-y-3">
176
230
  {collaborator.relatedContracts.slice(0, 2).map((contract) => (
177
231
  <div
178
232
  key={contract.id}
179
233
  className="flex items-center justify-between rounded-lg border px-4 py-3"
180
- >
181
- <div>
182
- <div className="font-medium">
183
- {contract.name || contract.code}
184
- </div>
185
- <div className="text-sm text-muted-foreground">
186
- {contract.code} • {formatEnumLabel(contract.contractCategory)}
187
- </div>
234
+ >
235
+ <div>
236
+ <div className="font-medium">
237
+ {contract.name || contract.code}
238
+ </div>
239
+ <div className="text-sm text-muted-foreground">
240
+ {contract.code} •{' '}
241
+ {formatEnumLabel(contract.contractCategory)}
242
+ </div>
188
243
  </div>
189
244
  <div className="flex items-center gap-2">
190
245
  <StatusBadge
@@ -209,7 +264,10 @@ export function CollaboratorDetailsScreen({
209
264
  <KpiCardsGrid items={summaryCards} />
210
265
 
211
266
  <div className="grid gap-4 xl:grid-cols-2">
212
- <SectionCard title={t('sections.projects')} description={t('sections.projectsDescription')}>
267
+ <SectionCard
268
+ title={t('sections.projects')}
269
+ description={t('sections.projectsDescription')}
270
+ >
213
271
  {collaborator.assignedProjects.length > 0 ? (
214
272
  <div className="overflow-x-auto rounded-md border">
215
273
  <Table>
@@ -230,8 +288,12 @@ export function CollaboratorDetailsScreen({
230
288
  {project.code}
231
289
  </div>
232
290
  </TableCell>
233
- <TableCell>{project.roleLabel || commonT('labels.notAssigned')}</TableCell>
234
- <TableCell>{formatDateRange(project.startDate, project.endDate)}</TableCell>
291
+ <TableCell>
292
+ {project.roleLabel || commonT('labels.notAssigned')}
293
+ </TableCell>
294
+ <TableCell>
295
+ {formatDateRange(project.startDate, project.endDate)}
296
+ </TableCell>
235
297
  <TableCell>
236
298
  <StatusBadge
237
299
  label={formatEnumLabel(project.status)}
@@ -248,27 +310,48 @@ export function CollaboratorDetailsScreen({
248
310
  )}
249
311
  </SectionCard>
250
312
 
251
- <SectionCard title={t('sections.schedule')} description={t('sections.scheduleDescription')}>
313
+ <SectionCard
314
+ title={t('sections.schedule')}
315
+ description={t('sections.scheduleDescription')}
316
+ >
252
317
  <p className="text-sm">
253
- {summarizeScheduleDays(collaborator.weeklySchedule)}
318
+ {summarizeScheduleDays(
319
+ collaborator.weeklySchedule,
320
+ currentLocaleCode
321
+ )}
254
322
  </p>
255
323
  </SectionCard>
256
324
  </div>
257
325
 
258
326
  <div className="grid gap-4 xl:grid-cols-2">
259
- <SectionCard title={t('sections.timeOff')} description={t('sections.timeOffDescription')}>
327
+ <SectionCard
328
+ title={t('sections.timeOff')}
329
+ description={t('sections.timeOffDescription')}
330
+ >
260
331
  <dl className="grid gap-3 md:grid-cols-3 text-sm">
261
332
  <div>
262
- <dt className="text-muted-foreground">{commonT('labels.total')}</dt>
263
- <dd className="font-medium">{collaborator.timeOffSummary.totalRequests}</dd>
333
+ <dt className="text-muted-foreground">
334
+ {commonT('labels.total')}
335
+ </dt>
336
+ <dd className="font-medium">
337
+ {collaborator.timeOffSummary.totalRequests}
338
+ </dd>
264
339
  </div>
265
340
  <div>
266
- <dt className="text-muted-foreground">{commonT('labels.pending')}</dt>
267
- <dd className="font-medium">{collaborator.timeOffSummary.pendingRequests}</dd>
341
+ <dt className="text-muted-foreground">
342
+ {commonT('labels.pending')}
343
+ </dt>
344
+ <dd className="font-medium">
345
+ {collaborator.timeOffSummary.pendingRequests}
346
+ </dd>
268
347
  </div>
269
348
  <div>
270
- <dt className="text-muted-foreground">{commonT('labels.approved')}</dt>
271
- <dd className="font-medium">{collaborator.timeOffSummary.approvedRequests}</dd>
349
+ <dt className="text-muted-foreground">
350
+ {commonT('labels.approved')}
351
+ </dt>
352
+ <dd className="font-medium">
353
+ {collaborator.timeOffSummary.approvedRequests}
354
+ </dd>
272
355
  </div>
273
356
  </dl>
274
357
  </SectionCard>
@@ -303,7 +386,9 @@ export function CollaboratorDetailsScreen({
303
386
  ))}
304
387
  </div>
305
388
  ) : (
306
- <p className="text-sm text-muted-foreground">{t('noScheduleAdjustments')}</p>
389
+ <p className="text-sm text-muted-foreground">
390
+ {t('noScheduleAdjustments')}
391
+ </p>
307
392
  )}
308
393
  </SectionCard>
309
394
  </div>