@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,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
+ }