@hed-hog/operations 0.0.332 → 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 (109) hide show
  1. package/dist/controllers/operations-collaborators.controller.d.ts +0 -54
  2. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-collaborators.controller.js +0 -100
  4. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  5. package/dist/controllers/operations-contracts.controller.d.ts +12 -12
  6. package/dist/operations.service.d.ts +0 -76
  7. package/dist/operations.service.d.ts.map +1 -1
  8. package/dist/operations.service.js +7 -230
  9. package/dist/operations.service.js.map +1 -1
  10. package/dist/operations.service.spec.js +6 -0
  11. package/dist/operations.service.spec.js.map +1 -1
  12. package/hedhog/data/menu.yaml +8 -27
  13. package/hedhog/data/route.yaml +0 -72
  14. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +476 -0
  15. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +3 -39
  16. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +261 -0
  17. package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -358
  18. package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +6 -6
  19. package/hedhog/frontend/app/_components/contract-content-editor.tsx.ejs +258 -0
  20. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +5 -4
  21. package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -0
  22. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +10 -218
  23. package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +23 -23
  24. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +24 -708
  25. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +38 -158
  26. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +1 -5
  27. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -629
  28. package/hedhog/frontend/app/_lib/api.ts.ejs +0 -151
  29. package/hedhog/frontend/app/_lib/types.ts.ejs +0 -1
  30. package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +0 -18
  31. package/hedhog/frontend/app/my-projects/page.tsx.ejs +2 -16
  32. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +86 -24
  33. package/hedhog/frontend/app/projects/page.tsx.ejs +6 -42
  34. package/hedhog/frontend/messages/en.json +2 -96
  35. package/hedhog/frontend/messages/operations/operations/en.json +2100 -0
  36. package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
  37. package/hedhog/frontend/messages/pt.json +2 -96
  38. package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +16 -16
  39. package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +16 -16
  40. package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +16 -16
  41. package/hedhog/frontend/widgets/index.ts.ejs +25 -25
  42. package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +16 -16
  43. package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +16 -16
  44. package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +16 -16
  45. package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +16 -16
  46. package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +16 -16
  47. package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +16 -16
  48. package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +16 -16
  49. package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +16 -16
  50. package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +16 -16
  51. package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +16 -16
  52. package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +16 -16
  53. package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +16 -16
  54. package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +16 -16
  55. package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +16 -16
  56. package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +169 -169
  57. package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +16 -16
  58. package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +16 -16
  59. package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +16 -16
  60. package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +16 -16
  61. package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +16 -16
  62. package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +16 -16
  63. package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +16 -16
  64. package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +16 -16
  65. package/hedhog/table/operations_collaborator.yaml +8 -8
  66. package/hedhog/table/operations_task.yaml +76 -76
  67. package/hedhog/table/operations_task_activity.yaml +51 -51
  68. package/package.json +6 -6
  69. package/src/controllers/operations-collaborators.controller.ts +8 -117
  70. package/src/controllers/operations-tasks.controller.ts +156 -156
  71. package/src/dashboard/widgets/MyQuickActions.tsx +22 -22
  72. package/src/dto/create-collaborator.dto.ts +4 -4
  73. package/src/operations.service.spec.ts +1006 -988
  74. package/src/operations.service.ts +7 -323
  75. package/dist/dto/create-collaborator-invoice.dto.d.ts +0 -11
  76. package/dist/dto/create-collaborator-invoice.dto.d.ts.map +0 -1
  77. package/dist/dto/create-collaborator-invoice.dto.js +0 -55
  78. package/dist/dto/create-collaborator-invoice.dto.js.map +0 -1
  79. package/dist/dto/create-collaborator-payment.dto.d.ts +0 -10
  80. package/dist/dto/create-collaborator-payment.dto.d.ts.map +0 -1
  81. package/dist/dto/create-collaborator-payment.dto.js +0 -50
  82. package/dist/dto/create-collaborator-payment.dto.js.map +0 -1
  83. package/dist/dto/list-collaborator-invoice.dto.d.ts +0 -4
  84. package/dist/dto/list-collaborator-invoice.dto.d.ts.map +0 -1
  85. package/dist/dto/list-collaborator-invoice.dto.js +0 -8
  86. package/dist/dto/list-collaborator-invoice.dto.js.map +0 -1
  87. package/dist/dto/list-collaborator-payment.dto.d.ts +0 -4
  88. package/dist/dto/list-collaborator-payment.dto.d.ts.map +0 -1
  89. package/dist/dto/list-collaborator-payment.dto.js +0 -8
  90. package/dist/dto/list-collaborator-payment.dto.js.map +0 -1
  91. package/dist/dto/update-collaborator-invoice.dto.d.ts +0 -6
  92. package/dist/dto/update-collaborator-invoice.dto.d.ts.map +0 -1
  93. package/dist/dto/update-collaborator-invoice.dto.js +0 -9
  94. package/dist/dto/update-collaborator-invoice.dto.js.map +0 -1
  95. package/dist/dto/update-collaborator-payment.dto.d.ts +0 -6
  96. package/dist/dto/update-collaborator-payment.dto.d.ts.map +0 -1
  97. package/dist/dto/update-collaborator-payment.dto.js +0 -9
  98. package/dist/dto/update-collaborator-payment.dto.js.map +0 -1
  99. package/hedhog/frontend/app/_components/collaborator-invoices-tab.tsx.ejs +0 -443
  100. package/hedhog/frontend/app/_components/collaborator-payment-history-tab.tsx.ejs +0 -429
  101. package/hedhog/frontend/app/tasks-gantt/page.tsx.ejs +0 -953
  102. package/hedhog/table/operations_collaborator_invoice.yaml +0 -35
  103. package/hedhog/table/operations_collaborator_payment.yaml +0 -32
  104. package/src/dto/create-collaborator-invoice.dto.ts +0 -39
  105. package/src/dto/create-collaborator-payment.dto.ts +0 -35
  106. package/src/dto/list-collaborator-invoice.dto.ts +0 -3
  107. package/src/dto/list-collaborator-payment.dto.ts +0 -3
  108. package/src/dto/update-collaborator-invoice.dto.ts +0 -6
  109. package/src/dto/update-collaborator-payment.dto.ts +0 -6
@@ -1,443 +0,0 @@
1
- 'use client';
2
-
3
- import { Badge } from '@/components/ui/badge';
4
- import { Button } from '@/components/ui/button';
5
- import { Input } from '@/components/ui/input';
6
- import { Label } from '@/components/ui/label';
7
- import {
8
- Select,
9
- SelectContent,
10
- SelectItem,
11
- SelectTrigger,
12
- SelectValue,
13
- } from '@/components/ui/select';
14
- import {
15
- Table,
16
- TableBody,
17
- TableCell,
18
- TableHead,
19
- TableHeader,
20
- TableRow,
21
- } from '@/components/ui/table';
22
- import { Textarea } from '@/components/ui/textarea';
23
- import { useApp, useQuery } from '@hed-hog/next-app-provider';
24
- import { FileTextIcon, Pencil, Plus, Trash2, X } from 'lucide-react';
25
- import { useTranslations } from 'next-intl';
26
- import { useState } from 'react';
27
- import {
28
- type CollaboratorInvoice,
29
- createCollaboratorInvoice,
30
- deleteCollaboratorInvoice,
31
- fetchCollaboratorInvoices,
32
- updateCollaboratorInvoice,
33
- } from '../_lib/api';
34
- import { formatCurrency, formatDate } from '../_lib/utils/format';
35
-
36
- const INVOICE_STATUSES = ['pending', 'paid', 'cancelled', 'overdue'] as const;
37
- type InvoiceStatus = (typeof INVOICE_STATUSES)[number];
38
-
39
- const STATUS_VARIANT: Record<
40
- InvoiceStatus,
41
- 'default' | 'secondary' | 'destructive' | 'outline'
42
- > = {
43
- pending: 'secondary',
44
- paid: 'default',
45
- cancelled: 'outline',
46
- overdue: 'destructive',
47
- };
48
-
49
- type FormState = {
50
- invoiceNumber: string;
51
- amount: string;
52
- issueDate: string;
53
- dueDate: string;
54
- status: InvoiceStatus;
55
- description: string;
56
- };
57
-
58
- function emptyForm(): FormState {
59
- return {
60
- invoiceNumber: '',
61
- amount: '',
62
- issueDate: '',
63
- dueDate: '',
64
- status: 'pending',
65
- description: '',
66
- };
67
- }
68
-
69
- function invoiceToForm(inv: CollaboratorInvoice): FormState {
70
- return {
71
- invoiceNumber: inv.invoiceNumber ?? '',
72
- amount: inv.amount ? Number(inv.amount).toFixed(2).replace('.', ',') : '',
73
- issueDate: inv.issueDate ? inv.issueDate.slice(0, 10) : '',
74
- dueDate: inv.dueDate ? inv.dueDate.slice(0, 10) : '',
75
- status: (inv.status as InvoiceStatus) ?? 'pending',
76
- description: inv.description ?? '',
77
- };
78
- }
79
-
80
- type Props = {
81
- collaboratorId: number;
82
- disabled?: boolean;
83
- };
84
-
85
- export function CollaboratorInvoicesTab({
86
- collaboratorId,
87
- disabled = false,
88
- }: Props) {
89
- const t = useTranslations('operations.CollaboratorFormPage');
90
- const commonT = useTranslations('operations.Common');
91
- const { request, showToastHandler, getSettingValue, currentLocaleCode } =
92
- useApp();
93
-
94
- const [isAdding, setIsAdding] = useState(false);
95
- const [editingId, setEditingId] = useState<number | null>(null);
96
- const [confirmDeleteId, setConfirmDeleteId] = useState<number | null>(null);
97
- const [form, setForm] = useState<FormState>(emptyForm());
98
- const [saving, setSaving] = useState(false);
99
-
100
- const {
101
- data: invoices = [],
102
- isLoading,
103
- refetch,
104
- } = useQuery<CollaboratorInvoice[]>({
105
- queryKey: [
106
- 'operations-collaborator-invoices',
107
- currentLocaleCode,
108
- collaboratorId,
109
- ],
110
- staleTime: 0,
111
- refetchOnMount: 'always',
112
- queryFn: () => fetchCollaboratorInvoices(request, collaboratorId),
113
- });
114
-
115
- function openAdd() {
116
- setEditingId(null);
117
- setForm(emptyForm());
118
- setIsAdding(true);
119
- }
120
-
121
- function openEdit(inv: CollaboratorInvoice) {
122
- setIsAdding(false);
123
- setForm(invoiceToForm(inv));
124
- setEditingId(inv.id);
125
- }
126
-
127
- function cancelForm() {
128
- setIsAdding(false);
129
- setEditingId(null);
130
- setForm(emptyForm());
131
- }
132
-
133
- async function handleSave() {
134
- const amount = parseFloat(form.amount.replace(',', '.'));
135
- if (!form.issueDate || isNaN(amount) || amount <= 0) return;
136
-
137
- setSaving(true);
138
- try {
139
- const data = {
140
- invoiceNumber: form.invoiceNumber || null,
141
- amount,
142
- issueDate: form.issueDate,
143
- dueDate: form.dueDate || null,
144
- status: form.status,
145
- description: form.description || null,
146
- };
147
-
148
- if (editingId !== null) {
149
- await updateCollaboratorInvoice(
150
- request,
151
- collaboratorId,
152
- editingId,
153
- data
154
- );
155
- } else {
156
- await createCollaboratorInvoice(request, collaboratorId, data);
157
- }
158
-
159
- showToastHandler?.('success', t('messages.saveSuccess'));
160
- cancelForm();
161
- await refetch();
162
- } catch {
163
- showToastHandler?.('error', t('messages.saveError'));
164
- } finally {
165
- setSaving(false);
166
- }
167
- }
168
-
169
- async function handleDelete(invoiceId: number) {
170
- setSaving(true);
171
- try {
172
- await deleteCollaboratorInvoice(request, collaboratorId, invoiceId);
173
- showToastHandler?.('success', t('messages.deleteSuccess'));
174
- setConfirmDeleteId(null);
175
- await refetch();
176
- } catch {
177
- showToastHandler?.('error', t('messages.deleteError'));
178
- } finally {
179
- setSaving(false);
180
- }
181
- }
182
-
183
- const statusLabel = (s: string) => {
184
- const key =
185
- `invoices.status${s.charAt(0).toUpperCase() + s.slice(1)}` as Parameters<
186
- typeof t
187
- >[0];
188
- try {
189
- return t(key);
190
- } catch {
191
- return s;
192
- }
193
- };
194
-
195
- const showForm = isAdding || editingId !== null;
196
-
197
- return (
198
- <div className="space-y-4">
199
- {!disabled && (
200
- <div className="flex justify-end">
201
- <Button
202
- size="sm"
203
- variant="outline"
204
- onClick={openAdd}
205
- disabled={showForm}
206
- >
207
- <Plus className="mr-1 size-4" />
208
- {t('invoices.add')}
209
- </Button>
210
- </div>
211
- )}
212
-
213
- {showForm && (
214
- <div className="rounded-md border bg-muted/30 p-4">
215
- <p className="mb-3 text-sm font-medium">
216
- {editingId !== null ? t('invoices.editTitle') : t('invoices.add')}
217
- </p>
218
- <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
219
- <div className="space-y-1">
220
- <Label className="text-xs">{t('invoices.invoiceNumber')}</Label>
221
- <Input
222
- value={form.invoiceNumber}
223
- onChange={(e) =>
224
- setForm((f) => ({ ...f, invoiceNumber: e.target.value }))
225
- }
226
- />
227
- </div>
228
- <div className="space-y-1">
229
- <Label className="text-xs">{t('invoices.amount')} *</Label>
230
- <Input
231
- type="text"
232
- inputMode="numeric"
233
- placeholder="0,00"
234
- value={form.amount}
235
- onChange={(e) => {
236
- const digits = e.target.value.replace(/\D/g, '');
237
- if (!digits) {
238
- setForm((f) => ({ ...f, amount: '' }));
239
- return;
240
- }
241
- const cents = parseInt(digits, 10);
242
- const formatted = (cents / 100).toFixed(2).replace('.', ',');
243
- setForm((f) => ({ ...f, amount: formatted }));
244
- }}
245
- />
246
- </div>
247
- <div className="space-y-1">
248
- <Label className="text-xs">{t('invoices.issueDate')} *</Label>
249
- <Input
250
- type="date"
251
- value={form.issueDate}
252
- onChange={(e) =>
253
- setForm((f) => ({ ...f, issueDate: e.target.value }))
254
- }
255
- />
256
- </div>
257
- <div className="space-y-1">
258
- <Label className="text-xs">{t('invoices.dueDate')}</Label>
259
- <Input
260
- type="date"
261
- value={form.dueDate}
262
- onChange={(e) =>
263
- setForm((f) => ({ ...f, dueDate: e.target.value }))
264
- }
265
- />
266
- </div>
267
- <div className="space-y-1 col-span-2">
268
- <Label className="text-xs">{t('invoices.status')}</Label>
269
- <Select
270
- value={form.status}
271
- onValueChange={(v) =>
272
- setForm((f) => ({ ...f, status: v as InvoiceStatus }))
273
- }
274
- >
275
- <SelectTrigger className="w-full cursor-pointer">
276
- <SelectValue />
277
- </SelectTrigger>
278
- <SelectContent>
279
- {INVOICE_STATUSES.map((s) => (
280
- <SelectItem key={s} value={s} className="cursor-pointer">
281
- {statusLabel(s)}
282
- </SelectItem>
283
- ))}
284
- </SelectContent>
285
- </Select>
286
- </div>
287
- <div className="space-y-1 sm:col-span-2">
288
- <Label className="text-xs">{t('invoices.description')}</Label>
289
- <Textarea
290
- rows={2}
291
- value={form.description}
292
- onChange={(e) =>
293
- setForm((f) => ({ ...f, description: e.target.value }))
294
- }
295
- />
296
- </div>
297
- </div>
298
- <div className="mt-3 flex justify-end gap-2">
299
- <Button
300
- size="sm"
301
- variant="ghost"
302
- onClick={cancelForm}
303
- disabled={saving}
304
- >
305
- <X className="mr-1 size-4" />
306
- {commonT('actions.cancel')}
307
- </Button>
308
- <Button size="sm" onClick={handleSave} disabled={saving}>
309
- {commonT('actions.save')}
310
- </Button>
311
- </div>
312
- </div>
313
- )}
314
-
315
- {isLoading ? (
316
- <p className="text-sm text-muted-foreground">{t('loading')}</p>
317
- ) : invoices.length === 0 && !showForm ? (
318
- <div className="flex flex-col items-center gap-3 py-10 text-center">
319
- <FileTextIcon className="size-10 text-muted-foreground/40" />
320
- <p className="text-sm text-muted-foreground">
321
- {t('tabs.invoicesEmpty')}
322
- </p>
323
- </div>
324
- ) : invoices.length > 0 ? (
325
- <div className="overflow-x-auto rounded-md border">
326
- <Table>
327
- <TableHeader>
328
- <TableRow>
329
- <TableHead>{t('invoices.invoiceNumber')}</TableHead>
330
- <TableHead>{t('invoices.amount')}</TableHead>
331
- <TableHead>{t('invoices.issueDate')}</TableHead>
332
- <TableHead>{t('invoices.dueDate')}</TableHead>
333
- <TableHead>{t('invoices.status')}</TableHead>
334
- <TableHead>{t('invoices.description')}</TableHead>
335
- {!disabled && (
336
- <TableHead className="w-20 text-right"></TableHead>
337
- )}
338
- </TableRow>
339
- </TableHeader>
340
- <TableBody>
341
- {invoices.map((invoice) => (
342
- <TableRow key={invoice.id}>
343
- <TableCell className="font-medium">
344
- {invoice.invoiceNumber ?? '—'}
345
- </TableCell>
346
- <TableCell>
347
- {formatCurrency(
348
- Number(invoice.amount),
349
- getSettingValue,
350
- currentLocaleCode
351
- )}
352
- </TableCell>
353
- <TableCell>
354
- {invoice.issueDate
355
- ? formatDate(
356
- invoice.issueDate,
357
- getSettingValue,
358
- currentLocaleCode
359
- )
360
- : '—'}
361
- </TableCell>
362
- <TableCell>
363
- {invoice.dueDate
364
- ? formatDate(
365
- invoice.dueDate,
366
- getSettingValue,
367
- currentLocaleCode
368
- )
369
- : '—'}
370
- </TableCell>
371
- <TableCell>
372
- {invoice.status ? (
373
- <Badge
374
- variant={
375
- STATUS_VARIANT[invoice.status as InvoiceStatus] ??
376
- 'secondary'
377
- }
378
- >
379
- {statusLabel(invoice.status)}
380
- </Badge>
381
- ) : (
382
- '—'
383
- )}
384
- </TableCell>
385
- <TableCell className="text-muted-foreground">
386
- {invoice.description ?? '—'}
387
- </TableCell>
388
- {!disabled && (
389
- <TableCell className="text-right">
390
- {confirmDeleteId === invoice.id ? (
391
- <div className="flex items-center justify-end gap-1">
392
- <span className="text-xs text-muted-foreground">
393
- {t('invoices.deleteConfirm')}
394
- </span>
395
- <Button
396
- size="sm"
397
- variant="destructive"
398
- className="h-7 px-2 text-xs cursor-pointer"
399
- disabled={saving}
400
- onClick={() => handleDelete(invoice.id)}
401
- >
402
- {commonT('actions.delete')}
403
- </Button>
404
- <Button
405
- size="sm"
406
- variant="ghost"
407
- className="h-7 px-2 text-xs cursor-pointer"
408
- onClick={() => setConfirmDeleteId(null)}
409
- >
410
- <X className="size-3" />
411
- </Button>
412
- </div>
413
- ) : (
414
- <div className="flex items-center justify-end gap-1">
415
- <Button
416
- size="icon"
417
- variant="ghost"
418
- className="size-7 cursor-pointer"
419
- onClick={() => openEdit(invoice)}
420
- >
421
- <Pencil className="size-3.5" />
422
- </Button>
423
- <Button
424
- size="icon"
425
- variant="ghost"
426
- className="size-7 cursor-pointer text-destructive hover:text-destructive"
427
- onClick={() => setConfirmDeleteId(invoice.id)}
428
- >
429
- <Trash2 className="size-3.5" />
430
- </Button>
431
- </div>
432
- )}
433
- </TableCell>
434
- )}
435
- </TableRow>
436
- ))}
437
- </TableBody>
438
- </Table>
439
- </div>
440
- ) : null}
441
- </div>
442
- );
443
- }