@hed-hog/operations 0.0.304 → 0.0.306
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-projects.controller.d.ts +15 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.d.ts +41 -10
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +11 -0
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +21 -0
- package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
- package/dist/controllers/operations-timesheets.controller.js +12 -0
- package/dist/controllers/operations-timesheets.controller.js.map +1 -1
- package/dist/dto/create-task.dto.d.ts +7 -1
- package/dist/dto/create-task.dto.d.ts.map +1 -1
- package/dist/dto/create-task.dto.js +38 -5
- package/dist/dto/create-task.dto.js.map +1 -1
- package/dist/dto/list-tasks.dto.d.ts +1 -1
- package/dist/dto/list-tasks.dto.d.ts.map +1 -1
- package/dist/dto/list-tasks.dto.js +2 -2
- package/dist/dto/list-tasks.dto.js.map +1 -1
- package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
- package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
- package/dist/dto/update-collaborator-type.dto.js +2 -1
- package/dist/dto/update-collaborator-type.dto.js.map +1 -1
- package/dist/dto/update-task.dto.d.ts +7 -1
- package/dist/dto/update-task.dto.d.ts.map +1 -1
- package/dist/dto/update-task.dto.js +38 -5
- package/dist/dto/update-task.dto.js.map +1 -1
- package/dist/operations.service.d.ts +90 -12
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +560 -148
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +73 -0
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +26 -26
- package/hedhog/data/operations_collaborator_type.yaml +76 -76
- package/hedhog/data/route.yaml +26 -0
- package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
- package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
- package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
- package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
- package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
- package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
- package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
- package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
- package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
- package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
- package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
- package/hedhog/frontend/app/approvals/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +513 -502
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
- package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
- package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +235 -72
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +344 -134
- package/hedhog/frontend/messages/en.json +32 -4
- package/hedhog/frontend/messages/pt.json +34 -6
- package/hedhog/table/operations_collaborator.yaml +18 -18
- package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
- package/hedhog/table/operations_collaborator_type.yaml +33 -33
- package/hedhog/table/operations_contract_document.yaml +33 -33
- package/hedhog/table/operations_project.yaml +9 -0
- package/hedhog/table/operations_task.yaml +43 -4
- package/package.json +6 -6
- package/src/controllers/operations-tasks.controller.ts +11 -0
- package/src/controllers/operations-timesheets.controller.ts +13 -0
- package/src/dto/create-collaborator-type.dto.ts +43 -43
- package/src/dto/create-collaborator.dto.ts +223 -223
- package/src/dto/create-task.dto.ts +47 -7
- package/src/dto/list-collaborator-types.dto.ts +15 -15
- package/src/dto/list-collaborators.dto.ts +30 -30
- package/src/dto/list-tasks.dto.ts +3 -3
- package/src/dto/update-collaborator-type.dto.ts +4 -3
- package/src/dto/update-collaborator.dto.ts +3 -3
- package/src/dto/update-task.dto.ts +47 -7
- package/src/operations.service.spec.ts +96 -0
- package/src/operations.service.ts +813 -135
|
@@ -69,11 +69,19 @@ import {
|
|
|
69
69
|
formatWeekdayLabel,
|
|
70
70
|
getStatusBadgeClass,
|
|
71
71
|
} from '../_lib/utils/format';
|
|
72
|
-
import {
|
|
72
|
+
import {
|
|
73
|
+
normalizePercentInput,
|
|
74
|
+
parseNumberInput,
|
|
75
|
+
trimToNull,
|
|
76
|
+
} from '../_lib/utils/forms';
|
|
73
77
|
import { DepartmentSelectWithCreate } from './department-select-with-create';
|
|
74
78
|
import { OperationsHeader } from './operations-header';
|
|
75
79
|
import { PersonSelectWithCreate } from './person-select-with-create';
|
|
76
80
|
import { StatusBadge } from './status-badge';
|
|
81
|
+
import {
|
|
82
|
+
SystemUserSelectWithCreate,
|
|
83
|
+
type SystemUserOption,
|
|
84
|
+
} from './system-user-select-with-create';
|
|
77
85
|
|
|
78
86
|
const weekdays = [
|
|
79
87
|
'monday',
|
|
@@ -96,11 +104,6 @@ const COLLABORATOR_LEVEL_OPTIONS = [
|
|
|
96
104
|
|
|
97
105
|
type CollaboratorLevel = (typeof COLLABORATOR_LEVEL_OPTIONS)[number];
|
|
98
106
|
|
|
99
|
-
type SystemUserOption = {
|
|
100
|
-
id: number;
|
|
101
|
-
name: string;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
107
|
function normalizeCollaboratorLevel(
|
|
105
108
|
value?: string | null
|
|
106
109
|
): CollaboratorLevel | '' {
|
|
@@ -171,6 +174,32 @@ function normalizeTimeValue(value?: string | null, fallback = '') {
|
|
|
171
174
|
return fallback || String(value);
|
|
172
175
|
}
|
|
173
176
|
|
|
177
|
+
function normalizeDateInputValue(value?: string | null) {
|
|
178
|
+
if (!value) {
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const normalizedValue = String(value).trim();
|
|
183
|
+
|
|
184
|
+
if (!normalizedValue) {
|
|
185
|
+
return '';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const directMatch = normalizedValue.match(/^\d{4}-\d{2}-\d{2}/);
|
|
189
|
+
|
|
190
|
+
if (directMatch?.[0]) {
|
|
191
|
+
return directMatch[0];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const parsedDate = new Date(normalizedValue);
|
|
195
|
+
|
|
196
|
+
if (Number.isNaN(parsedDate.getTime())) {
|
|
197
|
+
return normalizedValue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return parsedDate.toISOString().slice(0, 10);
|
|
201
|
+
}
|
|
202
|
+
|
|
174
203
|
const EQUITY_ENABLED_TYPE_SLUGS = new Set([
|
|
175
204
|
'socio',
|
|
176
205
|
'acionista',
|
|
@@ -308,16 +337,19 @@ function toFormState(
|
|
|
308
337
|
? String(collaborator.weeklyCapacityHours)
|
|
309
338
|
: '',
|
|
310
339
|
status: collaborator.status ?? 'active',
|
|
311
|
-
joinedAt: collaborator.joinedAt
|
|
312
|
-
leftAt: collaborator.leftAt
|
|
340
|
+
joinedAt: normalizeDateInputValue(collaborator.joinedAt),
|
|
341
|
+
leftAt: normalizeDateInputValue(collaborator.leftAt),
|
|
313
342
|
supervisorCollaboratorId: collaborator.supervisorId
|
|
314
343
|
? String(collaborator.supervisorId)
|
|
315
344
|
: 'none',
|
|
316
345
|
compensationAmount:
|
|
317
|
-
collaborator.
|
|
318
|
-
collaborator.
|
|
319
|
-
? String(collaborator.
|
|
320
|
-
:
|
|
346
|
+
collaborator.compensationAmount !== null &&
|
|
347
|
+
collaborator.compensationAmount !== undefined
|
|
348
|
+
? String(collaborator.compensationAmount)
|
|
349
|
+
: collaborator.relatedContracts?.[0]?.budgetAmount !== null &&
|
|
350
|
+
collaborator.relatedContracts?.[0]?.budgetAmount !== undefined
|
|
351
|
+
? String(collaborator.relatedContracts[0].budgetAmount)
|
|
352
|
+
: '',
|
|
321
353
|
contractDescription: collaborator.relatedContracts?.[0]?.description ?? '',
|
|
322
354
|
autoGenerateContractDraft: true,
|
|
323
355
|
notes: collaborator.notes ?? '',
|
|
@@ -335,8 +367,12 @@ function toFormState(
|
|
|
335
367
|
collaborator.equityParticipation?.votingPower !== undefined
|
|
336
368
|
? String(collaborator.equityParticipation.votingPower)
|
|
337
369
|
: '',
|
|
338
|
-
startDate:
|
|
339
|
-
|
|
370
|
+
startDate: normalizeDateInputValue(
|
|
371
|
+
collaborator.equityParticipation?.startDate
|
|
372
|
+
),
|
|
373
|
+
endDate: normalizeDateInputValue(
|
|
374
|
+
collaborator.equityParticipation?.endDate
|
|
375
|
+
),
|
|
340
376
|
notes: collaborator.equityParticipation?.notes ?? '',
|
|
341
377
|
},
|
|
342
378
|
weeklySchedule: normalizeSchedule(collaborator.weeklySchedule),
|
|
@@ -496,166 +532,6 @@ function SupervisorAutocomplete({
|
|
|
496
532
|
);
|
|
497
533
|
}
|
|
498
534
|
|
|
499
|
-
type SystemUserAutocompleteProps = {
|
|
500
|
-
label: string;
|
|
501
|
-
value: string;
|
|
502
|
-
options: SystemUserOption[];
|
|
503
|
-
placeholder: string;
|
|
504
|
-
emptyLabel: string;
|
|
505
|
-
onChange: (value: string) => void;
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
function SystemUserAutocomplete({
|
|
509
|
-
label,
|
|
510
|
-
value,
|
|
511
|
-
options,
|
|
512
|
-
placeholder,
|
|
513
|
-
emptyLabel,
|
|
514
|
-
onChange,
|
|
515
|
-
}: SystemUserAutocompleteProps) {
|
|
516
|
-
const commonT = useTranslations('operations.Common');
|
|
517
|
-
const [open, setOpen] = useState(false);
|
|
518
|
-
const [search, setSearch] = useState('');
|
|
519
|
-
const [visibleCount, setVisibleCount] = useState(SUPERVISOR_PAGE_SIZE);
|
|
520
|
-
|
|
521
|
-
const filteredOptions = useMemo(() => {
|
|
522
|
-
const normalizedSearch = search.trim().toLowerCase();
|
|
523
|
-
|
|
524
|
-
if (!normalizedSearch) {
|
|
525
|
-
return options;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
return options.filter((option) =>
|
|
529
|
-
[option.name, `#${option.id}`]
|
|
530
|
-
.filter(Boolean)
|
|
531
|
-
.some((field) => String(field).toLowerCase().includes(normalizedSearch))
|
|
532
|
-
);
|
|
533
|
-
}, [options, search]);
|
|
534
|
-
|
|
535
|
-
const selectedOption =
|
|
536
|
-
options.find((option) => String(option.id) === value) ??
|
|
537
|
-
(value
|
|
538
|
-
? {
|
|
539
|
-
id: Number(value),
|
|
540
|
-
name: `#${value}`,
|
|
541
|
-
}
|
|
542
|
-
: null);
|
|
543
|
-
const visibleOptions = filteredOptions.slice(0, visibleCount);
|
|
544
|
-
|
|
545
|
-
return (
|
|
546
|
-
<div className="grid gap-2">
|
|
547
|
-
<Label>{label}</Label>
|
|
548
|
-
|
|
549
|
-
<Popover
|
|
550
|
-
open={open}
|
|
551
|
-
onOpenChange={(nextOpen) => {
|
|
552
|
-
setOpen(nextOpen);
|
|
553
|
-
setVisibleCount(SUPERVISOR_PAGE_SIZE);
|
|
554
|
-
|
|
555
|
-
if (!nextOpen) {
|
|
556
|
-
setSearch('');
|
|
557
|
-
}
|
|
558
|
-
}}
|
|
559
|
-
>
|
|
560
|
-
<PopoverTrigger asChild>
|
|
561
|
-
<Button
|
|
562
|
-
type="button"
|
|
563
|
-
variant="outline"
|
|
564
|
-
role="combobox"
|
|
565
|
-
className="w-full justify-between overflow-hidden"
|
|
566
|
-
>
|
|
567
|
-
<span className="truncate text-left">
|
|
568
|
-
{selectedOption
|
|
569
|
-
? `${selectedOption.name} (#${selectedOption.id})`
|
|
570
|
-
: emptyLabel}
|
|
571
|
-
</span>
|
|
572
|
-
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
573
|
-
</Button>
|
|
574
|
-
</PopoverTrigger>
|
|
575
|
-
<PopoverContent
|
|
576
|
-
className="p-0"
|
|
577
|
-
style={{ width: 'var(--radix-popover-trigger-width)' }}
|
|
578
|
-
>
|
|
579
|
-
<Command shouldFilter={false}>
|
|
580
|
-
<CommandInput
|
|
581
|
-
placeholder={placeholder}
|
|
582
|
-
value={search}
|
|
583
|
-
onValueChange={(nextValue) => {
|
|
584
|
-
setSearch(nextValue);
|
|
585
|
-
setVisibleCount(SUPERVISOR_PAGE_SIZE);
|
|
586
|
-
}}
|
|
587
|
-
/>
|
|
588
|
-
<CommandList>
|
|
589
|
-
<CommandEmpty>
|
|
590
|
-
<div className="p-2 text-sm text-muted-foreground">
|
|
591
|
-
{commonT('states.emptyDescription')}
|
|
592
|
-
</div>
|
|
593
|
-
</CommandEmpty>
|
|
594
|
-
|
|
595
|
-
<CommandGroup>
|
|
596
|
-
<CommandItem
|
|
597
|
-
value="none"
|
|
598
|
-
onSelect={() => {
|
|
599
|
-
onChange('');
|
|
600
|
-
setOpen(false);
|
|
601
|
-
}}
|
|
602
|
-
>
|
|
603
|
-
{!value ? (
|
|
604
|
-
<Check className="mr-2 h-4 w-4" />
|
|
605
|
-
) : (
|
|
606
|
-
<span className="mr-2 h-4 w-4" />
|
|
607
|
-
)}
|
|
608
|
-
{emptyLabel}
|
|
609
|
-
</CommandItem>
|
|
610
|
-
|
|
611
|
-
{visibleOptions.map((option) => (
|
|
612
|
-
<CommandItem
|
|
613
|
-
key={option.id}
|
|
614
|
-
value={`${option.name} #${option.id}`}
|
|
615
|
-
onSelect={() => {
|
|
616
|
-
onChange(String(option.id));
|
|
617
|
-
setOpen(false);
|
|
618
|
-
}}
|
|
619
|
-
>
|
|
620
|
-
{String(option.id) === value ? (
|
|
621
|
-
<Check className="mr-2 h-4 w-4" />
|
|
622
|
-
) : (
|
|
623
|
-
<span className="mr-2 h-4 w-4" />
|
|
624
|
-
)}
|
|
625
|
-
<div className="min-w-0">
|
|
626
|
-
<div className="truncate">{option.name}</div>
|
|
627
|
-
<div className="truncate text-xs text-muted-foreground">
|
|
628
|
-
#{option.id}
|
|
629
|
-
</div>
|
|
630
|
-
</div>
|
|
631
|
-
</CommandItem>
|
|
632
|
-
))}
|
|
633
|
-
</CommandGroup>
|
|
634
|
-
|
|
635
|
-
{filteredOptions.length > visibleCount ? (
|
|
636
|
-
<div className="border-t p-2">
|
|
637
|
-
<Button
|
|
638
|
-
type="button"
|
|
639
|
-
variant="ghost"
|
|
640
|
-
className="w-full"
|
|
641
|
-
onClick={() =>
|
|
642
|
-
setVisibleCount(
|
|
643
|
-
(current) => current + SUPERVISOR_PAGE_SIZE
|
|
644
|
-
)
|
|
645
|
-
}
|
|
646
|
-
>
|
|
647
|
-
{commonT('actions.loadMore')}
|
|
648
|
-
</Button>
|
|
649
|
-
</div>
|
|
650
|
-
) : null}
|
|
651
|
-
</CommandList>
|
|
652
|
-
</Command>
|
|
653
|
-
</PopoverContent>
|
|
654
|
-
</Popover>
|
|
655
|
-
</div>
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
535
|
type CollaboratorFormScreenProps = {
|
|
660
536
|
collaboratorId?: number;
|
|
661
537
|
onSaved?: (
|
|
@@ -714,26 +590,28 @@ export function CollaboratorFormScreen({
|
|
|
714
590
|
),
|
|
715
591
|
});
|
|
716
592
|
|
|
717
|
-
const { data: systemUsers = [] } = useQuery<
|
|
593
|
+
const { data: systemUsers = [], refetch: refetchSystemUsers } = useQuery<
|
|
594
|
+
SystemUserOption[]
|
|
595
|
+
>({
|
|
718
596
|
queryKey: ['operations-collaborator-form-system-users', currentLocaleCode],
|
|
719
597
|
enabled: access.isDirector,
|
|
720
598
|
staleTime: 0,
|
|
721
599
|
refetchOnMount: 'always',
|
|
722
600
|
queryFn: async () => {
|
|
723
|
-
const response = await request<
|
|
724
|
-
|
|
725
|
-
>({
|
|
726
|
-
url: '/
|
|
601
|
+
const response = await request<{
|
|
602
|
+
paginate: { data: Array<{ id: number; name: string | null }> };
|
|
603
|
+
}>({
|
|
604
|
+
url: '/user?pageSize=100',
|
|
727
605
|
method: 'GET',
|
|
728
606
|
});
|
|
729
607
|
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
:
|
|
608
|
+
const raw = response.data?.paginate?.data ?? [];
|
|
609
|
+
const options: SystemUserOption[] = raw.map((u) => ({
|
|
610
|
+
id: u.id,
|
|
611
|
+
name: u.name || `#${u.id}`,
|
|
612
|
+
}));
|
|
733
613
|
|
|
734
|
-
return
|
|
735
|
-
left.name.localeCompare(right.name)
|
|
736
|
-
);
|
|
614
|
+
return options.sort((left, right) => left.name.localeCompare(right.name));
|
|
737
615
|
},
|
|
738
616
|
});
|
|
739
617
|
|
|
@@ -823,19 +701,66 @@ export function CollaboratorFormScreen({
|
|
|
823
701
|
}));
|
|
824
702
|
}, [collaboratorId, collaboratorTypes, form.collaboratorTypeId]);
|
|
825
703
|
|
|
826
|
-
const selectedCollaboratorType = useMemo(
|
|
827
|
-
|
|
828
|
-
|
|
704
|
+
const selectedCollaboratorType = useMemo(() => {
|
|
705
|
+
const availableTypes = [...collaboratorTypes];
|
|
706
|
+
|
|
707
|
+
if (
|
|
708
|
+
collaborator?.collaboratorTypeId &&
|
|
709
|
+
collaborator.collaboratorType &&
|
|
710
|
+
!availableTypes.some(
|
|
711
|
+
(item) => item.id === collaborator.collaboratorTypeId
|
|
712
|
+
)
|
|
713
|
+
) {
|
|
714
|
+
availableTypes.push({
|
|
715
|
+
id: collaborator.collaboratorTypeId,
|
|
716
|
+
slug: collaborator.collaboratorTypeSlug ?? '',
|
|
717
|
+
name: collaborator.collaboratorType,
|
|
718
|
+
status: 'active',
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
return (
|
|
723
|
+
availableTypes.find(
|
|
829
724
|
(item) => String(item.id) === String(form.collaboratorTypeId)
|
|
830
|
-
) ?? null
|
|
831
|
-
|
|
832
|
-
);
|
|
725
|
+
) ?? null
|
|
726
|
+
);
|
|
727
|
+
}, [collaborator, collaboratorTypes, form.collaboratorTypeId]);
|
|
728
|
+
|
|
729
|
+
const supervisorOptions = useMemo(() => {
|
|
730
|
+
const options = new Map<number, OperationsCollaborator>();
|
|
731
|
+
|
|
732
|
+
for (const item of collaborators) {
|
|
733
|
+
if (item.id !== collaboratorId) {
|
|
734
|
+
options.set(item.id, item);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (
|
|
739
|
+
collaborator?.supervisorId &&
|
|
740
|
+
collaborator.supervisorName &&
|
|
741
|
+
collaborator.supervisorId !== collaboratorId &&
|
|
742
|
+
!options.has(collaborator.supervisorId)
|
|
743
|
+
) {
|
|
744
|
+
options.set(collaborator.supervisorId, {
|
|
745
|
+
id: collaborator.supervisorId,
|
|
746
|
+
code: '',
|
|
747
|
+
displayName: collaborator.supervisorName,
|
|
748
|
+
department: null,
|
|
749
|
+
title: null,
|
|
750
|
+
status: 'active',
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
return Array.from(options.values()).sort((left, right) =>
|
|
755
|
+
left.displayName.localeCompare(right.displayName)
|
|
756
|
+
);
|
|
757
|
+
}, [collaborator, collaboratorId, collaborators]);
|
|
833
758
|
|
|
834
759
|
const shouldShowEquitySection =
|
|
835
760
|
!isCreateMode &&
|
|
836
761
|
Boolean(
|
|
837
762
|
selectedCollaboratorType?.slug &&
|
|
838
|
-
|
|
763
|
+
EQUITY_ENABLED_TYPE_SLUGS.has(selectedCollaboratorType.slug)
|
|
839
764
|
);
|
|
840
765
|
|
|
841
766
|
const getCollaboratorTypeLabel = (
|
|
@@ -958,6 +883,29 @@ export function CollaboratorFormScreen({
|
|
|
958
883
|
}
|
|
959
884
|
};
|
|
960
885
|
|
|
886
|
+
const createSystemUser = async (
|
|
887
|
+
name: string,
|
|
888
|
+
email: string,
|
|
889
|
+
password: string
|
|
890
|
+
): Promise<SystemUserOption | null> => {
|
|
891
|
+
try {
|
|
892
|
+
const response = await request<{ id: number; name: string }>({
|
|
893
|
+
url: '/user',
|
|
894
|
+
method: 'POST',
|
|
895
|
+
data: { name, email, password },
|
|
896
|
+
});
|
|
897
|
+
await refetchSystemUsers();
|
|
898
|
+
showToastHandler?.('success', t('messages.createUserSuccess'));
|
|
899
|
+
return { id: response.data.id, name: response.data.name };
|
|
900
|
+
} catch (error) {
|
|
901
|
+
showToastHandler?.(
|
|
902
|
+
'error',
|
|
903
|
+
getOperationsErrorMessage(error, t('messages.createUserError'))
|
|
904
|
+
);
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
|
|
961
909
|
const updateScheduleDay = (
|
|
962
910
|
weekday: string,
|
|
963
911
|
patch: Partial<CollaboratorFormState['weeklySchedule'][number]>
|
|
@@ -994,6 +942,7 @@ export function CollaboratorFormScreen({
|
|
|
994
942
|
const onSubmit = async () => {
|
|
995
943
|
const userId = parseNumberInput(form.userId);
|
|
996
944
|
const personId = parseNumberInput(form.personId);
|
|
945
|
+
const compensationAmount = parseNumberInput(form.compensationAmount);
|
|
997
946
|
|
|
998
947
|
if (!personId) {
|
|
999
948
|
showToastHandler?.('error', t('messages.personRequired'));
|
|
@@ -1028,7 +977,8 @@ export function CollaboratorFormScreen({
|
|
|
1028
977
|
form.supervisorCollaboratorId === 'none'
|
|
1029
978
|
? null
|
|
1030
979
|
: parseNumberInput(form.supervisorCollaboratorId),
|
|
1031
|
-
compensationAmount:
|
|
980
|
+
compensationAmount:
|
|
981
|
+
compensationAmount !== null ? compensationAmount : null,
|
|
1032
982
|
contractDescription: trimToNull(form.contractDescription),
|
|
1033
983
|
autoGenerateContractDraft: form.autoGenerateContractDraft,
|
|
1034
984
|
notes: trimToNull(form.notes),
|
|
@@ -1073,7 +1023,7 @@ export function CollaboratorFormScreen({
|
|
|
1073
1023
|
setForm(toFormState(response));
|
|
1074
1024
|
|
|
1075
1025
|
await Promise.all([
|
|
1076
|
-
refetchCollaborator(),
|
|
1026
|
+
collaboratorId ? refetchCollaborator() : Promise.resolve(),
|
|
1077
1027
|
refetchCollaboratorTypes(),
|
|
1078
1028
|
refetchDepartments(),
|
|
1079
1029
|
refetchCollaborators(),
|
|
@@ -1229,7 +1179,7 @@ export function CollaboratorFormScreen({
|
|
|
1229
1179
|
label={t('fields.person')}
|
|
1230
1180
|
entityLabel={t('fields.personEntityLabel')}
|
|
1231
1181
|
value={form.personId ? Number(form.personId) : null}
|
|
1232
|
-
initialSelectedLabel={form.displayName}
|
|
1182
|
+
initialSelectedLabel={collaborator?.personName ?? form.displayName}
|
|
1233
1183
|
selectPlaceholder={t('placeholders.person')}
|
|
1234
1184
|
onChange={(personId, personName) =>
|
|
1235
1185
|
setForm((current) => ({
|
|
@@ -1241,7 +1191,7 @@ export function CollaboratorFormScreen({
|
|
|
1241
1191
|
/>
|
|
1242
1192
|
</div>
|
|
1243
1193
|
<div className="space-y-2 md:col-span-2 xl:col-span-2">
|
|
1244
|
-
<
|
|
1194
|
+
<SystemUserSelectWithCreate
|
|
1245
1195
|
label={t('fields.userIdOptional')}
|
|
1246
1196
|
value={form.userId}
|
|
1247
1197
|
options={systemUsers}
|
|
@@ -1253,6 +1203,7 @@ export function CollaboratorFormScreen({
|
|
|
1253
1203
|
userId: value,
|
|
1254
1204
|
}))
|
|
1255
1205
|
}
|
|
1206
|
+
onCreate={createSystemUser}
|
|
1256
1207
|
/>
|
|
1257
1208
|
<p className="text-xs text-muted-foreground">
|
|
1258
1209
|
{t('fields.userIdDescription')}
|
|
@@ -1467,11 +1418,15 @@ export function CollaboratorFormScreen({
|
|
|
1467
1418
|
</label>
|
|
1468
1419
|
<InputMoney
|
|
1469
1420
|
step="0.01"
|
|
1470
|
-
value={
|
|
1471
|
-
|
|
1421
|
+
value={
|
|
1422
|
+
form.compensationAmount === ''
|
|
1423
|
+
? ''
|
|
1424
|
+
: Number(form.compensationAmount)
|
|
1425
|
+
}
|
|
1426
|
+
onValueChange={(value) =>
|
|
1472
1427
|
setForm((current) => ({
|
|
1473
1428
|
...current,
|
|
1474
|
-
compensationAmount:
|
|
1429
|
+
compensationAmount: value !== null ? String(value) : '',
|
|
1475
1430
|
}))
|
|
1476
1431
|
}
|
|
1477
1432
|
/>
|
|
@@ -1534,10 +1489,8 @@ export function CollaboratorFormScreen({
|
|
|
1534
1489
|
<div className="space-y-2">
|
|
1535
1490
|
<Label>{t('fields.equityPercentage')}</Label>
|
|
1536
1491
|
<Input
|
|
1537
|
-
type="
|
|
1538
|
-
|
|
1539
|
-
max="100"
|
|
1540
|
-
step="0.0001"
|
|
1492
|
+
type="text"
|
|
1493
|
+
inputMode="decimal"
|
|
1541
1494
|
placeholder={t('placeholders.equityPercentage')}
|
|
1542
1495
|
value={form.equityParticipation.percentage}
|
|
1543
1496
|
onChange={(event) =>
|
|
@@ -1545,7 +1498,7 @@ export function CollaboratorFormScreen({
|
|
|
1545
1498
|
...current,
|
|
1546
1499
|
equityParticipation: {
|
|
1547
1500
|
...current.equityParticipation,
|
|
1548
|
-
percentage: event.target.value,
|
|
1501
|
+
percentage: normalizePercentInput(event.target.value),
|
|
1549
1502
|
},
|
|
1550
1503
|
}))
|
|
1551
1504
|
}
|
|
@@ -1554,10 +1507,8 @@ export function CollaboratorFormScreen({
|
|
|
1554
1507
|
<div className="space-y-2">
|
|
1555
1508
|
<Label>{t('fields.votingPower')}</Label>
|
|
1556
1509
|
<Input
|
|
1557
|
-
type="
|
|
1558
|
-
|
|
1559
|
-
max="100"
|
|
1560
|
-
step="0.0001"
|
|
1510
|
+
type="text"
|
|
1511
|
+
inputMode="decimal"
|
|
1561
1512
|
placeholder={t('placeholders.votingPower')}
|
|
1562
1513
|
value={form.equityParticipation.votingPower}
|
|
1563
1514
|
onChange={(event) =>
|
|
@@ -1565,7 +1516,7 @@ export function CollaboratorFormScreen({
|
|
|
1565
1516
|
...current,
|
|
1566
1517
|
equityParticipation: {
|
|
1567
1518
|
...current.equityParticipation,
|
|
1568
|
-
votingPower: event.target.value,
|
|
1519
|
+
votingPower: normalizePercentInput(event.target.value),
|
|
1569
1520
|
},
|
|
1570
1521
|
}))
|
|
1571
1522
|
}
|
|
@@ -1637,7 +1588,7 @@ export function CollaboratorFormScreen({
|
|
|
1637
1588
|
<SupervisorAutocomplete
|
|
1638
1589
|
label={commonT('labels.supervisor')}
|
|
1639
1590
|
value={form.supervisorCollaboratorId}
|
|
1640
|
-
options={
|
|
1591
|
+
options={supervisorOptions}
|
|
1641
1592
|
placeholder={t('placeholders.supervisor')}
|
|
1642
1593
|
emptyLabel={commonT('labels.notAssigned')}
|
|
1643
1594
|
onChange={(value) =>
|
|
@@ -1683,11 +1634,15 @@ export function CollaboratorFormScreen({
|
|
|
1683
1634
|
</label>
|
|
1684
1635
|
<InputMoney
|
|
1685
1636
|
step="0.01"
|
|
1686
|
-
value={
|
|
1687
|
-
|
|
1637
|
+
value={
|
|
1638
|
+
form.compensationAmount === ''
|
|
1639
|
+
? ''
|
|
1640
|
+
: Number(form.compensationAmount)
|
|
1641
|
+
}
|
|
1642
|
+
onValueChange={(value) =>
|
|
1688
1643
|
setForm((current) => ({
|
|
1689
1644
|
...current,
|
|
1690
|
-
compensationAmount:
|
|
1645
|
+
compensationAmount: value !== null ? String(value) : '',
|
|
1691
1646
|
}))
|
|
1692
1647
|
}
|
|
1693
1648
|
/>
|