@hed-hog/operations 0.0.331 → 0.0.338

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 (58) hide show
  1. package/dist/controllers/operations-contracts.controller.d.ts +12 -12
  2. package/dist/operations.service.d.ts.map +1 -1
  3. package/dist/operations.service.js +8 -1
  4. package/dist/operations.service.js.map +1 -1
  5. package/dist/operations.service.spec.js +6 -0
  6. package/dist/operations.service.spec.js.map +1 -1
  7. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +476 -0
  8. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +261 -0
  9. package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -358
  10. package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +6 -6
  11. package/hedhog/frontend/app/_components/contract-content-editor.tsx.ejs +258 -0
  12. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +84 -84
  13. package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -0
  14. package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +23 -23
  15. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +4 -4
  16. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +803 -803
  17. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -629
  18. package/hedhog/frontend/app/_lib/api.ts.ejs +480 -480
  19. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -5
  20. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +74 -74
  21. package/hedhog/frontend/messages/operations/operations/en.json +2100 -0
  22. package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
  23. package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +16 -16
  24. package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +16 -16
  25. package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +16 -16
  26. package/hedhog/frontend/widgets/index.ts.ejs +25 -25
  27. package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +16 -16
  28. package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +16 -16
  29. package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +16 -16
  30. package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +16 -16
  31. package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +16 -16
  32. package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +16 -16
  33. package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +16 -16
  34. package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +16 -16
  35. package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +16 -16
  36. package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +16 -16
  37. package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +16 -16
  38. package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +16 -16
  39. package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +16 -16
  40. package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +16 -16
  41. package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +169 -169
  42. package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +16 -16
  43. package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +16 -16
  44. package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +16 -16
  45. package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +16 -16
  46. package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +16 -16
  47. package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +16 -16
  48. package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +16 -16
  49. package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +16 -16
  50. package/hedhog/table/operations_collaborator.yaml +8 -8
  51. package/hedhog/table/operations_task.yaml +76 -76
  52. package/hedhog/table/operations_task_activity.yaml +51 -51
  53. package/package.json +5 -5
  54. package/src/controllers/operations-tasks.controller.ts +156 -156
  55. package/src/dashboard/widgets/MyQuickActions.tsx +22 -22
  56. package/src/dto/create-collaborator.dto.ts +4 -4
  57. package/src/operations.service.spec.ts +1006 -988
  58. package/src/operations.service.ts +8 -1
@@ -0,0 +1,261 @@
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 { useTranslations } from 'next-intl';
27
+ import { useEffect, useRef, useState } from 'react';
28
+ import { fetchOperations } from '../_lib/api';
29
+ import type {
30
+ OperationsCollaborator,
31
+ OperationsCollaboratorDetails,
32
+ } from '../_lib/types';
33
+ import { CollaboratorFormScreen } from './collaborator-form-screen';
34
+
35
+ type CollaboratorSelectWithCreateProps = {
36
+ label: string;
37
+ value?: number | null;
38
+ initialSelectedLabel?: string;
39
+ placeholder: string;
40
+ disabled?: boolean;
41
+ onChange: (option: OperationsCollaborator | null) => void;
42
+ };
43
+
44
+ export function CollaboratorSelectWithCreate({
45
+ label,
46
+ value,
47
+ initialSelectedLabel = '',
48
+ placeholder,
49
+ disabled = false,
50
+ onChange,
51
+ }: CollaboratorSelectWithCreateProps) {
52
+ const { request, currentLocaleCode } = useApp();
53
+ const commonT = useTranslations('operations.Common');
54
+ const [open, setOpen] = useState(false);
55
+ const [search, setSearch] = useState('');
56
+ const [createOpen, setCreateOpen] = useState(false);
57
+ const [selectedLabel, setSelectedLabel] = useState(initialSelectedLabel);
58
+ const parentScrollContainerRef = useRef<HTMLElement | null>(null);
59
+ const parentScrollTopRef = useRef(0);
60
+
61
+ const { data: collaborators = [] } = useQuery<OperationsCollaborator[]>({
62
+ queryKey: ['operations-inline-collaborators', currentLocaleCode],
63
+ queryFn: () =>
64
+ fetchOperations<OperationsCollaborator[]>(
65
+ request,
66
+ '/operations/collaborators'
67
+ ),
68
+ placeholderData: (old) => old ?? [],
69
+ });
70
+
71
+ useEffect(() => {
72
+ setSelectedLabel(initialSelectedLabel);
73
+ }, [initialSelectedLabel]);
74
+
75
+ useEffect(() => {
76
+ if (value == null) {
77
+ setSelectedLabel('');
78
+ return;
79
+ }
80
+
81
+ const option = collaborators.find((item) => item.id === value);
82
+ if (option) {
83
+ setSelectedLabel(option.displayName);
84
+ }
85
+ }, [collaborators, value]);
86
+
87
+ const filteredOptions = collaborators.filter((item) => {
88
+ const haystack = [item.displayName, item.code, item.title, item.department]
89
+ .filter(Boolean)
90
+ .join(' ')
91
+ .toLowerCase();
92
+ return haystack.includes(search.trim().toLowerCase());
93
+ });
94
+
95
+ const captureParentScrollPosition = (trigger: HTMLElement) => {
96
+ const parentSheetContent = trigger.closest(
97
+ '[data-radix-dialog-content]'
98
+ ) as HTMLElement | null;
99
+
100
+ if (!parentSheetContent) {
101
+ parentScrollContainerRef.current = null;
102
+ parentScrollTopRef.current = 0;
103
+ return;
104
+ }
105
+
106
+ parentScrollContainerRef.current = parentSheetContent;
107
+ parentScrollTopRef.current = parentSheetContent.scrollTop;
108
+ };
109
+
110
+ const restoreParentScrollPosition = () => {
111
+ const container =
112
+ parentScrollContainerRef.current &&
113
+ document.body.contains(parentScrollContainerRef.current)
114
+ ? parentScrollContainerRef.current
115
+ : null;
116
+
117
+ if (!container) {
118
+ return;
119
+ }
120
+
121
+ const restore = () => {
122
+ container.scrollTop = parentScrollTopRef.current;
123
+ };
124
+
125
+ requestAnimationFrame(restore);
126
+ setTimeout(restore, 0);
127
+ setTimeout(restore, 120);
128
+ };
129
+
130
+ return (
131
+ <div className="space-y-1.5">
132
+ <label className="text-xs font-medium text-muted-foreground">
133
+ {label}
134
+ </label>
135
+
136
+ <div className="flex gap-2">
137
+ <Popover open={open} onOpenChange={setOpen}>
138
+ <PopoverTrigger asChild>
139
+ <Button
140
+ type="button"
141
+ variant="outline"
142
+ role="combobox"
143
+ disabled={disabled}
144
+ className="h-9 flex-1 justify-between overflow-hidden"
145
+ >
146
+ <span className="truncate text-left">
147
+ {selectedLabel || placeholder}
148
+ </span>
149
+ <ChevronsUpDown className="size-4 shrink-0 opacity-50" />
150
+ </Button>
151
+ </PopoverTrigger>
152
+ <PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
153
+ <Command shouldFilter={false}>
154
+ <CommandInput
155
+ value={search}
156
+ onValueChange={setSearch}
157
+ placeholder={commonT('labels.searchCollaborator')}
158
+ />
159
+ <CommandList>
160
+ <CommandEmpty>
161
+ {commonT('labels.noCollaboratorsFound')}
162
+ </CommandEmpty>
163
+ <CommandGroup>
164
+ {filteredOptions.map((option) => (
165
+ <CommandItem
166
+ key={option.id}
167
+ value={String(option.id)}
168
+ onSelect={() => {
169
+ onChange(option);
170
+ setSelectedLabel(option.displayName);
171
+ setOpen(false);
172
+ }}
173
+ >
174
+ <Check
175
+ className={`mr-2 size-4 ${
176
+ value === option.id ? 'opacity-100' : 'opacity-0'
177
+ }`}
178
+ />
179
+ <div className="min-w-0">
180
+ <div className="truncate">{option.displayName}</div>
181
+ <div className="truncate text-xs text-muted-foreground">
182
+ {[option.code, option.title, option.department]
183
+ .filter(Boolean)
184
+ .join(' • ')}
185
+ </div>
186
+ </div>
187
+ </CommandItem>
188
+ ))}
189
+ </CommandGroup>
190
+ </CommandList>
191
+ </Command>
192
+ </PopoverContent>
193
+ </Popover>
194
+
195
+ {value ? (
196
+ <Button
197
+ type="button"
198
+ variant="outline"
199
+ size="icon"
200
+ className="h-9 w-9 shrink-0 cursor-pointer"
201
+ onClick={() => {
202
+ onChange(null);
203
+ setSelectedLabel('');
204
+ }}
205
+ >
206
+ <X className="size-4" />
207
+ </Button>
208
+ ) : null}
209
+
210
+ <Button
211
+ type="button"
212
+ variant="outline"
213
+ size="icon"
214
+ className="h-9 w-9 shrink-0 cursor-pointer"
215
+ disabled={disabled}
216
+ onClick={(event) => {
217
+ captureParentScrollPosition(event.currentTarget);
218
+ setCreateOpen(true);
219
+ }}
220
+ aria-label={`Criar ${label.toLowerCase()}`}
221
+ >
222
+ <Plus className="size-4" />
223
+ </Button>
224
+ </div>
225
+
226
+ <Sheet
227
+ open={createOpen}
228
+ onOpenChange={(nextOpen) => {
229
+ setCreateOpen(nextOpen);
230
+ if (!nextOpen) {
231
+ restoreParentScrollPosition();
232
+ }
233
+ }}
234
+ >
235
+ <SheetContent className="w-full overflow-y-auto sm:max-w-3xl">
236
+ <SheetHeader>
237
+ <SheetTitle>Novo colaborador</SheetTitle>
238
+ <SheetDescription>
239
+ Crie o colaborador sem sair do cadastro do contrato.
240
+ </SheetDescription>
241
+ </SheetHeader>
242
+ <CollaboratorFormScreen
243
+ onCancel={() => setCreateOpen(false)}
244
+ onSaved={async (collaborator: OperationsCollaboratorDetails) => {
245
+ onChange({
246
+ id: collaborator.id,
247
+ code: collaborator.code,
248
+ displayName: collaborator.displayName,
249
+ title: collaborator.title,
250
+ department: collaborator.department,
251
+ status: collaborator.status,
252
+ });
253
+ setSelectedLabel(collaborator.displayName);
254
+ setCreateOpen(false);
255
+ }}
256
+ />
257
+ </SheetContent>
258
+ </Sheet>
259
+ </div>
260
+ );
261
+ }