@hed-hog/operations 0.0.318 → 0.0.321
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.
- package/dist/controllers/operations-collaborator-costs.controller.d.ts +144 -0
- package/dist/controllers/operations-collaborator-costs.controller.d.ts.map +1 -0
- package/dist/controllers/operations-collaborator-costs.controller.js +162 -0
- package/dist/controllers/operations-collaborator-costs.controller.js.map +1 -0
- package/dist/controllers/operations-collaborators.controller.d.ts +14 -0
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +11 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +9 -9
- package/dist/controllers/operations-projects.controller.d.ts +31 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +23 -0
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-reports.controller.d.ts +199 -0
- package/dist/controllers/operations-reports.controller.d.ts.map +1 -0
- package/dist/controllers/operations-reports.controller.js +53 -0
- package/dist/controllers/operations-reports.controller.js.map +1 -0
- package/dist/controllers/operations-tasks.controller.d.ts +41 -2
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +17 -5
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/dto/create-collaborator-cost.dto.d.ts +16 -0
- package/dist/dto/create-collaborator-cost.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-cost.dto.js +88 -0
- package/dist/dto/create-collaborator-cost.dto.js.map +1 -0
- package/dist/dto/create-collaborator.dto.d.ts +0 -1
- package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
- package/dist/dto/create-collaborator.dto.js +0 -6
- package/dist/dto/create-collaborator.dto.js.map +1 -1
- package/dist/dto/create-cost-type.dto.d.ts +13 -0
- package/dist/dto/create-cost-type.dto.d.ts.map +1 -0
- package/dist/dto/create-cost-type.dto.js +87 -0
- package/dist/dto/create-cost-type.dto.js.map +1 -0
- package/dist/dto/list-approvals.dto.d.ts +2 -0
- package/dist/dto/list-approvals.dto.d.ts.map +1 -1
- package/dist/dto/list-approvals.dto.js +10 -0
- package/dist/dto/list-approvals.dto.js.map +1 -1
- package/dist/dto/list-collaborator-costs.dto.d.ts +5 -0
- package/dist/dto/list-collaborator-costs.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-costs.dto.js +23 -0
- package/dist/dto/list-collaborator-costs.dto.js.map +1 -0
- package/dist/dto/list-cost-types.dto.d.ts +6 -0
- package/dist/dto/list-cost-types.dto.d.ts.map +1 -0
- package/dist/dto/list-cost-types.dto.js +35 -0
- package/dist/dto/list-cost-types.dto.js.map +1 -0
- package/dist/dto/list-my-projects.dto.d.ts +5 -0
- package/dist/dto/list-my-projects.dto.d.ts.map +1 -0
- package/dist/dto/list-my-projects.dto.js +23 -0
- package/dist/dto/list-my-projects.dto.js.map +1 -0
- package/dist/dto/list-my-tasks.dto.d.ts +6 -0
- package/dist/dto/list-my-tasks.dto.d.ts.map +1 -0
- package/dist/dto/list-my-tasks.dto.js +33 -0
- package/dist/dto/list-my-tasks.dto.js.map +1 -0
- package/dist/dto/list-projects.dto.d.ts +1 -0
- package/dist/dto/list-projects.dto.d.ts.map +1 -1
- package/dist/dto/list-projects.dto.js +7 -0
- package/dist/dto/list-projects.dto.js.map +1 -1
- package/dist/dto/list-reports.dto.d.ts +16 -0
- package/dist/dto/list-reports.dto.d.ts.map +1 -0
- package/dist/dto/list-reports.dto.js +75 -0
- package/dist/dto/list-reports.dto.js.map +1 -0
- package/dist/dto/list-tasks.dto.d.ts +2 -0
- package/dist/dto/list-tasks.dto.d.ts.map +1 -1
- package/dist/dto/list-tasks.dto.js +12 -0
- package/dist/dto/list-tasks.dto.js.map +1 -1
- package/dist/dto/list-timesheets.dto.d.ts +2 -0
- package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
- package/dist/dto/list-timesheets.dto.js +10 -0
- package/dist/dto/list-timesheets.dto.js.map +1 -1
- package/dist/dto/update-collaborator-cost.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-cost.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-cost.dto.js +9 -0
- package/dist/dto/update-collaborator-cost.dto.js.map +1 -0
- package/dist/dto/update-task.dto.d.ts +1 -0
- package/dist/dto/update-task.dto.d.ts.map +1 -1
- package/dist/dto/update-task.dto.js +6 -0
- package/dist/dto/update-task.dto.js.map +1 -1
- package/dist/operations.module.d.ts.map +1 -1
- package/dist/operations.module.js +4 -0
- package/dist/operations.module.js.map +1 -1
- package/dist/operations.service.d.ts +457 -3
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1445 -208
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +31 -7
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +112 -7
- package/hedhog/data/operations_cost_type.yaml +166 -0
- package/hedhog/data/route.yaml +185 -0
- package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +884 -0
- package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +80 -1
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +219 -94
- package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +21 -32
- package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +178 -89
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +1185 -0
- package/hedhog/frontend/app/_components/operations-calendar-view.tsx.ejs +306 -0
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +943 -782
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +223 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +162 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +227 -1
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +11 -3
- package/hedhog/frontend/app/approvals/page.tsx.ejs +191 -46
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +133 -25
- package/hedhog/frontend/app/my-projects/[id]/page.tsx.ejs +11 -0
- package/hedhog/frontend/app/my-projects/page.tsx.ejs +440 -0
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +1304 -0
- package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +771 -0
- package/hedhog/frontend/app/reports/projects/page.tsx.ejs +809 -0
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +322 -58
- package/hedhog/frontend/messages/en.json +234 -25
- package/hedhog/frontend/messages/pt.json +234 -25
- package/hedhog/table/operations_collaborator.yaml +0 -4
- package/hedhog/table/operations_collaborator_compensation_history.yaml +28 -0
- package/hedhog/table/operations_collaborator_cost.yaml +56 -0
- package/hedhog/table/operations_cost_type.yaml +38 -0
- package/package.json +7 -7
- package/src/controllers/operations-collaborator-costs.controller.ts +147 -0
- package/src/controllers/operations-collaborators.controller.ts +19 -8
- package/src/controllers/operations-projects.controller.ts +19 -8
- package/src/controllers/operations-reports.controller.ts +32 -0
- package/src/controllers/operations-tasks.controller.ts +32 -12
- package/src/dto/create-collaborator-cost.dto.ts +78 -0
- package/src/dto/create-collaborator.dto.ts +9 -14
- package/src/dto/create-cost-type.dto.ts +62 -0
- package/src/dto/list-approvals.dto.ts +8 -0
- package/src/dto/list-collaborator-costs.dto.ts +8 -0
- package/src/dto/list-cost-types.dto.ts +19 -0
- package/src/dto/list-my-projects.dto.ts +8 -0
- package/src/dto/list-my-tasks.dto.ts +17 -0
- package/src/dto/list-projects.dto.ts +7 -1
- package/src/dto/list-reports.dto.ts +51 -0
- package/src/dto/list-tasks.dto.ts +11 -1
- package/src/dto/list-timesheets.dto.ts +8 -0
- package/src/dto/update-collaborator-cost.dto.ts +4 -0
- package/src/dto/update-task.dto.ts +6 -0
- package/src/operations.module.ts +4 -0
- package/src/operations.service.spec.ts +45 -7
- package/src/operations.service.ts +1988 -221
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
Sheet,
|
|
6
|
+
SheetContent,
|
|
7
|
+
SheetHeader,
|
|
8
|
+
SheetTitle,
|
|
9
|
+
} from '@/components/ui/sheet';
|
|
10
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
11
|
+
import { AlarmClock, Calendar, Tag, User } from 'lucide-react';
|
|
12
|
+
import { useTranslations } from 'next-intl';
|
|
13
|
+
import { formatDate, getStatusBadgeClass } from '../_lib/utils/format';
|
|
14
|
+
import { StatusBadge } from './status-badge';
|
|
15
|
+
|
|
16
|
+
export type TaskDetailSheetData = {
|
|
17
|
+
id: number;
|
|
18
|
+
name: string;
|
|
19
|
+
description?: string | null;
|
|
20
|
+
status: string;
|
|
21
|
+
priority?: string | null;
|
|
22
|
+
dueDate?: string | null;
|
|
23
|
+
estimateHours?: number | null;
|
|
24
|
+
tags?: string | null;
|
|
25
|
+
projectName?: string | null;
|
|
26
|
+
projectCode?: string | null;
|
|
27
|
+
assigneeName?: string | null;
|
|
28
|
+
assigneeUserPhotoId?: number | null;
|
|
29
|
+
assigneePersonAvatarId?: number | null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type Props = {
|
|
33
|
+
task: TaskDetailSheetData | null;
|
|
34
|
+
open: boolean;
|
|
35
|
+
onOpenChange: (open: boolean) => void;
|
|
36
|
+
statusLabel?: (status: string) => string;
|
|
37
|
+
footer?: ReactNode;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function getPriorityLabel(value?: string | null) {
|
|
41
|
+
const labels: Record<string, string> = {
|
|
42
|
+
low: 'Baixa',
|
|
43
|
+
medium: 'Média',
|
|
44
|
+
high: 'Alta',
|
|
45
|
+
};
|
|
46
|
+
return labels[value ?? ''] ?? value ?? '—';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getPriorityClassName(value?: string | null) {
|
|
50
|
+
if (value === 'high') return 'bg-rose-100 text-rose-700 border-rose-200';
|
|
51
|
+
if (value === 'medium') return 'bg-amber-100 text-amber-700 border-amber-200';
|
|
52
|
+
return 'bg-emerald-100 text-emerald-700 border-emerald-200';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getAvatarSrc(task: TaskDetailSheetData | null) {
|
|
56
|
+
if (!task) return null;
|
|
57
|
+
if (
|
|
58
|
+
typeof task.assigneeUserPhotoId === 'number' &&
|
|
59
|
+
task.assigneeUserPhotoId > 0
|
|
60
|
+
)
|
|
61
|
+
return `${process.env.NEXT_PUBLIC_API_BASE_URL}/file/open/${task.assigneeUserPhotoId}`;
|
|
62
|
+
if (
|
|
63
|
+
typeof task.assigneePersonAvatarId === 'number' &&
|
|
64
|
+
task.assigneePersonAvatarId > 0
|
|
65
|
+
)
|
|
66
|
+
return `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${task.assigneePersonAvatarId}`;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getInitials(value?: string | null) {
|
|
71
|
+
const parts = String(value ?? '')
|
|
72
|
+
.trim()
|
|
73
|
+
.split(/\s+/)
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.slice(0, 2);
|
|
76
|
+
if (!parts.length) return '??';
|
|
77
|
+
return parts.map((p) => p[0]?.toUpperCase() ?? '').join('');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function TaskDetailSheet({
|
|
81
|
+
task,
|
|
82
|
+
open,
|
|
83
|
+
onOpenChange,
|
|
84
|
+
statusLabel,
|
|
85
|
+
footer,
|
|
86
|
+
}: Props) {
|
|
87
|
+
const commonT = useTranslations('operations.Common');
|
|
88
|
+
const detailT = useTranslations('operations.ProjectDetailsPage');
|
|
89
|
+
const { getSettingValue, currentLocaleCode } = useApp();
|
|
90
|
+
|
|
91
|
+
const avatarSrc = getAvatarSrc(task);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<Sheet open={open} onOpenChange={onOpenChange}>
|
|
95
|
+
<SheetContent className="flex w-full flex-col gap-0 overflow-y-auto sm:max-w-md">
|
|
96
|
+
{task ? (
|
|
97
|
+
<>
|
|
98
|
+
<SheetHeader className="pb-2">
|
|
99
|
+
<SheetTitle className="pr-6 text-base font-semibold leading-snug">
|
|
100
|
+
{task.name}
|
|
101
|
+
</SheetTitle>
|
|
102
|
+
<div className="flex flex-wrap gap-2 pt-1">
|
|
103
|
+
<StatusBadge
|
|
104
|
+
label={statusLabel?.(task.status) ?? task.status}
|
|
105
|
+
className={getStatusBadgeClass(task.status)}
|
|
106
|
+
/>
|
|
107
|
+
{task.priority ? (
|
|
108
|
+
<span
|
|
109
|
+
className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold ${getPriorityClassName(task.priority)}`}
|
|
110
|
+
>
|
|
111
|
+
{getPriorityLabel(task.priority)}
|
|
112
|
+
</span>
|
|
113
|
+
) : null}
|
|
114
|
+
</div>
|
|
115
|
+
</SheetHeader>
|
|
116
|
+
|
|
117
|
+
<div className="flex flex-col gap-5 p-4 pt-3">
|
|
118
|
+
{task.description ? (
|
|
119
|
+
<div>
|
|
120
|
+
<p className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
121
|
+
{detailT('taskForm.descriptionLabel')}
|
|
122
|
+
</p>
|
|
123
|
+
<p className="text-sm leading-relaxed">{task.description}</p>
|
|
124
|
+
</div>
|
|
125
|
+
) : null}
|
|
126
|
+
|
|
127
|
+
{task.dueDate || task.estimateHours != null ? (
|
|
128
|
+
<div className="grid grid-cols-2 gap-3">
|
|
129
|
+
{task.dueDate ? (
|
|
130
|
+
<div className="flex items-start gap-2 text-sm">
|
|
131
|
+
<Calendar className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
|
132
|
+
<div>
|
|
133
|
+
<p className="text-[11px] text-muted-foreground">
|
|
134
|
+
{detailT('taskForm.deadlineLabel')}
|
|
135
|
+
</p>
|
|
136
|
+
<p className="font-medium">
|
|
137
|
+
{formatDate(
|
|
138
|
+
task.dueDate,
|
|
139
|
+
getSettingValue,
|
|
140
|
+
currentLocaleCode
|
|
141
|
+
)}
|
|
142
|
+
</p>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
) : null}
|
|
146
|
+
{task.estimateHours != null ? (
|
|
147
|
+
<div className="flex items-start gap-2 text-sm">
|
|
148
|
+
<AlarmClock className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
|
149
|
+
<div>
|
|
150
|
+
<p className="text-[11px] text-muted-foreground">
|
|
151
|
+
{detailT('taskForm.estimateLabel')}
|
|
152
|
+
</p>
|
|
153
|
+
<p className="font-medium">{task.estimateHours}h</p>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
) : null}
|
|
157
|
+
</div>
|
|
158
|
+
) : null}
|
|
159
|
+
|
|
160
|
+
{task.tags ? (
|
|
161
|
+
<div>
|
|
162
|
+
<div className="mb-1.5 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
163
|
+
<Tag className="size-3" />
|
|
164
|
+
{detailT('taskForm.tagsLabel')}
|
|
165
|
+
</div>
|
|
166
|
+
<div className="flex flex-wrap gap-1.5">
|
|
167
|
+
{task.tags.split(',').map((tag) => (
|
|
168
|
+
<span
|
|
169
|
+
key={tag.trim()}
|
|
170
|
+
className="rounded-md bg-muted px-2 py-0.5 text-xs text-muted-foreground"
|
|
171
|
+
>
|
|
172
|
+
{tag.trim()}
|
|
173
|
+
</span>
|
|
174
|
+
))}
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
) : null}
|
|
178
|
+
|
|
179
|
+
{task.assigneeName ? (
|
|
180
|
+
<div>
|
|
181
|
+
<div className="mb-1.5 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
182
|
+
<User className="size-3" />
|
|
183
|
+
{commonT('labels.collaborator')}
|
|
184
|
+
</div>
|
|
185
|
+
<div className="flex items-center gap-2.5">
|
|
186
|
+
<div className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-xs font-semibold uppercase text-muted-foreground ring-1 ring-border">
|
|
187
|
+
{avatarSrc ? (
|
|
188
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
189
|
+
<img
|
|
190
|
+
src={avatarSrc}
|
|
191
|
+
alt={task.assigneeName}
|
|
192
|
+
className="size-full object-cover"
|
|
193
|
+
/>
|
|
194
|
+
) : (
|
|
195
|
+
getInitials(task.assigneeName)
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
<span className="text-sm">{task.assigneeName}</span>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
) : null}
|
|
202
|
+
|
|
203
|
+
{task.projectName || task.projectCode ? (
|
|
204
|
+
<div className="rounded-lg border bg-muted/20 px-3 py-2.5">
|
|
205
|
+
<p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
206
|
+
{commonT('labels.project')}
|
|
207
|
+
</p>
|
|
208
|
+
<p className="mt-0.5 text-sm font-medium">
|
|
209
|
+
{[task.projectName, task.projectCode]
|
|
210
|
+
.filter(Boolean)
|
|
211
|
+
.join(' • ')}
|
|
212
|
+
</p>
|
|
213
|
+
</div>
|
|
214
|
+
) : null}
|
|
215
|
+
|
|
216
|
+
{footer ? <div className="border-t pt-5">{footer}</div> : null}
|
|
217
|
+
</div>
|
|
218
|
+
</>
|
|
219
|
+
) : null}
|
|
220
|
+
</SheetContent>
|
|
221
|
+
</Sheet>
|
|
222
|
+
);
|
|
223
|
+
}
|
|
@@ -56,3 +56,165 @@ export async function mutateOperations<T>(
|
|
|
56
56
|
|
|
57
57
|
return response.data as T;
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
// ─── Cost Types ───────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
export type CostType = {
|
|
63
|
+
id: number;
|
|
64
|
+
slug: string;
|
|
65
|
+
name: string;
|
|
66
|
+
code?: string | null;
|
|
67
|
+
category?: string | null;
|
|
68
|
+
description?: string | null;
|
|
69
|
+
defaultRecurrence?: 'monthly' | 'one_time' | 'yearly' | null;
|
|
70
|
+
isAllocatable?: boolean | null;
|
|
71
|
+
isDepreciable?: boolean | null;
|
|
72
|
+
isActive: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export function fetchCostTypes(
|
|
76
|
+
request: RequestFn,
|
|
77
|
+
args: {
|
|
78
|
+
search?: string;
|
|
79
|
+
active?: boolean;
|
|
80
|
+
page?: number;
|
|
81
|
+
pageSize?: number;
|
|
82
|
+
} = {}
|
|
83
|
+
) {
|
|
84
|
+
const { search, active, page, pageSize } = args;
|
|
85
|
+
const params = new URLSearchParams();
|
|
86
|
+
if (search?.trim()) {
|
|
87
|
+
params.set('search', search.trim());
|
|
88
|
+
}
|
|
89
|
+
if (active !== undefined) {
|
|
90
|
+
params.set('active', String(active));
|
|
91
|
+
}
|
|
92
|
+
if (page !== undefined) {
|
|
93
|
+
params.set('page', String(page));
|
|
94
|
+
}
|
|
95
|
+
if (pageSize !== undefined) {
|
|
96
|
+
params.set('pageSize', String(pageSize));
|
|
97
|
+
}
|
|
98
|
+
const qs = params.toString();
|
|
99
|
+
return fetchOperations<CostType[]>(
|
|
100
|
+
request,
|
|
101
|
+
qs ? `/operations/cost-types?${qs}` : '/operations/cost-types'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function createCostType(
|
|
106
|
+
request: RequestFn,
|
|
107
|
+
data: {
|
|
108
|
+
name: string;
|
|
109
|
+
slug?: string | null;
|
|
110
|
+
code?: string | null;
|
|
111
|
+
category?: string | null;
|
|
112
|
+
description?: string | null;
|
|
113
|
+
defaultRecurrence?: string | null;
|
|
114
|
+
isAllocatable?: boolean | null;
|
|
115
|
+
isDepreciable?: boolean | null;
|
|
116
|
+
isActive?: boolean;
|
|
117
|
+
}
|
|
118
|
+
) {
|
|
119
|
+
return mutateOperations<CostType>(
|
|
120
|
+
request,
|
|
121
|
+
'/operations/cost-types',
|
|
122
|
+
'POST',
|
|
123
|
+
data
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── Collaborator Costs ───────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
export type CollaboratorCost = {
|
|
130
|
+
id: number;
|
|
131
|
+
collaboratorId: number;
|
|
132
|
+
costTypeId: number;
|
|
133
|
+
costTypeName: string;
|
|
134
|
+
costTypeSlug: string;
|
|
135
|
+
amount: string;
|
|
136
|
+
currency: string;
|
|
137
|
+
recurrence: 'one_time' | 'monthly' | 'yearly';
|
|
138
|
+
allocatable?: boolean;
|
|
139
|
+
/** ISO date string — mapped to startDate going forward */
|
|
140
|
+
referenceDate: string | null;
|
|
141
|
+
startDate?: string | null;
|
|
142
|
+
endDate?: string | null;
|
|
143
|
+
depreciationMonths?: number | null;
|
|
144
|
+
description: string | null;
|
|
145
|
+
notes?: string | null;
|
|
146
|
+
createdAt: string;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export function fetchCollaboratorCosts(
|
|
150
|
+
request: RequestFn,
|
|
151
|
+
collaboratorId: number
|
|
152
|
+
) {
|
|
153
|
+
return fetchOperations<CollaboratorCost[]>(
|
|
154
|
+
request,
|
|
155
|
+
`/operations/collaborators/${collaboratorId}/costs`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function createCollaboratorCost(
|
|
160
|
+
request: RequestFn,
|
|
161
|
+
collaboratorId: number,
|
|
162
|
+
data: {
|
|
163
|
+
costTypeId: number;
|
|
164
|
+
amount: number;
|
|
165
|
+
currency?: string;
|
|
166
|
+
recurrence?: string;
|
|
167
|
+
allocatable?: boolean;
|
|
168
|
+
referenceDate?: string | null;
|
|
169
|
+
startDate?: string | null;
|
|
170
|
+
endDate?: string | null;
|
|
171
|
+
depreciationMonths?: number | null;
|
|
172
|
+
description?: string | null;
|
|
173
|
+
notes?: string | null;
|
|
174
|
+
}
|
|
175
|
+
) {
|
|
176
|
+
return mutateOperations<CollaboratorCost>(
|
|
177
|
+
request,
|
|
178
|
+
`/operations/collaborators/${collaboratorId}/costs`,
|
|
179
|
+
'POST',
|
|
180
|
+
data
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function updateCollaboratorCost(
|
|
185
|
+
request: RequestFn,
|
|
186
|
+
collaboratorId: number,
|
|
187
|
+
costId: number,
|
|
188
|
+
data: Partial<{
|
|
189
|
+
costTypeId: number;
|
|
190
|
+
amount: number;
|
|
191
|
+
currency: string;
|
|
192
|
+
recurrence: string;
|
|
193
|
+
allocatable: boolean;
|
|
194
|
+
referenceDate: string | null;
|
|
195
|
+
startDate: string | null;
|
|
196
|
+
endDate: string | null;
|
|
197
|
+
depreciationMonths: number | null;
|
|
198
|
+
description: string | null;
|
|
199
|
+
notes: string | null;
|
|
200
|
+
}>
|
|
201
|
+
) {
|
|
202
|
+
return mutateOperations<CollaboratorCost>(
|
|
203
|
+
request,
|
|
204
|
+
`/operations/collaborators/${collaboratorId}/costs/${costId}`,
|
|
205
|
+
'PATCH',
|
|
206
|
+
data
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function deleteCollaboratorCost(
|
|
211
|
+
request: RequestFn,
|
|
212
|
+
collaboratorId: number,
|
|
213
|
+
costId: number
|
|
214
|
+
) {
|
|
215
|
+
return mutateOperations<{ success: boolean }>(
|
|
216
|
+
request,
|
|
217
|
+
`/operations/collaborators/${collaboratorId}/costs/${costId}`,
|
|
218
|
+
'DELETE'
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -34,6 +34,182 @@ export type OperationsDashboard = {
|
|
|
34
34
|
}>;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
export type OperationsReportScenario = 'base' | 'growth' | 'conservative';
|
|
38
|
+
|
|
39
|
+
export type OperationsReportPlanningCard = {
|
|
40
|
+
title: string;
|
|
41
|
+
value: string;
|
|
42
|
+
description: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type OperationsProjectsReport = {
|
|
46
|
+
filters: {
|
|
47
|
+
from: string;
|
|
48
|
+
to: string;
|
|
49
|
+
status: string;
|
|
50
|
+
client: string;
|
|
51
|
+
scenario: OperationsReportScenario;
|
|
52
|
+
clients: string[];
|
|
53
|
+
};
|
|
54
|
+
summary: {
|
|
55
|
+
contractedRevenue: number;
|
|
56
|
+
recognizedRevenue: number;
|
|
57
|
+
realizedCost: number;
|
|
58
|
+
forecastCost: number;
|
|
59
|
+
profit: number;
|
|
60
|
+
margin: number;
|
|
61
|
+
plannedHours: number;
|
|
62
|
+
actualHours: number;
|
|
63
|
+
billableHours: number;
|
|
64
|
+
reworkHours: number;
|
|
65
|
+
backlogValue: number;
|
|
66
|
+
avgDeadline: number;
|
|
67
|
+
avgAllocation: number;
|
|
68
|
+
atRisk: number;
|
|
69
|
+
burnRate: number;
|
|
70
|
+
};
|
|
71
|
+
forecast: Array<{
|
|
72
|
+
month: string;
|
|
73
|
+
revenue: number;
|
|
74
|
+
cost: number;
|
|
75
|
+
profit: number;
|
|
76
|
+
backlog: number;
|
|
77
|
+
planned: number;
|
|
78
|
+
actual: number;
|
|
79
|
+
}>;
|
|
80
|
+
costComposition: Array<{ name: string; value: number }>;
|
|
81
|
+
hoursByProject: Array<{
|
|
82
|
+
project: string;
|
|
83
|
+
Faturável: number;
|
|
84
|
+
Interno: number;
|
|
85
|
+
Retrabalho: number;
|
|
86
|
+
Livre: number;
|
|
87
|
+
}>;
|
|
88
|
+
health: Array<{
|
|
89
|
+
project: string;
|
|
90
|
+
margem: number;
|
|
91
|
+
prazo: number;
|
|
92
|
+
alocacao: number;
|
|
93
|
+
saude: number;
|
|
94
|
+
}>;
|
|
95
|
+
ranking: Array<{ name: string; Lucro: number; Custo: number }>;
|
|
96
|
+
progress: Array<{ month: string; Planejado: number; Realizado: number }>;
|
|
97
|
+
planningCards: OperationsReportPlanningCard[];
|
|
98
|
+
rows: Array<{
|
|
99
|
+
id: number;
|
|
100
|
+
name: string;
|
|
101
|
+
client: string;
|
|
102
|
+
manager: string;
|
|
103
|
+
squad: string;
|
|
104
|
+
status: 'on_track' | 'attention' | 'late' | 'paused';
|
|
105
|
+
contractType: 'fixed_price' | 'time_materials' | 'retainer';
|
|
106
|
+
priority: string;
|
|
107
|
+
startDate: string | null;
|
|
108
|
+
endDate: string | null;
|
|
109
|
+
contractedRevenue: number;
|
|
110
|
+
recognizedRevenue: number;
|
|
111
|
+
realizedCost: number;
|
|
112
|
+
forecastCost: number;
|
|
113
|
+
teamCost: number;
|
|
114
|
+
infraCost: number;
|
|
115
|
+
licenseCost: number;
|
|
116
|
+
thirdPartyCost: number;
|
|
117
|
+
reworkCost: number;
|
|
118
|
+
plannedHours: number;
|
|
119
|
+
actualHours: number;
|
|
120
|
+
billableHours: number;
|
|
121
|
+
reworkHours: number;
|
|
122
|
+
internalHours: number;
|
|
123
|
+
allocatedCapacity: number;
|
|
124
|
+
physicalProgress: number;
|
|
125
|
+
financialProgress: number;
|
|
126
|
+
backlogValue: number;
|
|
127
|
+
futureDeliveries: number;
|
|
128
|
+
risk: 'baixo' | 'médio' | 'alto';
|
|
129
|
+
recommendation: string;
|
|
130
|
+
}>;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export type OperationsCollaboratorsReport = {
|
|
134
|
+
filters: {
|
|
135
|
+
from: string;
|
|
136
|
+
to: string;
|
|
137
|
+
department: string;
|
|
138
|
+
contractType: string;
|
|
139
|
+
scenario: OperationsReportScenario;
|
|
140
|
+
departments: string[];
|
|
141
|
+
contractTypes: string[];
|
|
142
|
+
};
|
|
143
|
+
summary: {
|
|
144
|
+
cost: number;
|
|
145
|
+
salary: number;
|
|
146
|
+
benefits: number;
|
|
147
|
+
taxes: number;
|
|
148
|
+
tools: number;
|
|
149
|
+
billableValue: number;
|
|
150
|
+
profit: number;
|
|
151
|
+
margin: number;
|
|
152
|
+
availableHours: number;
|
|
153
|
+
allocatedHours: number;
|
|
154
|
+
billableHours: number;
|
|
155
|
+
internalHours: number;
|
|
156
|
+
overtimeHours: number;
|
|
157
|
+
freeHours: number;
|
|
158
|
+
allocation: number;
|
|
159
|
+
utilization: number;
|
|
160
|
+
overloadCount: number;
|
|
161
|
+
hourlyCost: number;
|
|
162
|
+
};
|
|
163
|
+
forecast: Array<{
|
|
164
|
+
month: string;
|
|
165
|
+
revenue: number;
|
|
166
|
+
cost: number;
|
|
167
|
+
profit: number;
|
|
168
|
+
margin: number;
|
|
169
|
+
capacity: number;
|
|
170
|
+
}>;
|
|
171
|
+
costComposition: Array<{ name: string; value: number }>;
|
|
172
|
+
capacityByDepartment: Array<{
|
|
173
|
+
department: string;
|
|
174
|
+
Faturável: number;
|
|
175
|
+
Interno: number;
|
|
176
|
+
Livre: number;
|
|
177
|
+
Sobrecarga: number;
|
|
178
|
+
}>;
|
|
179
|
+
health: Array<{
|
|
180
|
+
department: string;
|
|
181
|
+
margem: number;
|
|
182
|
+
alocacao: number;
|
|
183
|
+
utilizacao: number;
|
|
184
|
+
saude: number;
|
|
185
|
+
}>;
|
|
186
|
+
ranking: Array<{ name: string; Custo: number; Lucro: number }>;
|
|
187
|
+
planningCards: OperationsReportPlanningCard[];
|
|
188
|
+
rows: Array<{
|
|
189
|
+
id: number;
|
|
190
|
+
name: string;
|
|
191
|
+
role: string;
|
|
192
|
+
seniority: string;
|
|
193
|
+
department: string;
|
|
194
|
+
contractType: string;
|
|
195
|
+
startDate: string | null;
|
|
196
|
+
endDate: string | null;
|
|
197
|
+
salaryCost: number;
|
|
198
|
+
benefitsCost: number;
|
|
199
|
+
taxesCost: number;
|
|
200
|
+
toolsCost: number;
|
|
201
|
+
billableValue: number;
|
|
202
|
+
availableHours: number;
|
|
203
|
+
allocatedHours: number;
|
|
204
|
+
billableHours: number;
|
|
205
|
+
internalHours: number;
|
|
206
|
+
overtimeHours: number;
|
|
207
|
+
projects: number;
|
|
208
|
+
risk: 'baixo' | 'médio' | 'alto';
|
|
209
|
+
recommendation: string;
|
|
210
|
+
}>;
|
|
211
|
+
};
|
|
212
|
+
|
|
37
213
|
export type OperationsCollaborator = {
|
|
38
214
|
id: number;
|
|
39
215
|
userId?: number | null;
|
|
@@ -72,6 +248,10 @@ export type OperationsCollaboratorStats = {
|
|
|
72
248
|
active: number;
|
|
73
249
|
onLeave: number;
|
|
74
250
|
withContracts: number;
|
|
251
|
+
totalSalary: number;
|
|
252
|
+
totalCosts: number;
|
|
253
|
+
avgSalaryPlusCosts: number;
|
|
254
|
+
avgSalaryPlusCostsPerCollaborator: number;
|
|
75
255
|
};
|
|
76
256
|
|
|
77
257
|
export type OperationsContractStats = {
|
|
@@ -150,11 +330,19 @@ export type OperationsTaskOption = {
|
|
|
150
330
|
name: string;
|
|
151
331
|
description?: string | null;
|
|
152
332
|
status: string;
|
|
333
|
+
priority?: string | null;
|
|
153
334
|
projectId: number;
|
|
154
|
-
projectAssignmentId: number;
|
|
335
|
+
projectAssignmentId: number | null;
|
|
155
336
|
projectName: string;
|
|
156
337
|
projectCode?: string | null;
|
|
338
|
+
dueDate?: string | null;
|
|
339
|
+
estimateHours?: number | null;
|
|
340
|
+
tags?: string | null;
|
|
341
|
+
assigneeName?: string | null;
|
|
342
|
+
assigneeUserPhotoId?: number | null;
|
|
343
|
+
assigneePersonAvatarId?: number | null;
|
|
157
344
|
createdAt?: string | null;
|
|
345
|
+
deletedAt?: string | null;
|
|
158
346
|
};
|
|
159
347
|
|
|
160
348
|
export type OperationsProject = {
|
|
@@ -410,6 +598,44 @@ export type OperationsCollaboratorDetails = OperationsCollaborator & {
|
|
|
410
598
|
}>;
|
|
411
599
|
};
|
|
412
600
|
|
|
601
|
+
export type OperationsMyProjectSummary = {
|
|
602
|
+
id: number;
|
|
603
|
+
code: string;
|
|
604
|
+
name: string;
|
|
605
|
+
status: string;
|
|
606
|
+
summary: string | null;
|
|
607
|
+
startDate: string | null;
|
|
608
|
+
endDate: string | null;
|
|
609
|
+
myAssignmentId: number | null;
|
|
610
|
+
myRoleLabel: string | null;
|
|
611
|
+
assignments: Array<{
|
|
612
|
+
id: number;
|
|
613
|
+
collaboratorId: number;
|
|
614
|
+
collaboratorName: string;
|
|
615
|
+
roleLabel: string | null;
|
|
616
|
+
status: string;
|
|
617
|
+
avatarId: number | null;
|
|
618
|
+
userPhotoId: number | null;
|
|
619
|
+
}>;
|
|
620
|
+
tasks: Array<{
|
|
621
|
+
id: number;
|
|
622
|
+
name: string;
|
|
623
|
+
description: string | null;
|
|
624
|
+
priority: string;
|
|
625
|
+
status: string;
|
|
626
|
+
dueDate: string | null;
|
|
627
|
+
estimateHours: number | null;
|
|
628
|
+
position: number;
|
|
629
|
+
tags: string | null;
|
|
630
|
+
assigneeCollaboratorId: number | null;
|
|
631
|
+
assigneeName: string | null;
|
|
632
|
+
assigneeUserPhotoId: number | null;
|
|
633
|
+
assigneePersonAvatarId: number | null;
|
|
634
|
+
projectAssignmentId: number | null;
|
|
635
|
+
createdAt: string;
|
|
636
|
+
}>;
|
|
637
|
+
};
|
|
638
|
+
|
|
413
639
|
export type OperationsProjectDetails = OperationsProject & {
|
|
414
640
|
assignments: Array<{
|
|
415
641
|
id: number;
|
|
@@ -22,6 +22,16 @@ function resolveLocale(currentLocaleCode: string) {
|
|
|
22
22
|
return currentLocaleCode.startsWith('pt') ? 'pt-BR' : 'en-US';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
function parseDateValue(value: string) {
|
|
26
|
+
const dateMatch = String(value).match(/(\d{4}-\d{2}-\d{2})/);
|
|
27
|
+
|
|
28
|
+
if (dateMatch?.[1]) {
|
|
29
|
+
return new Date(`${dateMatch[1]}T12:00:00`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return new Date(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
export function formatDate(
|
|
26
36
|
value?: string | null,
|
|
27
37
|
getSettingValue?: ((key: string) => unknown) | null,
|
|
@@ -31,9 +41,7 @@ export function formatDate(
|
|
|
31
41
|
return '-';
|
|
32
42
|
}
|
|
33
43
|
|
|
34
|
-
const parsedDate =
|
|
35
|
-
value.includes('T') ? value : `${value}T00:00:00`
|
|
36
|
-
);
|
|
44
|
+
const parsedDate = parseDateValue(value);
|
|
37
45
|
|
|
38
46
|
if (Number.isNaN(parsedDate.getTime())) {
|
|
39
47
|
return '-';
|