@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.
Files changed (81) hide show
  1. package/dist/controllers/operations-projects.controller.d.ts +15 -0
  2. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-tasks.controller.d.ts +41 -10
  4. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  5. package/dist/controllers/operations-tasks.controller.js +11 -0
  6. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  7. package/dist/controllers/operations-timesheets.controller.d.ts +21 -0
  8. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  9. package/dist/controllers/operations-timesheets.controller.js +12 -0
  10. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  11. package/dist/dto/create-task.dto.d.ts +7 -1
  12. package/dist/dto/create-task.dto.d.ts.map +1 -1
  13. package/dist/dto/create-task.dto.js +38 -5
  14. package/dist/dto/create-task.dto.js.map +1 -1
  15. package/dist/dto/list-tasks.dto.d.ts +1 -1
  16. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  17. package/dist/dto/list-tasks.dto.js +2 -2
  18. package/dist/dto/list-tasks.dto.js.map +1 -1
  19. package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
  20. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
  21. package/dist/dto/update-collaborator-type.dto.js +2 -1
  22. package/dist/dto/update-collaborator-type.dto.js.map +1 -1
  23. package/dist/dto/update-task.dto.d.ts +7 -1
  24. package/dist/dto/update-task.dto.d.ts.map +1 -1
  25. package/dist/dto/update-task.dto.js +38 -5
  26. package/dist/dto/update-task.dto.js.map +1 -1
  27. package/dist/operations.service.d.ts +90 -12
  28. package/dist/operations.service.d.ts.map +1 -1
  29. package/dist/operations.service.js +560 -148
  30. package/dist/operations.service.js.map +1 -1
  31. package/dist/operations.service.spec.js +73 -0
  32. package/dist/operations.service.spec.js.map +1 -1
  33. package/hedhog/data/menu.yaml +26 -26
  34. package/hedhog/data/operations_collaborator_type.yaml +76 -76
  35. package/hedhog/data/route.yaml +26 -0
  36. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
  37. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +44 -44
  38. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +168 -213
  39. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  40. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  41. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  42. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  43. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  44. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  45. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +528 -403
  46. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  47. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  48. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
  49. package/hedhog/frontend/app/_lib/types.ts.ejs +5 -0
  50. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +7 -7
  51. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  52. package/hedhog/frontend/app/approvals/page.tsx.ejs +2 -2
  53. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +513 -502
  54. package/hedhog/frontend/app/collaborators/page.tsx.ejs +10 -7
  55. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  56. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  57. package/hedhog/frontend/app/projects/page.tsx.ejs +360 -133
  58. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +235 -72
  59. package/hedhog/frontend/app/timesheets/page.tsx.ejs +344 -134
  60. package/hedhog/frontend/messages/en.json +32 -4
  61. package/hedhog/frontend/messages/pt.json +34 -6
  62. package/hedhog/table/operations_collaborator.yaml +18 -18
  63. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
  64. package/hedhog/table/operations_collaborator_type.yaml +33 -33
  65. package/hedhog/table/operations_contract_document.yaml +33 -33
  66. package/hedhog/table/operations_project.yaml +9 -0
  67. package/hedhog/table/operations_task.yaml +43 -4
  68. package/package.json +6 -6
  69. package/src/controllers/operations-tasks.controller.ts +11 -0
  70. package/src/controllers/operations-timesheets.controller.ts +13 -0
  71. package/src/dto/create-collaborator-type.dto.ts +43 -43
  72. package/src/dto/create-collaborator.dto.ts +223 -223
  73. package/src/dto/create-task.dto.ts +47 -7
  74. package/src/dto/list-collaborator-types.dto.ts +15 -15
  75. package/src/dto/list-collaborators.dto.ts +30 -30
  76. package/src/dto/list-tasks.dto.ts +3 -3
  77. package/src/dto/update-collaborator-type.dto.ts +4 -3
  78. package/src/dto/update-collaborator.dto.ts +3 -3
  79. package/src/dto/update-task.dto.ts +47 -7
  80. package/src/operations.service.spec.ts +96 -0
  81. 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 { parseNumberInput, trimToNull } from '../_lib/utils/forms';
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.relatedContracts?.[0]?.budgetAmount !== null &&
318
- collaborator.relatedContracts?.[0]?.budgetAmount !== undefined
319
- ? String(collaborator.relatedContracts[0].budgetAmount)
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: collaborator.equityParticipation?.startDate ?? '',
339
- endDate: collaborator.equityParticipation?.endDate ?? '',
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<SystemUserOption[]>({
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
- SystemUserOption[] | { data?: SystemUserOption[] }
725
- >({
726
- url: '/person/owner-options',
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 options = Array.isArray(response.data)
731
- ? response.data
732
- : response.data?.data || [];
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 [...options].sort((left, right) =>
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
- collaboratorTypes.find(
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
- [collaboratorTypes, form.collaboratorTypeId]
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
- EQUITY_ENABLED_TYPE_SLUGS.has(selectedCollaboratorType.slug)
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: parseNumberInput(form.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
- <SystemUserAutocomplete
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={form.compensationAmount}
1471
- onChange={(event) =>
1421
+ value={
1422
+ form.compensationAmount === ''
1423
+ ? ''
1424
+ : Number(form.compensationAmount)
1425
+ }
1426
+ onValueChange={(value) =>
1472
1427
  setForm((current) => ({
1473
1428
  ...current,
1474
- compensationAmount: event.target.value,
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="number"
1538
- min="0"
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="number"
1558
- min="0"
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={collaborators.filter((item) => item.id !== collaboratorId)}
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={form.compensationAmount}
1687
- onChange={(event) =>
1637
+ value={
1638
+ form.compensationAmount === ''
1639
+ ? ''
1640
+ : Number(form.compensationAmount)
1641
+ }
1642
+ onValueChange={(value) =>
1688
1643
  setForm((current) => ({
1689
1644
  ...current,
1690
- compensationAmount: event.target.value,
1645
+ compensationAmount: value !== null ? String(value) : '',
1691
1646
  }))
1692
1647
  }
1693
1648
  />