@hed-hog/operations 0.0.304 → 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 (52) 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/dto/create-task.dto.d.ts +7 -1
  8. package/dist/dto/create-task.dto.d.ts.map +1 -1
  9. package/dist/dto/create-task.dto.js +38 -5
  10. package/dist/dto/create-task.dto.js.map +1 -1
  11. package/dist/dto/list-tasks.dto.d.ts +1 -1
  12. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  13. package/dist/dto/list-tasks.dto.js +2 -2
  14. package/dist/dto/list-tasks.dto.js.map +1 -1
  15. package/dist/dto/update-task.dto.d.ts +7 -1
  16. package/dist/dto/update-task.dto.d.ts.map +1 -1
  17. package/dist/dto/update-task.dto.js +38 -5
  18. package/dist/dto/update-task.dto.js.map +1 -1
  19. package/dist/operations.service.d.ts +68 -12
  20. package/dist/operations.service.d.ts.map +1 -1
  21. package/dist/operations.service.js +380 -101
  22. package/dist/operations.service.js.map +1 -1
  23. package/hedhog/data/route.yaml +13 -0
  24. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
  25. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
  26. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  27. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  28. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  29. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  30. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  31. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  32. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
  33. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  34. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  35. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
  36. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
  37. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  38. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -502
  39. package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
  40. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  41. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  42. package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
  43. package/hedhog/frontend/messages/en.json +27 -4
  44. package/hedhog/frontend/messages/pt.json +27 -4
  45. package/hedhog/table/operations_project.yaml +9 -0
  46. package/hedhog/table/operations_task.yaml +43 -4
  47. package/package.json +5 -5
  48. package/src/controllers/operations-tasks.controller.ts +11 -0
  49. package/src/dto/create-task.dto.ts +47 -7
  50. package/src/dto/list-tasks.dto.ts +3 -3
  51. package/src/dto/update-task.dto.ts +47 -7
  52. package/src/operations.service.ts +556 -88
@@ -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;