@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,247 +1,247 @@
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 { fetchOperations } from '../_lib/api';
28
- import type { OperationsContractTemplate } from '../_lib/types';
29
- import { ContractTemplateFormScreen } from './contract-template-form-screen';
30
-
31
- type ContractTemplateSelectWithCreateProps = {
32
- label: string;
33
- value?: number | null;
34
- initialSelectedLabel?: string;
35
- placeholder: string;
36
- disabled?: boolean;
37
- onChange: (option: OperationsContractTemplate | null) => void;
38
- };
39
-
40
- export function ContractTemplateSelectWithCreate({
41
- label,
42
- value,
43
- initialSelectedLabel = '',
44
- placeholder,
45
- disabled = false,
46
- onChange,
47
- }: ContractTemplateSelectWithCreateProps) {
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: templates = [] } = useQuery<OperationsContractTemplate[]>({
57
- queryKey: ['operations-inline-contract-templates', currentLocaleCode],
58
- queryFn: () =>
59
- fetchOperations<OperationsContractTemplate[]>(
60
- request,
61
- '/operations/contract-templates'
62
- ),
63
- placeholderData: (old) => old ?? [],
64
- });
65
-
66
- useEffect(() => {
67
- setSelectedLabel(initialSelectedLabel);
68
- }, [initialSelectedLabel]);
69
-
70
- useEffect(() => {
71
- if (value == null) {
72
- setSelectedLabel('');
73
- return;
74
- }
75
-
76
- const option = templates.find((item) => item.id === value);
77
- if (option) {
78
- setSelectedLabel(option.name);
79
- }
80
- }, [templates, value]);
81
-
82
- const filteredOptions = templates.filter((item) => {
83
- const haystack = [item.name, item.code, item.description]
84
- .filter(Boolean)
85
- .join(' ')
86
- .toLowerCase();
87
- return haystack.includes(search.trim().toLowerCase());
88
- });
89
-
90
- const captureParentScrollPosition = (trigger: HTMLElement) => {
91
- const parentSheetContent = trigger.closest(
92
- '[data-radix-dialog-content]'
93
- ) as HTMLElement | null;
94
-
95
- if (!parentSheetContent) {
96
- parentScrollContainerRef.current = null;
97
- parentScrollTopRef.current = 0;
98
- return;
99
- }
100
-
101
- parentScrollContainerRef.current = parentSheetContent;
102
- parentScrollTopRef.current = parentSheetContent.scrollTop;
103
- };
104
-
105
- const restoreParentScrollPosition = () => {
106
- const container =
107
- parentScrollContainerRef.current &&
108
- document.body.contains(parentScrollContainerRef.current)
109
- ? parentScrollContainerRef.current
110
- : null;
111
-
112
- if (!container) {
113
- return;
114
- }
115
-
116
- const restore = () => {
117
- container.scrollTop = parentScrollTopRef.current;
118
- };
119
-
120
- requestAnimationFrame(restore);
121
- setTimeout(restore, 0);
122
- setTimeout(restore, 120);
123
- };
124
-
125
- return (
126
- <div className="space-y-1.5">
127
- <label className="text-xs font-medium text-muted-foreground">
128
- {label}
129
- </label>
130
-
131
- <div className="flex gap-2">
132
- <Popover open={open} onOpenChange={setOpen}>
133
- <PopoverTrigger asChild>
134
- <Button
135
- type="button"
136
- variant="outline"
137
- role="combobox"
138
- disabled={disabled}
139
- className="h-9 flex-1 justify-between overflow-hidden"
140
- >
141
- <span className="truncate text-left">
142
- {selectedLabel || placeholder}
143
- </span>
144
- <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
145
- </Button>
146
- </PopoverTrigger>
147
- <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
148
- <Command shouldFilter={false}>
149
- <CommandInput
150
- value={search}
151
- onValueChange={setSearch}
152
- placeholder="Buscar template..."
153
- />
154
- <CommandList>
155
- <CommandEmpty>Nenhum template encontrado.</CommandEmpty>
156
- <CommandGroup>
157
- {filteredOptions.map((option) => (
158
- <CommandItem
159
- key={option.id}
160
- value={String(option.id)}
161
- onSelect={() => {
162
- onChange(option);
163
- setSelectedLabel(option.name);
164
- setOpen(false);
165
- }}
166
- >
167
- <Check
168
- className={`mr-2 size-4 ${
169
- value === option.id ? 'opacity-100' : 'opacity-0'
170
- }`}
171
- />
172
- <div className="min-w-0">
173
- <div className="truncate">{option.name}</div>
174
- <div className="truncate text-xs text-muted-foreground">
175
- {[option.code, option.contractType]
176
- .filter(Boolean)
177
- .join(' • ')}
178
- </div>
179
- </div>
180
- </CommandItem>
181
- ))}
182
- </CommandGroup>
183
- </CommandList>
184
- </Command>
185
- </PopoverContent>
186
- </Popover>
187
-
188
- {value ? (
189
- <Button
190
- type="button"
191
- variant="outline"
192
- size="icon"
193
- className="h-9 w-9 shrink-0 cursor-pointer"
194
- onClick={() => {
195
- onChange(null);
196
- setSelectedLabel('');
197
- }}
198
- >
199
- <X className="size-4" />
200
- </Button>
201
- ) : null}
202
-
203
- <Button
204
- type="button"
205
- variant="outline"
206
- size="icon"
207
- className="h-9 w-9 shrink-0 cursor-pointer"
208
- disabled={disabled}
209
- onClick={(event) => {
210
- captureParentScrollPosition(event.currentTarget);
211
- setCreateOpen(true);
212
- }}
213
- aria-label={`Criar ${label.toLowerCase()}`}
214
- >
215
- <Plus className="size-4" />
216
- </Button>
217
- </div>
218
-
219
- <Sheet
220
- open={createOpen}
221
- onOpenChange={(nextOpen) => {
222
- setCreateOpen(nextOpen);
223
- if (!nextOpen) {
224
- restoreParentScrollPosition();
225
- }
226
- }}
227
- >
228
- <SheetContent className="w-full overflow-y-auto sm:max-w-[min(92vw,72rem)]">
229
- <SheetHeader>
230
- <SheetTitle>Novo template</SheetTitle>
231
- <SheetDescription>
232
- Crie um template sem sair do wizard do contrato.
233
- </SheetDescription>
234
- </SheetHeader>
235
- <ContractTemplateFormScreen
236
- onCancel={() => setCreateOpen(false)}
237
- onSaved={async (template) => {
238
- onChange(template);
239
- setSelectedLabel(template.name);
240
- setCreateOpen(false);
241
- }}
242
- />
243
- </SheetContent>
244
- </Sheet>
245
- </div>
246
- );
247
- }
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 { fetchOperations } from '../_lib/api';
28
+ import type { OperationsContractTemplate } from '../_lib/types';
29
+ import { ContractTemplateFormScreen } from './contract-template-form-screen';
30
+
31
+ type ContractTemplateSelectWithCreateProps = {
32
+ label: string;
33
+ value?: number | null;
34
+ initialSelectedLabel?: string;
35
+ placeholder: string;
36
+ disabled?: boolean;
37
+ onChange: (option: OperationsContractTemplate | null) => void;
38
+ };
39
+
40
+ export function ContractTemplateSelectWithCreate({
41
+ label,
42
+ value,
43
+ initialSelectedLabel = '',
44
+ placeholder,
45
+ disabled = false,
46
+ onChange,
47
+ }: ContractTemplateSelectWithCreateProps) {
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: templates = [] } = useQuery<OperationsContractTemplate[]>({
57
+ queryKey: ['operations-inline-contract-templates', currentLocaleCode],
58
+ queryFn: () =>
59
+ fetchOperations<OperationsContractTemplate[]>(
60
+ request,
61
+ '/operations/contract-templates'
62
+ ),
63
+ placeholderData: (old) => old ?? [],
64
+ });
65
+
66
+ useEffect(() => {
67
+ setSelectedLabel(initialSelectedLabel);
68
+ }, [initialSelectedLabel]);
69
+
70
+ useEffect(() => {
71
+ if (value == null) {
72
+ setSelectedLabel('');
73
+ return;
74
+ }
75
+
76
+ const option = templates.find((item) => item.id === value);
77
+ if (option) {
78
+ setSelectedLabel(option.name);
79
+ }
80
+ }, [templates, value]);
81
+
82
+ const filteredOptions = templates.filter((item) => {
83
+ const haystack = [item.name, item.code, item.description]
84
+ .filter(Boolean)
85
+ .join(' ')
86
+ .toLowerCase();
87
+ return haystack.includes(search.trim().toLowerCase());
88
+ });
89
+
90
+ const captureParentScrollPosition = (trigger: HTMLElement) => {
91
+ const parentSheetContent = trigger.closest(
92
+ '[data-radix-dialog-content]'
93
+ ) as HTMLElement | null;
94
+
95
+ if (!parentSheetContent) {
96
+ parentScrollContainerRef.current = null;
97
+ parentScrollTopRef.current = 0;
98
+ return;
99
+ }
100
+
101
+ parentScrollContainerRef.current = parentSheetContent;
102
+ parentScrollTopRef.current = parentSheetContent.scrollTop;
103
+ };
104
+
105
+ const restoreParentScrollPosition = () => {
106
+ const container =
107
+ parentScrollContainerRef.current &&
108
+ document.body.contains(parentScrollContainerRef.current)
109
+ ? parentScrollContainerRef.current
110
+ : null;
111
+
112
+ if (!container) {
113
+ return;
114
+ }
115
+
116
+ const restore = () => {
117
+ container.scrollTop = parentScrollTopRef.current;
118
+ };
119
+
120
+ requestAnimationFrame(restore);
121
+ setTimeout(restore, 0);
122
+ setTimeout(restore, 120);
123
+ };
124
+
125
+ return (
126
+ <div className="space-y-1.5">
127
+ <label className="text-xs font-medium text-muted-foreground">
128
+ {label}
129
+ </label>
130
+
131
+ <div className="flex gap-2">
132
+ <Popover open={open} onOpenChange={setOpen}>
133
+ <PopoverTrigger asChild>
134
+ <Button
135
+ type="button"
136
+ variant="outline"
137
+ role="combobox"
138
+ disabled={disabled}
139
+ className="h-9 flex-1 justify-between overflow-hidden"
140
+ >
141
+ <span className="truncate text-left">
142
+ {selectedLabel || placeholder}
143
+ </span>
144
+ <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
145
+ </Button>
146
+ </PopoverTrigger>
147
+ <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
148
+ <Command shouldFilter={false}>
149
+ <CommandInput
150
+ value={search}
151
+ onValueChange={setSearch}
152
+ placeholder="Buscar template..."
153
+ />
154
+ <CommandList>
155
+ <CommandEmpty>Nenhum template encontrado.</CommandEmpty>
156
+ <CommandGroup>
157
+ {filteredOptions.map((option) => (
158
+ <CommandItem
159
+ key={option.id}
160
+ value={String(option.id)}
161
+ onSelect={() => {
162
+ onChange(option);
163
+ setSelectedLabel(option.name);
164
+ setOpen(false);
165
+ }}
166
+ >
167
+ <Check
168
+ className={`mr-2 size-4 ${
169
+ value === option.id ? 'opacity-100' : 'opacity-0'
170
+ }`}
171
+ />
172
+ <div className="min-w-0">
173
+ <div className="truncate">{option.name}</div>
174
+ <div className="truncate text-xs text-muted-foreground">
175
+ {[option.code, option.contractType]
176
+ .filter(Boolean)
177
+ .join(' • ')}
178
+ </div>
179
+ </div>
180
+ </CommandItem>
181
+ ))}
182
+ </CommandGroup>
183
+ </CommandList>
184
+ </Command>
185
+ </PopoverContent>
186
+ </Popover>
187
+
188
+ {value ? (
189
+ <Button
190
+ type="button"
191
+ variant="outline"
192
+ size="icon"
193
+ className="h-9 w-9 shrink-0 cursor-pointer"
194
+ onClick={() => {
195
+ onChange(null);
196
+ setSelectedLabel('');
197
+ }}
198
+ >
199
+ <X className="size-4" />
200
+ </Button>
201
+ ) : null}
202
+
203
+ <Button
204
+ type="button"
205
+ variant="outline"
206
+ size="icon"
207
+ className="h-9 w-9 shrink-0 cursor-pointer"
208
+ disabled={disabled}
209
+ onClick={(event) => {
210
+ captureParentScrollPosition(event.currentTarget);
211
+ setCreateOpen(true);
212
+ }}
213
+ aria-label={`Criar ${label.toLowerCase()}`}
214
+ >
215
+ <Plus className="size-4" />
216
+ </Button>
217
+ </div>
218
+
219
+ <Sheet
220
+ open={createOpen}
221
+ onOpenChange={(nextOpen) => {
222
+ setCreateOpen(nextOpen);
223
+ if (!nextOpen) {
224
+ restoreParentScrollPosition();
225
+ }
226
+ }}
227
+ >
228
+ <SheetContent className="w-full overflow-y-auto sm:max-w-[min(92vw,72rem)]">
229
+ <SheetHeader>
230
+ <SheetTitle>Novo template</SheetTitle>
231
+ <SheetDescription>
232
+ Crie um template sem sair do wizard do contrato.
233
+ </SheetDescription>
234
+ </SheetHeader>
235
+ <ContractTemplateFormScreen
236
+ onCancel={() => setCreateOpen(false)}
237
+ onSaved={async (template) => {
238
+ onChange(template);
239
+ setSelectedLabel(template.name);
240
+ setCreateOpen(false);
241
+ }}
242
+ />
243
+ </SheetContent>
244
+ </Sheet>
245
+ </div>
246
+ );
247
+ }