@hed-hog/operations 0.0.304 → 0.0.306

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 (81) hide show
  1. package/dist/controllers/operations-projects.controller.d.ts +15 -0
  2. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-tasks.controller.d.ts +41 -10
  4. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  5. package/dist/controllers/operations-tasks.controller.js +11 -0
  6. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  7. package/dist/controllers/operations-timesheets.controller.d.ts +21 -0
  8. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  9. package/dist/controllers/operations-timesheets.controller.js +12 -0
  10. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  11. package/dist/dto/create-task.dto.d.ts +7 -1
  12. package/dist/dto/create-task.dto.d.ts.map +1 -1
  13. package/dist/dto/create-task.dto.js +38 -5
  14. package/dist/dto/create-task.dto.js.map +1 -1
  15. package/dist/dto/list-tasks.dto.d.ts +1 -1
  16. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  17. package/dist/dto/list-tasks.dto.js +2 -2
  18. package/dist/dto/list-tasks.dto.js.map +1 -1
  19. package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
  20. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
  21. package/dist/dto/update-collaborator-type.dto.js +2 -1
  22. package/dist/dto/update-collaborator-type.dto.js.map +1 -1
  23. package/dist/dto/update-task.dto.d.ts +7 -1
  24. package/dist/dto/update-task.dto.d.ts.map +1 -1
  25. package/dist/dto/update-task.dto.js +38 -5
  26. package/dist/dto/update-task.dto.js.map +1 -1
  27. package/dist/operations.service.d.ts +90 -12
  28. package/dist/operations.service.d.ts.map +1 -1
  29. package/dist/operations.service.js +560 -148
  30. package/dist/operations.service.js.map +1 -1
  31. package/dist/operations.service.spec.js +73 -0
  32. package/dist/operations.service.spec.js.map +1 -1
  33. package/hedhog/data/menu.yaml +26 -26
  34. package/hedhog/data/operations_collaborator_type.yaml +76 -76
  35. package/hedhog/data/route.yaml +26 -0
  36. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
  37. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
  38. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
  39. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  40. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  41. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  42. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  43. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  44. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  45. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
  46. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  47. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  48. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
  49. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
  50. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
  51. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  52. package/hedhog/frontend/app/approvals/page.tsx.ejs +2 -2
  53. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +513 -502
  54. package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
  55. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  56. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  57. package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
  58. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +235 -72
  59. package/hedhog/frontend/app/timesheets/page.tsx.ejs +344 -134
  60. package/hedhog/frontend/messages/en.json +32 -4
  61. package/hedhog/frontend/messages/pt.json +34 -6
  62. package/hedhog/table/operations_collaborator.yaml +18 -18
  63. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
  64. package/hedhog/table/operations_collaborator_type.yaml +33 -33
  65. package/hedhog/table/operations_contract_document.yaml +33 -33
  66. package/hedhog/table/operations_project.yaml +9 -0
  67. package/hedhog/table/operations_task.yaml +43 -4
  68. package/package.json +6 -6
  69. package/src/controllers/operations-tasks.controller.ts +11 -0
  70. package/src/controllers/operations-timesheets.controller.ts +13 -0
  71. package/src/dto/create-collaborator-type.dto.ts +43 -43
  72. package/src/dto/create-collaborator.dto.ts +223 -223
  73. package/src/dto/create-task.dto.ts +47 -7
  74. package/src/dto/list-collaborator-types.dto.ts +15 -15
  75. package/src/dto/list-collaborators.dto.ts +30 -30
  76. package/src/dto/list-tasks.dto.ts +3 -3
  77. package/src/dto/update-collaborator-type.dto.ts +4 -3
  78. package/src/dto/update-collaborator.dto.ts +3 -3
  79. package/src/dto/update-task.dto.ts +47 -7
  80. package/src/operations.service.spec.ts +96 -0
  81. package/src/operations.service.ts +813 -135
@@ -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;