@hed-hog/operations 0.0.317 → 0.0.319

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 (137) hide show
  1. package/dist/controllers/operations-collaborator-costs.controller.d.ts +144 -0
  2. package/dist/controllers/operations-collaborator-costs.controller.d.ts.map +1 -0
  3. package/dist/controllers/operations-collaborator-costs.controller.js +162 -0
  4. package/dist/controllers/operations-collaborator-costs.controller.js.map +1 -0
  5. package/dist/controllers/operations-collaborators.controller.d.ts +14 -0
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +11 -0
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-projects.controller.d.ts +31 -0
  10. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-projects.controller.js +23 -0
  12. package/dist/controllers/operations-projects.controller.js.map +1 -1
  13. package/dist/controllers/operations-reports.controller.d.ts +199 -0
  14. package/dist/controllers/operations-reports.controller.d.ts.map +1 -0
  15. package/dist/controllers/operations-reports.controller.js +53 -0
  16. package/dist/controllers/operations-reports.controller.js.map +1 -0
  17. package/dist/controllers/operations-tasks.controller.d.ts +41 -2
  18. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-tasks.controller.js +17 -5
  20. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  21. package/dist/dto/create-collaborator-cost.dto.d.ts +16 -0
  22. package/dist/dto/create-collaborator-cost.dto.d.ts.map +1 -0
  23. package/dist/dto/create-collaborator-cost.dto.js +88 -0
  24. package/dist/dto/create-collaborator-cost.dto.js.map +1 -0
  25. package/dist/dto/create-collaborator.dto.d.ts +0 -1
  26. package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
  27. package/dist/dto/create-collaborator.dto.js +0 -6
  28. package/dist/dto/create-collaborator.dto.js.map +1 -1
  29. package/dist/dto/create-cost-type.dto.d.ts +13 -0
  30. package/dist/dto/create-cost-type.dto.d.ts.map +1 -0
  31. package/dist/dto/create-cost-type.dto.js +87 -0
  32. package/dist/dto/create-cost-type.dto.js.map +1 -0
  33. package/dist/dto/list-approvals.dto.d.ts +2 -0
  34. package/dist/dto/list-approvals.dto.d.ts.map +1 -1
  35. package/dist/dto/list-approvals.dto.js +10 -0
  36. package/dist/dto/list-approvals.dto.js.map +1 -1
  37. package/dist/dto/list-collaborator-costs.dto.d.ts +5 -0
  38. package/dist/dto/list-collaborator-costs.dto.d.ts.map +1 -0
  39. package/dist/dto/list-collaborator-costs.dto.js +23 -0
  40. package/dist/dto/list-collaborator-costs.dto.js.map +1 -0
  41. package/dist/dto/list-cost-types.dto.d.ts +6 -0
  42. package/dist/dto/list-cost-types.dto.d.ts.map +1 -0
  43. package/dist/dto/list-cost-types.dto.js +35 -0
  44. package/dist/dto/list-cost-types.dto.js.map +1 -0
  45. package/dist/dto/list-my-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-my-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-my-projects.dto.js +23 -0
  48. package/dist/dto/list-my-projects.dto.js.map +1 -0
  49. package/dist/dto/list-my-tasks.dto.d.ts +6 -0
  50. package/dist/dto/list-my-tasks.dto.d.ts.map +1 -0
  51. package/dist/dto/list-my-tasks.dto.js +33 -0
  52. package/dist/dto/list-my-tasks.dto.js.map +1 -0
  53. package/dist/dto/list-projects.dto.d.ts +1 -0
  54. package/dist/dto/list-projects.dto.d.ts.map +1 -1
  55. package/dist/dto/list-projects.dto.js +7 -0
  56. package/dist/dto/list-projects.dto.js.map +1 -1
  57. package/dist/dto/list-reports.dto.d.ts +16 -0
  58. package/dist/dto/list-reports.dto.d.ts.map +1 -0
  59. package/dist/dto/list-reports.dto.js +75 -0
  60. package/dist/dto/list-reports.dto.js.map +1 -0
  61. package/dist/dto/list-tasks.dto.d.ts +2 -0
  62. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  63. package/dist/dto/list-tasks.dto.js +12 -0
  64. package/dist/dto/list-tasks.dto.js.map +1 -1
  65. package/dist/dto/list-timesheets.dto.d.ts +2 -0
  66. package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
  67. package/dist/dto/list-timesheets.dto.js +10 -0
  68. package/dist/dto/list-timesheets.dto.js.map +1 -1
  69. package/dist/dto/update-collaborator-cost.dto.d.ts +6 -0
  70. package/dist/dto/update-collaborator-cost.dto.d.ts.map +1 -0
  71. package/dist/dto/update-collaborator-cost.dto.js +9 -0
  72. package/dist/dto/update-collaborator-cost.dto.js.map +1 -0
  73. package/dist/dto/update-task.dto.d.ts +1 -0
  74. package/dist/dto/update-task.dto.d.ts.map +1 -1
  75. package/dist/dto/update-task.dto.js +6 -0
  76. package/dist/dto/update-task.dto.js.map +1 -1
  77. package/dist/operations.module.d.ts.map +1 -1
  78. package/dist/operations.module.js +4 -0
  79. package/dist/operations.module.js.map +1 -1
  80. package/dist/operations.service.d.ts +457 -3
  81. package/dist/operations.service.d.ts.map +1 -1
  82. package/dist/operations.service.js +1445 -208
  83. package/dist/operations.service.js.map +1 -1
  84. package/dist/operations.service.spec.js +31 -7
  85. package/dist/operations.service.spec.js.map +1 -1
  86. package/hedhog/data/menu.yaml +112 -7
  87. package/hedhog/data/operations_cost_type.yaml +166 -0
  88. package/hedhog/data/route.yaml +185 -0
  89. package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +884 -0
  90. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +94 -15
  91. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +219 -94
  92. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +21 -32
  93. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +178 -89
  94. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +1185 -0
  95. package/hedhog/frontend/app/_components/operations-calendar-view.tsx.ejs +306 -0
  96. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +943 -782
  97. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +223 -0
  98. package/hedhog/frontend/app/_lib/api.ts.ejs +162 -0
  99. package/hedhog/frontend/app/_lib/types.ts.ejs +229 -3
  100. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +11 -3
  101. package/hedhog/frontend/app/approvals/page.tsx.ejs +191 -46
  102. package/hedhog/frontend/app/collaborators/page.tsx.ejs +133 -25
  103. package/hedhog/frontend/app/my-projects/[id]/page.tsx.ejs +11 -0
  104. package/hedhog/frontend/app/my-projects/page.tsx.ejs +440 -0
  105. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +1304 -0
  106. package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +771 -0
  107. package/hedhog/frontend/app/reports/projects/page.tsx.ejs +809 -0
  108. package/hedhog/frontend/app/timesheets/page.tsx.ejs +322 -58
  109. package/hedhog/frontend/messages/en.json +234 -25
  110. package/hedhog/frontend/messages/pt.json +234 -25
  111. package/hedhog/table/operations_collaborator.yaml +0 -4
  112. package/hedhog/table/operations_collaborator_compensation_history.yaml +28 -0
  113. package/hedhog/table/operations_collaborator_cost.yaml +56 -0
  114. package/hedhog/table/operations_cost_type.yaml +38 -0
  115. package/package.json +6 -6
  116. package/src/controllers/operations-collaborator-costs.controller.ts +147 -0
  117. package/src/controllers/operations-collaborators.controller.ts +19 -8
  118. package/src/controllers/operations-projects.controller.ts +19 -8
  119. package/src/controllers/operations-reports.controller.ts +32 -0
  120. package/src/controllers/operations-tasks.controller.ts +32 -12
  121. package/src/dto/create-collaborator-cost.dto.ts +78 -0
  122. package/src/dto/create-collaborator.dto.ts +9 -14
  123. package/src/dto/create-cost-type.dto.ts +62 -0
  124. package/src/dto/list-approvals.dto.ts +8 -0
  125. package/src/dto/list-collaborator-costs.dto.ts +8 -0
  126. package/src/dto/list-cost-types.dto.ts +19 -0
  127. package/src/dto/list-my-projects.dto.ts +8 -0
  128. package/src/dto/list-my-tasks.dto.ts +17 -0
  129. package/src/dto/list-projects.dto.ts +7 -1
  130. package/src/dto/list-reports.dto.ts +51 -0
  131. package/src/dto/list-tasks.dto.ts +11 -1
  132. package/src/dto/list-timesheets.dto.ts +8 -0
  133. package/src/dto/update-collaborator-cost.dto.ts +4 -0
  134. package/src/dto/update-task.dto.ts +6 -0
  135. package/src/operations.module.ts +7 -3
  136. package/src/operations.service.spec.ts +45 -7
  137. package/src/operations.service.ts +1992 -225
@@ -0,0 +1,306 @@
1
+ 'use client';
2
+
3
+ import { Button } from '@/components/ui/button';
4
+ import { cn } from '@/lib/utils';
5
+ import {
6
+ addDays,
7
+ addMonths,
8
+ eachDayOfInterval,
9
+ endOfWeek,
10
+ isSameDay,
11
+ isSameMonth,
12
+ startOfMonth,
13
+ startOfWeek,
14
+ subMonths,
15
+ } from 'date-fns';
16
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
17
+ import { useEffect, useMemo, useRef, useState } from 'react';
18
+
19
+ type CalendarItem = {
20
+ id: number | string;
21
+ date: string;
22
+ title: string;
23
+ subtitle?: string | null;
24
+ meta?: string | null;
25
+ kind?: string | null;
26
+ statusLabel?: string | null;
27
+ badgeClassName?: string | null;
28
+ };
29
+
30
+ function formatMonthLabel(date: Date, locale: string) {
31
+ return new Intl.DateTimeFormat(locale, {
32
+ month: 'long',
33
+ year: 'numeric',
34
+ }).format(date);
35
+ }
36
+
37
+ function formatDayNumber(date: Date, locale: string) {
38
+ return new Intl.DateTimeFormat(locale, {
39
+ day: 'numeric',
40
+ }).format(date);
41
+ }
42
+
43
+ function formatDateKey(date: Date) {
44
+ const year = date.getFullYear();
45
+ const month = String(date.getMonth() + 1).padStart(2, '0');
46
+ const day = String(date.getDate()).padStart(2, '0');
47
+ return `${year}-${month}-${day}`;
48
+ }
49
+
50
+ function buildWeekdayLabels(locale: string) {
51
+ const monday = new Date(Date.UTC(2024, 0, 1, 12, 0, 0));
52
+
53
+ return Array.from({ length: 7 }, (_, index) =>
54
+ new Intl.DateTimeFormat(locale, { weekday: 'short' })
55
+ .format(addDays(monday, index))
56
+ .slice(0, 3)
57
+ );
58
+ }
59
+
60
+ export function OperationsCalendarView({
61
+ locale,
62
+ items,
63
+ emptyLabel,
64
+ onOpenItem,
65
+ onDateSelect,
66
+ actionLabel,
67
+ onMonthChange,
68
+ }: {
69
+ locale: string;
70
+ items: CalendarItem[];
71
+ emptyLabel: string;
72
+ onOpenItem: (item: CalendarItem) => void;
73
+ onDateSelect?: (date: string, items: CalendarItem[]) => void;
74
+ actionLabel: string;
75
+ onMonthChange?: (year: number, month: number) => void;
76
+ }) {
77
+ const [currentMonth, setCurrentMonth] = useState(() => new Date());
78
+ const [selectedDate, setSelectedDate] = useState<Date | null>(null);
79
+ const weekdayLabels = useMemo(() => buildWeekdayLabels(locale), [locale]);
80
+
81
+ const onMonthChangeRef = useRef(onMonthChange);
82
+ onMonthChangeRef.current = onMonthChange;
83
+
84
+ useEffect(() => {
85
+ onMonthChangeRef.current?.(
86
+ currentMonth.getFullYear(),
87
+ currentMonth.getMonth() + 1
88
+ );
89
+ }, [currentMonth]);
90
+
91
+ const itemMap = useMemo(() => {
92
+ const mapped = new Map<string, CalendarItem[]>();
93
+
94
+ items.forEach((item) => {
95
+ const key = item.date;
96
+ const current = mapped.get(key) ?? [];
97
+ current.push(item);
98
+ mapped.set(key, current);
99
+ });
100
+
101
+ return mapped;
102
+ }, [items]);
103
+
104
+ const gridStart = startOfWeek(startOfMonth(currentMonth), {
105
+ weekStartsOn: 1,
106
+ });
107
+ const gridEnd = endOfWeek(addDays(gridStart, 41), {
108
+ weekStartsOn: 1,
109
+ });
110
+ const days = eachDayOfInterval({ start: gridStart, end: gridEnd });
111
+
112
+ const selectedItems = selectedDate
113
+ ? (itemMap.get(formatDateKey(selectedDate)) ?? [])
114
+ : [];
115
+
116
+ return (
117
+ <div className="space-y-4">
118
+ <div className="flex items-center justify-between rounded-2xl border border-border/70 bg-muted/20 px-3 py-2.5">
119
+ <Button
120
+ type="button"
121
+ variant="ghost"
122
+ size="icon"
123
+ className="cursor-pointer rounded-full"
124
+ onClick={() => {
125
+ setCurrentMonth((current) => subMonths(current, 1));
126
+ setSelectedDate(null);
127
+ }}
128
+ >
129
+ <ChevronLeft className="size-4" />
130
+ </Button>
131
+ <div className="text-sm font-semibold capitalize text-foreground">
132
+ {formatMonthLabel(currentMonth, locale)}
133
+ </div>
134
+ <Button
135
+ type="button"
136
+ variant="ghost"
137
+ size="icon"
138
+ className="cursor-pointer rounded-full"
139
+ onClick={() => {
140
+ setCurrentMonth((current) => addMonths(current, 1));
141
+ setSelectedDate(null);
142
+ }}
143
+ >
144
+ <ChevronRight className="size-4" />
145
+ </Button>
146
+ </div>
147
+
148
+ <div className="rounded-3xl border border-border/70 bg-card p-3 shadow-xs lg:p-4">
149
+ <div className="mb-2 grid grid-cols-7">
150
+ {weekdayLabels.map((label, index) => (
151
+ <div
152
+ key={`${label}-${index}`}
153
+ className="py-1 text-center text-[11px] font-medium uppercase tracking-wide text-muted-foreground"
154
+ >
155
+ {label}
156
+ </div>
157
+ ))}
158
+ </div>
159
+
160
+ <div className="grid grid-cols-7 gap-1 lg:gap-2">
161
+ {days.map((day) => {
162
+ const key = formatDateKey(day);
163
+ const dayItems = itemMap.get(key) ?? [];
164
+ const inMonth = isSameMonth(day, currentMonth);
165
+ const isSelected =
166
+ selectedDate !== null && isSameDay(day, selectedDate);
167
+
168
+ return (
169
+ <button
170
+ key={key}
171
+ type="button"
172
+ disabled={!inMonth}
173
+ onClick={() => {
174
+ const nextDate =
175
+ selectedDate !== null && isSameDay(selectedDate, day)
176
+ ? null
177
+ : day;
178
+ setSelectedDate(nextDate);
179
+ if (nextDate) {
180
+ onDateSelect?.(key, dayItems);
181
+ }
182
+ }}
183
+ className={cn(
184
+ 'min-h-24 rounded-2xl border border-border/60 p-2 text-left transition-colors lg:min-h-32',
185
+ inMonth
186
+ ? 'cursor-pointer bg-background/90 hover:border-border hover:bg-muted/30'
187
+ : 'cursor-default bg-muted/20 opacity-35',
188
+ isSelected && 'ring-1 ring-primary/35'
189
+ )}
190
+ >
191
+ <div className="flex items-start justify-between gap-2">
192
+ <span
193
+ className={cn(
194
+ 'flex size-8 items-center justify-center rounded-full text-sm',
195
+ isSelected
196
+ ? 'bg-primary font-semibold text-primary-foreground'
197
+ : 'text-foreground'
198
+ )}
199
+ >
200
+ {formatDayNumber(day, locale)}
201
+ </span>
202
+ {dayItems.length ? (
203
+ <span className="rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-semibold text-primary">
204
+ {dayItems.length}
205
+ </span>
206
+ ) : null}
207
+ </div>
208
+
209
+ {inMonth && dayItems.length ? (
210
+ <div className="mt-2 hidden space-y-1 lg:block">
211
+ {dayItems.slice(0, 3).map((item) => (
212
+ <div
213
+ key={`${item.id}-${item.kind ?? 'item'}`}
214
+ className={cn(
215
+ 'truncate rounded-md px-2 py-1 text-[10px] font-medium',
216
+ item.badgeClassName ?? 'bg-muted text-foreground'
217
+ )}
218
+ title={item.title}
219
+ >
220
+ {item.title}
221
+ </div>
222
+ ))}
223
+ {dayItems.length > 3 ? (
224
+ <div className="text-center text-[10px] text-muted-foreground">
225
+ +{dayItems.length - 3}
226
+ </div>
227
+ ) : null}
228
+ </div>
229
+ ) : null}
230
+
231
+ {inMonth && dayItems.length ? (
232
+ <div className="mt-2 flex justify-center gap-1 lg:hidden">
233
+ {dayItems.slice(0, 3).map((item) => (
234
+ <span
235
+ key={`${item.id}-${item.kind ?? 'item'}`}
236
+ className={cn(
237
+ 'size-1.5 rounded-full',
238
+ item.badgeClassName?.split(' ')[0] ?? 'bg-primary'
239
+ )}
240
+ />
241
+ ))}
242
+ </div>
243
+ ) : null}
244
+ </button>
245
+ );
246
+ })}
247
+ </div>
248
+ </div>
249
+
250
+ <div className="rounded-2xl border border-border/70 bg-card p-4">
251
+ {selectedDate && selectedItems.length ? (
252
+ <div className="space-y-3">
253
+ {selectedItems.map((item) => (
254
+ <div
255
+ key={`${item.id}-${item.kind ?? 'item'}`}
256
+ className="flex flex-col gap-3 rounded-xl border border-border/60 px-4 py-3 lg:flex-row lg:items-center lg:justify-between"
257
+ >
258
+ <div className="min-w-0 space-y-1">
259
+ <div className="flex flex-wrap items-center gap-2">
260
+ <div className="truncate font-medium text-foreground">
261
+ {item.title}
262
+ </div>
263
+ {item.statusLabel ? (
264
+ <span
265
+ className={cn(
266
+ 'rounded-full px-2 py-0.5 text-[11px] font-medium',
267
+ item.badgeClassName ?? 'bg-muted text-foreground'
268
+ )}
269
+ >
270
+ {item.statusLabel}
271
+ </span>
272
+ ) : null}
273
+ </div>
274
+ {item.subtitle ? (
275
+ <div className="text-sm text-muted-foreground">
276
+ {item.subtitle}
277
+ </div>
278
+ ) : null}
279
+ {item.meta ? (
280
+ <div className="text-xs text-muted-foreground">
281
+ {item.meta}
282
+ </div>
283
+ ) : null}
284
+ </div>
285
+
286
+ <Button
287
+ type="button"
288
+ variant="outline"
289
+ size="sm"
290
+ className="cursor-pointer"
291
+ onClick={() => onOpenItem(item)}
292
+ >
293
+ {actionLabel}
294
+ </Button>
295
+ </div>
296
+ ))}
297
+ </div>
298
+ ) : (
299
+ <div className="text-sm text-muted-foreground">{emptyLabel}</div>
300
+ )}
301
+ </div>
302
+ </div>
303
+ );
304
+ }
305
+
306
+ export type { CalendarItem as OperationsCalendarItem };