@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
@@ -1,256 +1,256 @@
1
- 'use client';
2
-
3
- import { Button } from '@/components/ui/button';
4
- import {
5
- Command,
6
- CommandEmpty,
7
- CommandGroup,
8
- CommandInput,
9
- CommandItem,
10
- CommandList,
11
- } from '@/components/ui/command';
12
- import {
13
- Popover,
14
- PopoverContent,
15
- PopoverTrigger,
16
- } from '@/components/ui/popover';
17
- import {
18
- Sheet,
19
- SheetContent,
20
- SheetDescription,
21
- SheetHeader,
22
- SheetTitle,
23
- } from '@/components/ui/sheet';
24
- import { useApp, useQuery } from '@hed-hog/next-app-provider';
25
- import { Check, ChevronsUpDown, Plus, X } from 'lucide-react';
26
- import { useEffect, useRef, useState } from 'react';
27
- import type { OperationsCollaborator, OperationsCollaboratorDetails } from '../_lib/types';
28
- import { fetchOperations } from '../_lib/api';
29
- import { CollaboratorFormScreen } from './collaborator-form-screen';
30
-
31
- type CollaboratorSelectWithCreateProps = {
32
- label: string;
33
- value?: number | null;
34
- initialSelectedLabel?: string;
35
- placeholder: string;
36
- disabled?: boolean;
37
- onChange: (option: OperationsCollaborator | null) => void;
38
- };
39
-
40
- export function CollaboratorSelectWithCreate({
41
- label,
42
- value,
43
- initialSelectedLabel = '',
44
- placeholder,
45
- disabled = false,
46
- onChange,
47
- }: CollaboratorSelectWithCreateProps) {
48
- const { request, currentLocaleCode } = useApp();
49
- const [open, setOpen] = useState(false);
50
- const [search, setSearch] = useState('');
51
- const [createOpen, setCreateOpen] = useState(false);
52
- const [selectedLabel, setSelectedLabel] = useState(initialSelectedLabel);
53
- const parentScrollContainerRef = useRef<HTMLElement | null>(null);
54
- const parentScrollTopRef = useRef(0);
55
-
56
- const { data: collaborators = [] } = useQuery<OperationsCollaborator[]>({
57
- queryKey: ['operations-inline-collaborators', currentLocaleCode],
58
- queryFn: () =>
59
- fetchOperations<OperationsCollaborator[]>(request, '/operations/collaborators'),
60
- placeholderData: (old) => old ?? [],
61
- });
62
-
63
- useEffect(() => {
64
- setSelectedLabel(initialSelectedLabel);
65
- }, [initialSelectedLabel]);
66
-
67
- useEffect(() => {
68
- if (value == null) {
69
- setSelectedLabel('');
70
- return;
71
- }
72
-
73
- const option = collaborators.find((item) => item.id === value);
74
- if (option) {
75
- setSelectedLabel(option.displayName);
76
- }
77
- }, [collaborators, value]);
78
-
79
- const filteredOptions = collaborators.filter((item) => {
80
- const haystack = [
81
- item.displayName,
82
- item.code,
83
- item.title,
84
- item.department,
85
- ]
86
- .filter(Boolean)
87
- .join(' ')
88
- .toLowerCase();
89
- return haystack.includes(search.trim().toLowerCase());
90
- });
91
-
92
- const captureParentScrollPosition = (trigger: HTMLElement) => {
93
- const parentSheetContent = trigger.closest(
94
- '[data-radix-dialog-content]'
95
- ) as HTMLElement | null;
96
-
97
- if (!parentSheetContent) {
98
- parentScrollContainerRef.current = null;
99
- parentScrollTopRef.current = 0;
100
- return;
101
- }
102
-
103
- parentScrollContainerRef.current = parentSheetContent;
104
- parentScrollTopRef.current = parentSheetContent.scrollTop;
105
- };
106
-
107
- const restoreParentScrollPosition = () => {
108
- const container =
109
- parentScrollContainerRef.current &&
110
- document.body.contains(parentScrollContainerRef.current)
111
- ? parentScrollContainerRef.current
112
- : null;
113
-
114
- if (!container) {
115
- return;
116
- }
117
-
118
- const restore = () => {
119
- container.scrollTop = parentScrollTopRef.current;
120
- };
121
-
122
- requestAnimationFrame(restore);
123
- setTimeout(restore, 0);
124
- setTimeout(restore, 120);
125
- };
126
-
127
- return (
128
- <div className="space-y-1.5">
129
- <label className="text-xs font-medium text-muted-foreground">
130
- {label}
131
- </label>
132
-
133
- <div className="flex gap-2">
134
- <Popover open={open} onOpenChange={setOpen}>
135
- <PopoverTrigger asChild>
136
- <Button
137
- type="button"
138
- variant="outline"
139
- role="combobox"
140
- disabled={disabled}
141
- className="h-9 flex-1 justify-between overflow-hidden"
142
- >
143
- <span className="truncate text-left">
144
- {selectedLabel || placeholder}
145
- </span>
146
- <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
147
- </Button>
148
- </PopoverTrigger>
149
- <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
150
- <Command shouldFilter={false}>
151
- <CommandInput
152
- value={search}
153
- onValueChange={setSearch}
154
- placeholder="Buscar colaborador..."
155
- />
156
- <CommandList>
157
- <CommandEmpty>Nenhum colaborador encontrado.</CommandEmpty>
158
- <CommandGroup>
159
- {filteredOptions.map((option) => (
160
- <CommandItem
161
- key={option.id}
162
- value={String(option.id)}
163
- onSelect={() => {
164
- onChange(option);
165
- setSelectedLabel(option.displayName);
166
- setOpen(false);
167
- }}
168
- >
169
- <Check
170
- className={`mr-2 size-4 ${
171
- value === option.id ? 'opacity-100' : 'opacity-0'
172
- }`}
173
- />
174
- <div className="min-w-0">
175
- <div className="truncate">{option.displayName}</div>
176
- <div className="truncate text-xs text-muted-foreground">
177
- {[option.code, option.title, option.department]
178
- .filter(Boolean)
179
- .join(' • ')}
180
- </div>
181
- </div>
182
- </CommandItem>
183
- ))}
184
- </CommandGroup>
185
- </CommandList>
186
- </Command>
187
- </PopoverContent>
188
- </Popover>
189
-
190
- {value ? (
191
- <Button
192
- type="button"
193
- variant="outline"
194
- size="icon"
195
- className="h-9 w-9 shrink-0 cursor-pointer"
196
- onClick={() => {
197
- onChange(null);
198
- setSelectedLabel('');
199
- }}
200
- >
201
- <X className="size-4" />
202
- </Button>
203
- ) : null}
204
-
205
- <Button
206
- type="button"
207
- variant="outline"
208
- size="icon"
209
- className="h-9 w-9 shrink-0 cursor-pointer"
210
- disabled={disabled}
211
- onClick={(event) => {
212
- captureParentScrollPosition(event.currentTarget);
213
- setCreateOpen(true);
214
- }}
215
- aria-label={`Criar ${label.toLowerCase()}`}
216
- >
217
- <Plus className="size-4" />
218
- </Button>
219
- </div>
220
-
221
- <Sheet
222
- open={createOpen}
223
- onOpenChange={(nextOpen) => {
224
- setCreateOpen(nextOpen);
225
- if (!nextOpen) {
226
- restoreParentScrollPosition();
227
- }
228
- }}
229
- >
230
- <SheetContent className="w-full overflow-y-auto sm:max-w-3xl">
231
- <SheetHeader>
232
- <SheetTitle>Novo colaborador</SheetTitle>
233
- <SheetDescription>
234
- Crie o colaborador sem sair do cadastro do contrato.
235
- </SheetDescription>
236
- </SheetHeader>
237
- <CollaboratorFormScreen
238
- onCancel={() => setCreateOpen(false)}
239
- onSaved={async (collaborator: OperationsCollaboratorDetails) => {
240
- onChange({
241
- id: collaborator.id,
242
- code: collaborator.code,
243
- displayName: collaborator.displayName,
244
- title: collaborator.title,
245
- department: collaborator.department,
246
- status: collaborator.status,
247
- });
248
- setSelectedLabel(collaborator.displayName);
249
- setCreateOpen(false);
250
- }}
251
- />
252
- </SheetContent>
253
- </Sheet>
254
- </div>
255
- );
256
- }
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import {
5
+ Command,
6
+ CommandEmpty,
7
+ CommandGroup,
8
+ CommandInput,
9
+ CommandItem,
10
+ CommandList,
11
+ } from '@/components/ui/command';
12
+ import {
13
+ Popover,
14
+ PopoverContent,
15
+ PopoverTrigger,
16
+ } from '@/components/ui/popover';
17
+ import {
18
+ Sheet,
19
+ SheetContent,
20
+ SheetDescription,
21
+ SheetHeader,
22
+ SheetTitle,
23
+ } from '@/components/ui/sheet';
24
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
25
+ import { Check, ChevronsUpDown, Plus, X } from 'lucide-react';
26
+ import { useEffect, useRef, useState } from 'react';
27
+ import type { OperationsCollaborator, OperationsCollaboratorDetails } from '../_lib/types';
28
+ import { fetchOperations } from '../_lib/api';
29
+ import { CollaboratorFormScreen } from './collaborator-form-screen';
30
+
31
+ type CollaboratorSelectWithCreateProps = {
32
+ label: string;
33
+ value?: number | null;
34
+ initialSelectedLabel?: string;
35
+ placeholder: string;
36
+ disabled?: boolean;
37
+ onChange: (option: OperationsCollaborator | null) => void;
38
+ };
39
+
40
+ export function CollaboratorSelectWithCreate({
41
+ label,
42
+ value,
43
+ initialSelectedLabel = '',
44
+ placeholder,
45
+ disabled = false,
46
+ onChange,
47
+ }: CollaboratorSelectWithCreateProps) {
48
+ const { request, currentLocaleCode } = useApp();
49
+ const [open, setOpen] = useState(false);
50
+ const [search, setSearch] = useState('');
51
+ const [createOpen, setCreateOpen] = useState(false);
52
+ const [selectedLabel, setSelectedLabel] = useState(initialSelectedLabel);
53
+ const parentScrollContainerRef = useRef<HTMLElement | null>(null);
54
+ const parentScrollTopRef = useRef(0);
55
+
56
+ const { data: collaborators = [] } = useQuery<OperationsCollaborator[]>({
57
+ queryKey: ['operations-inline-collaborators', currentLocaleCode],
58
+ queryFn: () =>
59
+ fetchOperations<OperationsCollaborator[]>(request, '/operations/collaborators'),
60
+ placeholderData: (old) => old ?? [],
61
+ });
62
+
63
+ useEffect(() => {
64
+ setSelectedLabel(initialSelectedLabel);
65
+ }, [initialSelectedLabel]);
66
+
67
+ useEffect(() => {
68
+ if (value == null) {
69
+ setSelectedLabel('');
70
+ return;
71
+ }
72
+
73
+ const option = collaborators.find((item) => item.id === value);
74
+ if (option) {
75
+ setSelectedLabel(option.displayName);
76
+ }
77
+ }, [collaborators, value]);
78
+
79
+ const filteredOptions = collaborators.filter((item) => {
80
+ const haystack = [
81
+ item.displayName,
82
+ item.code,
83
+ item.title,
84
+ item.department,
85
+ ]
86
+ .filter(Boolean)
87
+ .join(' ')
88
+ .toLowerCase();
89
+ return haystack.includes(search.trim().toLowerCase());
90
+ });
91
+
92
+ const captureParentScrollPosition = (trigger: HTMLElement) => {
93
+ const parentSheetContent = trigger.closest(
94
+ '[data-radix-dialog-content]'
95
+ ) as HTMLElement | null;
96
+
97
+ if (!parentSheetContent) {
98
+ parentScrollContainerRef.current = null;
99
+ parentScrollTopRef.current = 0;
100
+ return;
101
+ }
102
+
103
+ parentScrollContainerRef.current = parentSheetContent;
104
+ parentScrollTopRef.current = parentSheetContent.scrollTop;
105
+ };
106
+
107
+ const restoreParentScrollPosition = () => {
108
+ const container =
109
+ parentScrollContainerRef.current &&
110
+ document.body.contains(parentScrollContainerRef.current)
111
+ ? parentScrollContainerRef.current
112
+ : null;
113
+
114
+ if (!container) {
115
+ return;
116
+ }
117
+
118
+ const restore = () => {
119
+ container.scrollTop = parentScrollTopRef.current;
120
+ };
121
+
122
+ requestAnimationFrame(restore);
123
+ setTimeout(restore, 0);
124
+ setTimeout(restore, 120);
125
+ };
126
+
127
+ return (
128
+ <div className="space-y-1.5">
129
+ <label className="text-xs font-medium text-muted-foreground">
130
+ {label}
131
+ </label>
132
+
133
+ <div className="flex gap-2">
134
+ <Popover open={open} onOpenChange={setOpen}>
135
+ <PopoverTrigger asChild>
136
+ <Button
137
+ type="button"
138
+ variant="outline"
139
+ role="combobox"
140
+ disabled={disabled}
141
+ className="h-9 flex-1 justify-between overflow-hidden"
142
+ >
143
+ <span className="truncate text-left">
144
+ {selectedLabel || placeholder}
145
+ </span>
146
+ <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
147
+ </Button>
148
+ </PopoverTrigger>
149
+ <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
150
+ <Command shouldFilter={false}>
151
+ <CommandInput
152
+ value={search}
153
+ onValueChange={setSearch}
154
+ placeholder="Buscar colaborador..."
155
+ />
156
+ <CommandList>
157
+ <CommandEmpty>Nenhum colaborador encontrado.</CommandEmpty>
158
+ <CommandGroup>
159
+ {filteredOptions.map((option) => (
160
+ <CommandItem
161
+ key={option.id}
162
+ value={String(option.id)}
163
+ onSelect={() => {
164
+ onChange(option);
165
+ setSelectedLabel(option.displayName);
166
+ setOpen(false);
167
+ }}
168
+ >
169
+ <Check
170
+ className={`mr-2 size-4 ${
171
+ value === option.id ? 'opacity-100' : 'opacity-0'
172
+ }`}
173
+ />
174
+ <div className="min-w-0">
175
+ <div className="truncate">{option.displayName}</div>
176
+ <div className="truncate text-xs text-muted-foreground">
177
+ {[option.code, option.title, option.department]
178
+ .filter(Boolean)
179
+ .join(' • ')}
180
+ </div>
181
+ </div>
182
+ </CommandItem>
183
+ ))}
184
+ </CommandGroup>
185
+ </CommandList>
186
+ </Command>
187
+ </PopoverContent>
188
+ </Popover>
189
+
190
+ {value ? (
191
+ <Button
192
+ type="button"
193
+ variant="outline"
194
+ size="icon"
195
+ className="h-9 w-9 shrink-0 cursor-pointer"
196
+ onClick={() => {
197
+ onChange(null);
198
+ setSelectedLabel('');
199
+ }}
200
+ >
201
+ <X className="size-4" />
202
+ </Button>
203
+ ) : null}
204
+
205
+ <Button
206
+ type="button"
207
+ variant="outline"
208
+ size="icon"
209
+ className="h-9 w-9 shrink-0 cursor-pointer"
210
+ disabled={disabled}
211
+ onClick={(event) => {
212
+ captureParentScrollPosition(event.currentTarget);
213
+ setCreateOpen(true);
214
+ }}
215
+ aria-label={`Criar ${label.toLowerCase()}`}
216
+ >
217
+ <Plus className="size-4" />
218
+ </Button>
219
+ </div>
220
+
221
+ <Sheet
222
+ open={createOpen}
223
+ onOpenChange={(nextOpen) => {
224
+ setCreateOpen(nextOpen);
225
+ if (!nextOpen) {
226
+ restoreParentScrollPosition();
227
+ }
228
+ }}
229
+ >
230
+ <SheetContent className="w-full overflow-y-auto sm:max-w-3xl">
231
+ <SheetHeader>
232
+ <SheetTitle>Novo colaborador</SheetTitle>
233
+ <SheetDescription>
234
+ Crie o colaborador sem sair do cadastro do contrato.
235
+ </SheetDescription>
236
+ </SheetHeader>
237
+ <CollaboratorFormScreen
238
+ onCancel={() => setCreateOpen(false)}
239
+ onSaved={async (collaborator: OperationsCollaboratorDetails) => {
240
+ onChange({
241
+ id: collaborator.id,
242
+ code: collaborator.code,
243
+ displayName: collaborator.displayName,
244
+ title: collaborator.title,
245
+ department: collaborator.department,
246
+ status: collaborator.status,
247
+ });
248
+ setSelectedLabel(collaborator.displayName);
249
+ setCreateOpen(false);
250
+ }}
251
+ />
252
+ </SheetContent>
253
+ </Sheet>
254
+ </div>
255
+ );
256
+ }
@@ -341,13 +341,13 @@ export function ContractFormScreen({
341
341
  return;
342
342
  }
343
343
 
344
- const next = toFormState(contract);
345
- if (duplicateFromId) {
346
- next.code = `${contract.code}-COPY`;
347
- next.name = `${contract.name || contract.code || 'Contrato'} Copy`;
348
- next.originType = 'manual';
349
- next.originId = '';
350
- next.status = 'draft';
344
+ const next = toFormState(contract);
345
+ if (duplicateFromId) {
346
+ next.code = `${contract.code}-COPY`;
347
+ next.name = `${contract.name || contract.code || 'Contrato'} Copy`;
348
+ next.originType = 'manual';
349
+ next.originId = '';
350
+ next.status = 'draft';
351
351
  next.signatureStatus = 'not_started';
352
352
  next.signedAt = '';
353
353
  next.isActive = true;