@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.
- 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-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 +94 -15
- 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 +229 -3
- 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 +6 -6
- 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 +7 -3
- package/src/operations.service.spec.ts +45 -7
- package/src/operations.service.ts +1992 -225
|
@@ -21,6 +21,7 @@ import { StatusBadge } from './status-badge';
|
|
|
21
21
|
import { fetchOperations } from '../_lib/api';
|
|
22
22
|
import type { OperationsCollaboratorDetails } from '../_lib/types';
|
|
23
23
|
import {
|
|
24
|
+
formatCurrency,
|
|
24
25
|
formatDate,
|
|
25
26
|
formatDateRange,
|
|
26
27
|
formatEnumLabel,
|
|
@@ -37,7 +38,7 @@ export function CollaboratorDetailsScreen({
|
|
|
37
38
|
}) {
|
|
38
39
|
const t = useTranslations('operations.CollaboratorDetailsPage');
|
|
39
40
|
const commonT = useTranslations('operations.Common');
|
|
40
|
-
const { request, currentLocaleCode } = useApp();
|
|
41
|
+
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
41
42
|
const access = useOperationsAccess();
|
|
42
43
|
|
|
43
44
|
const { data: collaborator, refetch } =
|
|
@@ -54,24 +55,50 @@ export function CollaboratorDetailsScreen({
|
|
|
54
55
|
),
|
|
55
56
|
});
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
type CompensationHistoryEntry = {
|
|
59
|
+
id: number;
|
|
60
|
+
collaboratorId: number;
|
|
61
|
+
amount: string;
|
|
62
|
+
effectiveDate: string | null;
|
|
63
|
+
actorUserId: number | null;
|
|
64
|
+
actorName: string | null;
|
|
65
|
+
notes: string | null;
|
|
66
|
+
createdAt: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const { data: compensationHistory = [] } =
|
|
70
|
+
useQuery<CompensationHistoryEntry[]>({
|
|
71
|
+
queryKey: [
|
|
72
|
+
'operations-collaborator-compensation-history',
|
|
73
|
+
currentLocaleCode,
|
|
74
|
+
collaboratorId,
|
|
75
|
+
],
|
|
76
|
+
enabled: access.isDirector,
|
|
77
|
+
queryFn: () =>
|
|
78
|
+
fetchOperations<CompensationHistoryEntry[]>(
|
|
79
|
+
request,
|
|
80
|
+
`/operations/collaborators/${collaboratorId}/compensation-history`
|
|
81
|
+
),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!collaborator) {
|
|
85
|
+
return (
|
|
86
|
+
<Page>
|
|
87
|
+
<OperationsHeader
|
|
88
|
+
title={t('title')}
|
|
89
|
+
description={t('description')}
|
|
90
|
+
current={t('breadcrumb')}
|
|
91
|
+
/>
|
|
92
|
+
<EmptyState
|
|
93
|
+
icon={<UserRound className="size-12" />}
|
|
67
94
|
title={commonT('states.emptyTitle')}
|
|
68
95
|
description={t('notFound')}
|
|
69
96
|
actionLabel={commonT('actions.refresh')}
|
|
70
97
|
onAction={() => void refetch()}
|
|
71
|
-
/>
|
|
72
|
-
</Page>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
98
|
+
/>
|
|
99
|
+
</Page>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
75
102
|
|
|
76
103
|
const summaryCards = [
|
|
77
104
|
{
|
|
@@ -392,6 +419,58 @@ export function CollaboratorDetailsScreen({
|
|
|
392
419
|
)}
|
|
393
420
|
</SectionCard>
|
|
394
421
|
</div>
|
|
422
|
+
|
|
423
|
+
{access.isDirector && (
|
|
424
|
+
<SectionCard
|
|
425
|
+
title={t('sections.compensationHistory')}
|
|
426
|
+
description={t('sections.compensationHistoryDescription')}
|
|
427
|
+
>
|
|
428
|
+
{compensationHistory.length > 0 ? (
|
|
429
|
+
<div className="overflow-x-auto rounded-md border">
|
|
430
|
+
<Table>
|
|
431
|
+
<TableHeader>
|
|
432
|
+
<TableRow>
|
|
433
|
+
<TableHead>{t('compensationHistory.amount')}</TableHead>
|
|
434
|
+
<TableHead>{t('compensationHistory.effectiveDate')}</TableHead>
|
|
435
|
+
<TableHead>{t('compensationHistory.recordedAt')}</TableHead>
|
|
436
|
+
<TableHead>{t('compensationHistory.changedBy')}</TableHead>
|
|
437
|
+
<TableHead>{t('compensationHistory.notes')}</TableHead>
|
|
438
|
+
</TableRow>
|
|
439
|
+
</TableHeader>
|
|
440
|
+
<TableBody>
|
|
441
|
+
{compensationHistory.map((entry) => (
|
|
442
|
+
<TableRow key={entry.id}>
|
|
443
|
+
<TableCell className="font-medium">
|
|
444
|
+
{formatCurrency(
|
|
445
|
+
Number(entry.amount),
|
|
446
|
+
getSettingValue,
|
|
447
|
+
currentLocaleCode
|
|
448
|
+
)}
|
|
449
|
+
</TableCell>
|
|
450
|
+
<TableCell>
|
|
451
|
+
{entry.effectiveDate
|
|
452
|
+
? formatDate(entry.effectiveDate)
|
|
453
|
+
: '—'}
|
|
454
|
+
</TableCell>
|
|
455
|
+
<TableCell>{formatDate(entry.createdAt)}</TableCell>
|
|
456
|
+
<TableCell>
|
|
457
|
+
{entry.actorName ?? '—'}
|
|
458
|
+
</TableCell>
|
|
459
|
+
<TableCell className="text-muted-foreground">
|
|
460
|
+
{entry.notes ?? '—'}
|
|
461
|
+
</TableCell>
|
|
462
|
+
</TableRow>
|
|
463
|
+
))}
|
|
464
|
+
</TableBody>
|
|
465
|
+
</Table>
|
|
466
|
+
</div>
|
|
467
|
+
) : (
|
|
468
|
+
<p className="text-sm text-muted-foreground">
|
|
469
|
+
{t('noCompensationHistory')}
|
|
470
|
+
</p>
|
|
471
|
+
)}
|
|
472
|
+
</SectionCard>
|
|
473
|
+
)}
|
|
395
474
|
</Page>
|
|
396
475
|
);
|
|
397
476
|
}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { PersonFormSheet } from '@/app/(app)/(libraries)/contact/person/_components/person-form-sheet';
|
|
4
|
+
import type {
|
|
5
|
+
ContactTypeOption,
|
|
6
|
+
DocumentTypeOption,
|
|
7
|
+
Person,
|
|
8
|
+
} from '@/app/(app)/(libraries)/contact/person/_components/person-types';
|
|
3
9
|
import { EmptyState, Page } from '@/components/entity-list';
|
|
10
|
+
import {
|
|
11
|
+
Accordion,
|
|
12
|
+
AccordionContent,
|
|
13
|
+
AccordionItem,
|
|
14
|
+
AccordionTrigger,
|
|
15
|
+
} from '@/components/ui/accordion';
|
|
4
16
|
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
|
5
17
|
import { Button } from '@/components/ui/button';
|
|
6
18
|
import {
|
|
@@ -11,6 +23,7 @@ import {
|
|
|
11
23
|
CommandItem,
|
|
12
24
|
CommandList,
|
|
13
25
|
} from '@/components/ui/command';
|
|
26
|
+
import { EntityPicker } from '@/components/ui/entity-picker';
|
|
14
27
|
import { FormActions } from '@/components/ui/form-actions';
|
|
15
28
|
import { Input } from '@/components/ui/input';
|
|
16
29
|
import { InputMoney } from '@/components/ui/input-money';
|
|
@@ -40,6 +53,7 @@ import {
|
|
|
40
53
|
Check,
|
|
41
54
|
ChevronsUpDown,
|
|
42
55
|
Info,
|
|
56
|
+
Pencil,
|
|
43
57
|
Save,
|
|
44
58
|
UserRound,
|
|
45
59
|
} from 'lucide-react';
|
|
@@ -57,9 +71,11 @@ import type {
|
|
|
57
71
|
OperationsCollaborator,
|
|
58
72
|
OperationsCollaboratorDetails,
|
|
59
73
|
OperationsCollaboratorType,
|
|
74
|
+
OperationsContract,
|
|
60
75
|
OperationsDepartment,
|
|
61
76
|
OperationsJobTitle,
|
|
62
77
|
OperationsWeeklyScheduleDay,
|
|
78
|
+
PaginatedResponse,
|
|
63
79
|
} from '../_lib/types';
|
|
64
80
|
import {
|
|
65
81
|
formatDate,
|
|
@@ -74,6 +90,7 @@ import {
|
|
|
74
90
|
parseNumberInput,
|
|
75
91
|
trimToNull,
|
|
76
92
|
} from '../_lib/utils/forms';
|
|
93
|
+
import { CollaboratorCostsSection } from './collaborator-costs-section';
|
|
77
94
|
import { DepartmentSelectWithCreate } from './department-select-with-create';
|
|
78
95
|
import { OperationsHeader } from './operations-header';
|
|
79
96
|
import { PersonSelectWithCreate } from './person-select-with-create';
|
|
@@ -212,11 +229,9 @@ const EQUITY_ENABLED_TYPE_SLUGS = new Set([
|
|
|
212
229
|
type CollaboratorFormState = {
|
|
213
230
|
userId: string;
|
|
214
231
|
personId: string;
|
|
215
|
-
code: string;
|
|
216
232
|
displayName: string;
|
|
217
233
|
collaboratorTypeId: string;
|
|
218
234
|
departmentId: string;
|
|
219
|
-
department: string;
|
|
220
235
|
jobTitleId: string;
|
|
221
236
|
title: string;
|
|
222
237
|
levelLabel: string;
|
|
@@ -226,8 +241,7 @@ type CollaboratorFormState = {
|
|
|
226
241
|
leftAt: string;
|
|
227
242
|
supervisorCollaboratorId: string;
|
|
228
243
|
compensationAmount: string;
|
|
229
|
-
|
|
230
|
-
autoGenerateContractDraft: boolean;
|
|
244
|
+
contractId: string;
|
|
231
245
|
notes: string;
|
|
232
246
|
equityParticipation: {
|
|
233
247
|
participationType: string;
|
|
@@ -260,11 +274,9 @@ function buildEmptyForm(): CollaboratorFormState {
|
|
|
260
274
|
return {
|
|
261
275
|
userId: '',
|
|
262
276
|
personId: '',
|
|
263
|
-
code: '',
|
|
264
277
|
displayName: '',
|
|
265
278
|
collaboratorTypeId: '',
|
|
266
279
|
departmentId: '',
|
|
267
|
-
department: '',
|
|
268
280
|
jobTitleId: '',
|
|
269
281
|
title: '',
|
|
270
282
|
levelLabel: '',
|
|
@@ -274,8 +286,7 @@ function buildEmptyForm(): CollaboratorFormState {
|
|
|
274
286
|
leftAt: '',
|
|
275
287
|
supervisorCollaboratorId: 'none',
|
|
276
288
|
compensationAmount: '',
|
|
277
|
-
|
|
278
|
-
autoGenerateContractDraft: true,
|
|
289
|
+
contractId: '',
|
|
279
290
|
notes: '',
|
|
280
291
|
equityParticipation: {
|
|
281
292
|
participationType: 'partner_quota_holder',
|
|
@@ -319,7 +330,6 @@ function toFormState(
|
|
|
319
330
|
return {
|
|
320
331
|
userId: collaborator.userId ? String(collaborator.userId) : '',
|
|
321
332
|
personId: collaborator.personId ? String(collaborator.personId) : '',
|
|
322
|
-
code: collaborator.code ?? '',
|
|
323
333
|
displayName: collaborator.displayName ?? '',
|
|
324
334
|
collaboratorTypeId: collaborator.collaboratorTypeId
|
|
325
335
|
? String(collaborator.collaboratorTypeId)
|
|
@@ -327,7 +337,6 @@ function toFormState(
|
|
|
327
337
|
departmentId: collaborator.departmentId
|
|
328
338
|
? String(collaborator.departmentId)
|
|
329
339
|
: '',
|
|
330
|
-
department: collaborator.department ?? '',
|
|
331
340
|
jobTitleId: collaborator.jobTitleId ? String(collaborator.jobTitleId) : '',
|
|
332
341
|
title: collaborator.title ?? '',
|
|
333
342
|
levelLabel: normalizeCollaboratorLevel(collaborator.levelLabel),
|
|
@@ -350,8 +359,9 @@ function toFormState(
|
|
|
350
359
|
collaborator.relatedContracts?.[0]?.budgetAmount !== undefined
|
|
351
360
|
? String(collaborator.relatedContracts[0].budgetAmount)
|
|
352
361
|
: '',
|
|
353
|
-
|
|
354
|
-
|
|
362
|
+
contractId: collaborator.relatedContracts?.[0]?.id
|
|
363
|
+
? String(collaborator.relatedContracts[0].id)
|
|
364
|
+
: '',
|
|
355
365
|
notes: collaborator.notes ?? '',
|
|
356
366
|
equityParticipation: {
|
|
357
367
|
participationType:
|
|
@@ -677,6 +687,49 @@ export function CollaboratorFormScreen({
|
|
|
677
687
|
fetchOperations<OperationsJobTitle[]>(request, '/operations/job-titles'),
|
|
678
688
|
});
|
|
679
689
|
|
|
690
|
+
const [personSheetOpen, setPersonSheetOpen] = useState(false);
|
|
691
|
+
const [personToEdit, setPersonToEdit] = useState<Person | null>(null);
|
|
692
|
+
|
|
693
|
+
const { data: contactTypes = [] } = useQuery<ContactTypeOption[]>({
|
|
694
|
+
queryKey: ['contact-person-contact-types', currentLocaleCode],
|
|
695
|
+
enabled: personSheetOpen,
|
|
696
|
+
queryFn: async () => {
|
|
697
|
+
const response = await request<{ data: ContactTypeOption[] }>({
|
|
698
|
+
url: '/person-contact-type?pageSize=100',
|
|
699
|
+
method: 'GET',
|
|
700
|
+
});
|
|
701
|
+
return response.data.data || [];
|
|
702
|
+
},
|
|
703
|
+
placeholderData: (previous) => previous ?? [],
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
const { data: documentTypes = [] } = useQuery<DocumentTypeOption[]>({
|
|
707
|
+
queryKey: ['contact-person-document-types', currentLocaleCode],
|
|
708
|
+
enabled: personSheetOpen,
|
|
709
|
+
queryFn: async () => {
|
|
710
|
+
const response = await request<{ data: DocumentTypeOption[] }>({
|
|
711
|
+
url: '/person-document-type?pageSize=100',
|
|
712
|
+
method: 'GET',
|
|
713
|
+
});
|
|
714
|
+
return response.data.data || [];
|
|
715
|
+
},
|
|
716
|
+
placeholderData: (previous) => previous ?? [],
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
const handleOpenPersonSheet = async () => {
|
|
720
|
+
if (!collaborator?.personId) return;
|
|
721
|
+
try {
|
|
722
|
+
const response = await request<Person>({
|
|
723
|
+
url: `/person/${collaborator.personId}`,
|
|
724
|
+
method: 'GET',
|
|
725
|
+
});
|
|
726
|
+
setPersonToEdit(response.data);
|
|
727
|
+
setPersonSheetOpen(true);
|
|
728
|
+
} catch {
|
|
729
|
+
showToastHandler?.('error', t('messages.updateError'));
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
680
733
|
useEffect(() => {
|
|
681
734
|
if (collaborator) {
|
|
682
735
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
@@ -793,40 +846,16 @@ export function CollaboratorFormScreen({
|
|
|
793
846
|
};
|
|
794
847
|
|
|
795
848
|
const departmentOptions = useMemo(() => {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
id?: number | null;
|
|
799
|
-
name: string;
|
|
800
|
-
code?: string | null;
|
|
801
|
-
description?: string | null;
|
|
802
|
-
}> = departments
|
|
803
|
-
.filter(
|
|
804
|
-
(item) => item.status === 'active' || item.name === selectedDepartment
|
|
805
|
-
)
|
|
849
|
+
return departments
|
|
850
|
+
.filter((item) => item.status === 'active')
|
|
806
851
|
.map((item) => ({
|
|
807
852
|
id: item.id,
|
|
808
853
|
name: item.name,
|
|
809
854
|
code: item.code ?? null,
|
|
810
855
|
description: item.description ?? null,
|
|
811
|
-
}))
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const alreadyIncluded = options.some(
|
|
815
|
-
(item) => item.name === selectedDepartment
|
|
816
|
-
);
|
|
817
|
-
|
|
818
|
-
if (!alreadyIncluded) {
|
|
819
|
-
options.push({
|
|
820
|
-
id: form.departmentId ? Number(form.departmentId) : undefined,
|
|
821
|
-
name: selectedDepartment,
|
|
822
|
-
code: null,
|
|
823
|
-
description: null,
|
|
824
|
-
});
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
return options.sort((left, right) => left.name.localeCompare(right.name));
|
|
829
|
-
}, [departments, form.department, form.departmentId]);
|
|
856
|
+
}))
|
|
857
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
858
|
+
}, [departments]);
|
|
830
859
|
|
|
831
860
|
const titleOptions = useMemo(() => {
|
|
832
861
|
const selectedTitle = trimToNull(form.title);
|
|
@@ -968,11 +997,9 @@ export function CollaboratorFormScreen({
|
|
|
968
997
|
const payload = {
|
|
969
998
|
userId: userId ?? undefined,
|
|
970
999
|
personId: personId ?? undefined,
|
|
971
|
-
code: trimToNull(form.code) ?? undefined,
|
|
972
1000
|
displayName: trimToNull(form.displayName),
|
|
973
1001
|
collaboratorTypeId,
|
|
974
1002
|
departmentId: departmentId ?? undefined,
|
|
975
|
-
department: departmentId ? undefined : trimToNull(form.department),
|
|
976
1003
|
jobTitleId: jobTitleId ?? undefined,
|
|
977
1004
|
title: trimToNull(form.title),
|
|
978
1005
|
levelLabel: trimToNull(form.levelLabel),
|
|
@@ -986,8 +1013,7 @@ export function CollaboratorFormScreen({
|
|
|
986
1013
|
: parseNumberInput(form.supervisorCollaboratorId),
|
|
987
1014
|
compensationAmount:
|
|
988
1015
|
compensationAmount !== null ? compensationAmount : null,
|
|
989
|
-
|
|
990
|
-
autoGenerateContractDraft: form.autoGenerateContractDraft,
|
|
1016
|
+
contractId: parseNumberInput(form.contractId),
|
|
991
1017
|
notes: trimToNull(form.notes),
|
|
992
1018
|
equityParticipation: shouldShowEquitySection
|
|
993
1019
|
? {
|
|
@@ -1101,7 +1127,7 @@ export function CollaboratorFormScreen({
|
|
|
1101
1127
|
</div>
|
|
1102
1128
|
</div>
|
|
1103
1129
|
|
|
1104
|
-
<div className="flex flex-wrap gap-2">
|
|
1130
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
1105
1131
|
<StatusBadge
|
|
1106
1132
|
label={getStatusLabel(collaborator.status)}
|
|
1107
1133
|
className={getStatusBadgeClass(collaborator.status)}
|
|
@@ -1113,6 +1139,23 @@ export function CollaboratorFormScreen({
|
|
|
1113
1139
|
collaborator.collaboratorTypeName
|
|
1114
1140
|
)}
|
|
1115
1141
|
</span>
|
|
1142
|
+
{collaborator.personId ? (
|
|
1143
|
+
<Tooltip>
|
|
1144
|
+
<TooltipTrigger asChild>
|
|
1145
|
+
<Button
|
|
1146
|
+
variant="outline"
|
|
1147
|
+
size="icon"
|
|
1148
|
+
className="h-7 w-7"
|
|
1149
|
+
onClick={() => void handleOpenPersonSheet()}
|
|
1150
|
+
>
|
|
1151
|
+
<Pencil className="size-3.5" />
|
|
1152
|
+
</Button>
|
|
1153
|
+
</TooltipTrigger>
|
|
1154
|
+
<TooltipContent side="bottom">
|
|
1155
|
+
{t('actions.editPersonCrm')}
|
|
1156
|
+
</TooltipContent>
|
|
1157
|
+
</Tooltip>
|
|
1158
|
+
) : null}
|
|
1116
1159
|
</div>
|
|
1117
1160
|
</div>
|
|
1118
1161
|
|
|
@@ -1216,20 +1259,7 @@ export function CollaboratorFormScreen({
|
|
|
1216
1259
|
{t('fields.userIdDescription')}
|
|
1217
1260
|
</p>
|
|
1218
1261
|
</div>
|
|
1219
|
-
|
|
1220
|
-
<Label>{t('fields.code')}</Label>
|
|
1221
|
-
<Input
|
|
1222
|
-
className="h-10"
|
|
1223
|
-
value={form.code}
|
|
1224
|
-
placeholder="COL-001"
|
|
1225
|
-
onChange={(event) =>
|
|
1226
|
-
setForm((current) => ({
|
|
1227
|
-
...current,
|
|
1228
|
-
code: event.target.value,
|
|
1229
|
-
}))
|
|
1230
|
-
}
|
|
1231
|
-
/>
|
|
1232
|
-
</div>
|
|
1262
|
+
|
|
1233
1263
|
{!isCreateMode ? (
|
|
1234
1264
|
<div className="space-y-2">
|
|
1235
1265
|
<Label>{t('fields.levelLabel')}</Label>
|
|
@@ -1258,7 +1288,10 @@ export function CollaboratorFormScreen({
|
|
|
1258
1288
|
<div className="space-y-2 xl:col-span-1">
|
|
1259
1289
|
<DepartmentSelectWithCreate
|
|
1260
1290
|
label={t('fields.department')}
|
|
1261
|
-
value={
|
|
1291
|
+
value={
|
|
1292
|
+
departmentOptions.find((d) => String(d.id) === form.departmentId)
|
|
1293
|
+
?.name ?? ''
|
|
1294
|
+
}
|
|
1262
1295
|
options={departmentOptions}
|
|
1263
1296
|
selectPlaceholder={t('placeholders.department')}
|
|
1264
1297
|
createDescription={t('fields.departmentDescription')}
|
|
@@ -1267,7 +1300,6 @@ export function CollaboratorFormScreen({
|
|
|
1267
1300
|
setForm((current) => ({
|
|
1268
1301
|
...current,
|
|
1269
1302
|
departmentId: department.id ? String(department.id) : '',
|
|
1270
|
-
department: department.name,
|
|
1271
1303
|
}))
|
|
1272
1304
|
}
|
|
1273
1305
|
/>
|
|
@@ -1290,6 +1322,73 @@ export function CollaboratorFormScreen({
|
|
|
1290
1322
|
}
|
|
1291
1323
|
/>
|
|
1292
1324
|
</div>
|
|
1325
|
+
<div className="space-y-2 xl:col-span-1">
|
|
1326
|
+
<label className="text-sm font-medium">{t('fields.contract')}</label>
|
|
1327
|
+
<EntityPicker<OperationsContract>
|
|
1328
|
+
value={form.contractId ? Number(form.contractId) : null}
|
|
1329
|
+
onChange={(v) =>
|
|
1330
|
+
setForm((current) => ({
|
|
1331
|
+
...current,
|
|
1332
|
+
contractId: v ? String(v) : '',
|
|
1333
|
+
}))
|
|
1334
|
+
}
|
|
1335
|
+
placeholder={t('fields.contractPlaceholder')}
|
|
1336
|
+
emptyLabel={t('fields.contractEmpty')}
|
|
1337
|
+
initialSelectedLabel={
|
|
1338
|
+
form.contractId
|
|
1339
|
+
? (collaborator?.relatedContracts.find(
|
|
1340
|
+
(c) => String(c.id) === form.contractId
|
|
1341
|
+
)?.name ??
|
|
1342
|
+
collaborator?.relatedContracts.find(
|
|
1343
|
+
(c) => String(c.id) === form.contractId
|
|
1344
|
+
)?.code ??
|
|
1345
|
+
undefined)
|
|
1346
|
+
: undefined
|
|
1347
|
+
}
|
|
1348
|
+
clearable
|
|
1349
|
+
showCreateButton
|
|
1350
|
+
createTitle={t('fields.contractCreateTitle')}
|
|
1351
|
+
createDescription={t('fields.contractCreateDescription')}
|
|
1352
|
+
createFields={[
|
|
1353
|
+
{
|
|
1354
|
+
name: 'name',
|
|
1355
|
+
label: t('fields.contractNameField'),
|
|
1356
|
+
placeholder: t('fields.contractNameField'),
|
|
1357
|
+
required: true,
|
|
1358
|
+
},
|
|
1359
|
+
]}
|
|
1360
|
+
loadOptions={async ({ page, pageSize, search }) => {
|
|
1361
|
+
const params = new URLSearchParams({
|
|
1362
|
+
page: String(page),
|
|
1363
|
+
pageSize: String(pageSize),
|
|
1364
|
+
});
|
|
1365
|
+
if (search.trim()) params.set('search', search.trim());
|
|
1366
|
+
const res = await fetchOperations<
|
|
1367
|
+
PaginatedResponse<OperationsContract>
|
|
1368
|
+
>(request, `/operations/contracts?${params.toString()}`);
|
|
1369
|
+
return {
|
|
1370
|
+
items: res.data ?? [],
|
|
1371
|
+
hasMore: res.page < res.lastPage,
|
|
1372
|
+
};
|
|
1373
|
+
}}
|
|
1374
|
+
getOptionValue={(c) => c.id}
|
|
1375
|
+
getOptionLabel={(c) => (c.name || c.code) as string}
|
|
1376
|
+
getOptionDescription={(c) =>
|
|
1377
|
+
[c.code, c.status].filter(Boolean).join(' • ') || undefined
|
|
1378
|
+
}
|
|
1379
|
+
onCreate={async (values) => {
|
|
1380
|
+
return mutateOperations<OperationsContract>(
|
|
1381
|
+
request,
|
|
1382
|
+
'/operations/contracts',
|
|
1383
|
+
'POST',
|
|
1384
|
+
{
|
|
1385
|
+
name: values.name,
|
|
1386
|
+
billingModel: 'time_and_material',
|
|
1387
|
+
}
|
|
1388
|
+
);
|
|
1389
|
+
}}
|
|
1390
|
+
/>
|
|
1391
|
+
</div>
|
|
1293
1392
|
</div>
|
|
1294
1393
|
</div>
|
|
1295
1394
|
);
|
|
@@ -1618,7 +1717,7 @@ export function CollaboratorFormScreen({
|
|
|
1618
1717
|
{t('sections.contractDescription')}
|
|
1619
1718
|
</p>
|
|
1620
1719
|
</div>
|
|
1621
|
-
<div className="grid gap-3 md:grid-cols-
|
|
1720
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
1622
1721
|
<div className="space-y-2">
|
|
1623
1722
|
<label className="text-sm font-medium">
|
|
1624
1723
|
{t('fields.weeklyCapacityHours')}
|
|
@@ -1654,36 +1753,17 @@ export function CollaboratorFormScreen({
|
|
|
1654
1753
|
}
|
|
1655
1754
|
/>
|
|
1656
1755
|
</div>
|
|
1657
|
-
<div className="space-y-2
|
|
1658
|
-
<
|
|
1659
|
-
{
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
onChange={(
|
|
1665
|
-
setForm((current) => ({
|
|
1666
|
-
...current,
|
|
1667
|
-
contractDescription: event.target.value,
|
|
1668
|
-
}))
|
|
1669
|
-
}
|
|
1670
|
-
/>
|
|
1671
|
-
</div>
|
|
1672
|
-
<div className="flex items-center justify-between rounded-lg border px-4 py-3 md:col-span-2">
|
|
1673
|
-
<div>
|
|
1674
|
-
<div className="font-medium">
|
|
1675
|
-
{t('fields.autoGenerateContractDraft')}
|
|
1676
|
-
</div>
|
|
1677
|
-
<div className="text-sm text-muted-foreground">
|
|
1678
|
-
{t('fields.autoGenerateContractDraftDescription')}
|
|
1679
|
-
</div>
|
|
1680
|
-
</div>
|
|
1681
|
-
<Switch
|
|
1682
|
-
checked={form.autoGenerateContractDraft}
|
|
1683
|
-
onCheckedChange={(checked) =>
|
|
1756
|
+
<div className="space-y-2">
|
|
1757
|
+
<SupervisorAutocomplete
|
|
1758
|
+
label={commonT('labels.supervisor')}
|
|
1759
|
+
value={form.supervisorCollaboratorId}
|
|
1760
|
+
options={supervisorOptions}
|
|
1761
|
+
placeholder={t('placeholders.supervisor')}
|
|
1762
|
+
emptyLabel={commonT('labels.notAssigned')}
|
|
1763
|
+
onChange={(value) =>
|
|
1684
1764
|
setForm((current) => ({
|
|
1685
1765
|
...current,
|
|
1686
|
-
|
|
1766
|
+
supervisorCollaboratorId: value,
|
|
1687
1767
|
}))
|
|
1688
1768
|
}
|
|
1689
1769
|
/>
|
|
@@ -2029,21 +2109,54 @@ export function CollaboratorFormScreen({
|
|
|
2029
2109
|
{basicInfoSection}
|
|
2030
2110
|
{employmentInfoSection}
|
|
2031
2111
|
{!isCreateMode ? equitySection : null}
|
|
2032
|
-
{supervisorSection}
|
|
2033
2112
|
</div>
|
|
2034
2113
|
);
|
|
2035
2114
|
|
|
2115
|
+
const costsSection = (
|
|
2116
|
+
<Accordion
|
|
2117
|
+
type="single"
|
|
2118
|
+
collapsible
|
|
2119
|
+
defaultValue={isCreateMode ? undefined : 'costs'}
|
|
2120
|
+
>
|
|
2121
|
+
<AccordionItem value="costs" className="rounded-xl border px-4">
|
|
2122
|
+
<AccordionTrigger className="py-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground hover:no-underline">
|
|
2123
|
+
{t('sections.costs')}
|
|
2124
|
+
</AccordionTrigger>
|
|
2125
|
+
<AccordionContent className="pb-4">
|
|
2126
|
+
{isCreateMode || !collaborator ? (
|
|
2127
|
+
<p className="text-sm text-muted-foreground">
|
|
2128
|
+
{t('sections.costsSaveBefore')}
|
|
2129
|
+
</p>
|
|
2130
|
+
) : (
|
|
2131
|
+
<CollaboratorCostsSection
|
|
2132
|
+
collaboratorId={collaborator.id}
|
|
2133
|
+
disabled={!access.isDirector}
|
|
2134
|
+
weeklyCapacity={collaborator.weeklyCapacityHours ?? undefined}
|
|
2135
|
+
remunerationValue={
|
|
2136
|
+
collaborator.compensationAmount != null
|
|
2137
|
+
? Number(collaborator.compensationAmount)
|
|
2138
|
+
: undefined
|
|
2139
|
+
}
|
|
2140
|
+
/>
|
|
2141
|
+
)}
|
|
2142
|
+
</AccordionContent>
|
|
2143
|
+
</AccordionItem>
|
|
2144
|
+
</Accordion>
|
|
2145
|
+
);
|
|
2146
|
+
|
|
2036
2147
|
const formContent = isSheetMode ? (
|
|
2037
2148
|
<div className="space-y-4 px-4">
|
|
2038
2149
|
{profileContent}
|
|
2039
|
-
{
|
|
2150
|
+
{contractSection}
|
|
2151
|
+
{!isCreateMode ? costsSection : null}
|
|
2040
2152
|
{!isCreateMode ? scheduleSection : null}
|
|
2041
2153
|
{activitySection}
|
|
2042
2154
|
</div>
|
|
2043
2155
|
) : (
|
|
2044
2156
|
<div className="space-y-4 px-4">
|
|
2045
2157
|
{profileContent}
|
|
2046
|
-
{
|
|
2158
|
+
{contractSection}
|
|
2159
|
+
{!isCreateMode ? costsSection : null}
|
|
2047
2160
|
{!isCreateMode ? scheduleSection : null}
|
|
2048
2161
|
</div>
|
|
2049
2162
|
);
|
|
@@ -2069,6 +2182,18 @@ export function CollaboratorFormScreen({
|
|
|
2069
2182
|
submitLabel={commonT('actions.save')}
|
|
2070
2183
|
submitSize="lg"
|
|
2071
2184
|
/>
|
|
2185
|
+
|
|
2186
|
+
<PersonFormSheet
|
|
2187
|
+
open={personSheetOpen}
|
|
2188
|
+
person={personToEdit}
|
|
2189
|
+
contactTypes={contactTypes}
|
|
2190
|
+
documentTypes={documentTypes}
|
|
2191
|
+
onOpenChange={(nextOpen) => {
|
|
2192
|
+
setPersonSheetOpen(nextOpen);
|
|
2193
|
+
if (!nextOpen) setPersonToEdit(null);
|
|
2194
|
+
}}
|
|
2195
|
+
onSuccess={() => void refetchCollaborator()}
|
|
2196
|
+
/>
|
|
2072
2197
|
</div>
|
|
2073
2198
|
);
|
|
2074
2199
|
}
|