@hed-hog/operations 0.0.305 → 0.0.309

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 (138) hide show
  1. package/dist/controllers/operations-approvals.controller.d.ts +114 -1
  2. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-approvals.controller.js +16 -3
  4. package/dist/controllers/operations-approvals.controller.js.map +1 -1
  5. package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +16 -3
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-contracts.controller.d.ts +14 -453
  10. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-contracts.controller.js +11 -112
  12. package/dist/controllers/operations-contracts.controller.js.map +1 -1
  13. package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
  14. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
  15. package/dist/controllers/operations-org-structure.controller.js +18 -5
  16. package/dist/controllers/operations-org-structure.controller.js.map +1 -1
  17. package/dist/controllers/operations-projects.controller.d.ts +28 -4
  18. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-projects.controller.js +17 -5
  20. package/dist/controllers/operations-projects.controller.js.map +1 -1
  21. package/dist/controllers/operations-timesheets.controller.d.ts +52 -4
  22. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  23. package/dist/controllers/operations-timesheets.controller.js +28 -11
  24. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  25. package/dist/dto/list-approvals.dto.d.ts +6 -0
  26. package/dist/dto/list-approvals.dto.d.ts.map +1 -0
  27. package/dist/dto/list-approvals.dto.js +28 -0
  28. package/dist/dto/list-approvals.dto.js.map +1 -0
  29. package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
  30. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
  31. package/dist/dto/list-collaborator-types.dto.js +7 -1
  32. package/dist/dto/list-collaborator-types.dto.js.map +1 -1
  33. package/dist/dto/list-collaborators.dto.d.ts +1 -0
  34. package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
  35. package/dist/dto/list-collaborators.dto.js +5 -0
  36. package/dist/dto/list-collaborators.dto.js.map +1 -1
  37. package/dist/dto/list-contracts.dto.d.ts +8 -0
  38. package/dist/dto/list-contracts.dto.d.ts.map +1 -0
  39. package/dist/dto/list-contracts.dto.js +38 -0
  40. package/dist/dto/list-contracts.dto.js.map +1 -0
  41. package/dist/dto/list-departments.dto.d.ts +5 -0
  42. package/dist/dto/list-departments.dto.d.ts.map +1 -0
  43. package/dist/dto/list-departments.dto.js +23 -0
  44. package/dist/dto/list-departments.dto.js.map +1 -0
  45. package/dist/dto/list-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-projects.dto.js +23 -0
  48. package/dist/dto/list-projects.dto.js.map +1 -0
  49. package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
  50. package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
  51. package/dist/dto/list-schedule-adjustments.dto.js +23 -0
  52. package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
  53. package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
  54. package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
  55. package/dist/dto/list-time-off-requests.dto.js +23 -0
  56. package/dist/dto/list-time-off-requests.dto.js.map +1 -0
  57. package/dist/dto/list-timesheets.dto.d.ts +5 -0
  58. package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
  59. package/dist/dto/list-timesheets.dto.js +23 -0
  60. package/dist/dto/list-timesheets.dto.js.map +1 -0
  61. package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
  62. package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
  63. package/dist/dto/reorder-collaborator-types.dto.js +25 -0
  64. package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
  65. package/dist/dto/update-collaborator-type.dto.d.ts +3 -1
  66. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -1
  67. package/dist/dto/update-collaborator-type.dto.js +2 -1
  68. package/dist/dto/update-collaborator-type.dto.js.map +1 -1
  69. package/dist/operations.service.d.ts +362 -271
  70. package/dist/operations.service.d.ts.map +1 -1
  71. package/dist/operations.service.js +1195 -1098
  72. package/dist/operations.service.js.map +1 -1
  73. package/dist/operations.service.spec.js +73 -22
  74. package/dist/operations.service.spec.js.map +1 -1
  75. package/hedhog/data/menu.yaml +19 -55
  76. package/hedhog/data/operations_collaborator_type.yaml +76 -76
  77. package/hedhog/data/route.yaml +52 -70
  78. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +5 -3
  79. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
  80. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
  81. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
  82. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
  83. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
  84. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
  85. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
  86. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +1 -0
  87. package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
  88. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
  89. package/hedhog/frontend/app/approvals/page.tsx.ejs +843 -151
  90. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +457 -154
  91. package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
  92. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
  93. package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
  94. package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
  95. package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
  96. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +546 -118
  97. package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
  98. package/hedhog/frontend/app/timesheets/page.tsx.ejs +647 -342
  99. package/hedhog/frontend/messages/en.json +148 -14
  100. package/hedhog/frontend/messages/pt.json +199 -56
  101. package/hedhog/table/operations_collaborator.yaml +18 -18
  102. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -43
  103. package/hedhog/table/operations_collaborator_type.yaml +33 -33
  104. package/hedhog/table/operations_contract.yaml +0 -9
  105. package/hedhog/table/operations_contract_document.yaml +33 -33
  106. package/package.json +4 -4
  107. package/src/controllers/operations-approvals.controller.ts +9 -3
  108. package/src/controllers/operations-collaborators.controller.ts +15 -2
  109. package/src/controllers/operations-contracts.controller.ts +8 -92
  110. package/src/controllers/operations-org-structure.controller.ts +17 -4
  111. package/src/controllers/operations-projects.controller.ts +10 -4
  112. package/src/controllers/operations-timesheets.controller.ts +30 -8
  113. package/src/dto/create-collaborator-type.dto.ts +43 -43
  114. package/src/dto/create-collaborator.dto.ts +223 -223
  115. package/src/dto/list-approvals.dto.ts +12 -0
  116. package/src/dto/list-collaborator-types.dto.ts +20 -15
  117. package/src/dto/list-collaborators.dto.ts +34 -30
  118. package/src/dto/list-contracts.dto.ts +20 -0
  119. package/src/dto/list-departments.dto.ts +8 -0
  120. package/src/dto/list-projects.dto.ts +8 -0
  121. package/src/dto/list-schedule-adjustments.dto.ts +8 -0
  122. package/src/dto/list-time-off-requests.dto.ts +8 -0
  123. package/src/dto/list-timesheets.dto.ts +8 -0
  124. package/src/dto/reorder-collaborator-types.dto.ts +10 -0
  125. package/src/dto/update-collaborator-type.dto.ts +4 -3
  126. package/src/dto/update-collaborator.dto.ts +3 -3
  127. package/src/operations.service.spec.ts +96 -30
  128. package/src/operations.service.ts +1738 -1777
  129. package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
  130. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
  131. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
  132. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
  133. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
  134. package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
  135. package/hedhog/table/operations_contract_financial_term.yaml +0 -40
  136. package/hedhog/table/operations_contract_revision.yaml +0 -38
  137. package/hedhog/table/operations_contract_signature.yaml +0 -38
  138. package/hedhog/table/operations_contract_template.yaml +0 -58
@@ -316,12 +316,54 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
316
316
  const t = useTranslations('operations.ProjectDetailsPage');
317
317
  const commonT = useTranslations('operations.Common');
318
318
  const formT = useTranslations('operations.ProjectFormPage');
319
+ const contractT = useTranslations('operations.ContractFormPage');
319
320
  const { request, currentLocaleCode, getSettingValue } = useApp();
320
321
  const access = useOperationsAccess();
321
322
  const router = useRouter();
322
323
  const pathname = usePathname();
323
324
  const searchParams = useSearchParams();
324
325
 
326
+ const getProjectStatusLabel = (value?: string | null) => {
327
+ if (!value) return commonT('labels.notAvailable');
328
+ try {
329
+ return formT(`options.statuses.${value}`);
330
+ } catch {
331
+ return formatEnumLabel(value);
332
+ }
333
+ };
334
+ const getContractStatusLabel = (value?: string | null) => {
335
+ if (!value) return commonT('labels.notAvailable');
336
+ try {
337
+ return contractT(`options.statuses.${value}`);
338
+ } catch {
339
+ return formatEnumLabel(value);
340
+ }
341
+ };
342
+ const getContractCategoryLabel = (value?: string | null) => {
343
+ if (!value) return commonT('labels.notAvailable');
344
+ try {
345
+ return contractT(`options.contractCategories.${value}`);
346
+ } catch {
347
+ return formatEnumLabel(value);
348
+ }
349
+ };
350
+ const getContractTypeLabel = (value?: string | null) => {
351
+ if (!value) return commonT('labels.notAvailable');
352
+ try {
353
+ return contractT(`options.contractTypes.${value}`);
354
+ } catch {
355
+ return formatEnumLabel(value);
356
+ }
357
+ };
358
+ const getSignatureStatusLabel = (value?: string | null) => {
359
+ if (!value) return commonT('labels.notAvailable');
360
+ try {
361
+ return contractT(`options.signatureStatuses.${value}`);
362
+ } catch {
363
+ return formatEnumLabel(value);
364
+ }
365
+ };
366
+
325
367
  const isEditSheetOpen = useMemo(
326
368
  () => shouldOpenEditSheet(searchParams.get('edit'), projectId),
327
369
  [projectId, searchParams]
@@ -401,6 +443,21 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
401
443
  enabled: Boolean(project),
402
444
  });
403
445
 
446
+ const { data: projectStats } = useQuery<{
447
+ weeklyVelocity: Array<{ weekLabel: string; loggedHours: number }>;
448
+ allocationByCollaborator: Array<{ name: string; allocation: number }>;
449
+ quickRadar: {
450
+ activeAssignments: number;
451
+ pendingTimesheets: number;
452
+ totalWeeklyHours: number;
453
+ };
454
+ }>({
455
+ queryKey: ['operations-project-stats', projectId],
456
+ queryFn: () =>
457
+ fetchOperations(request, `/operations/projects/${projectId}/stats`),
458
+ enabled: Boolean(project),
459
+ });
460
+
404
461
  const [boardState, setBoardState] = useState<BoardState | null>(null);
405
462
  const [selectedTaskId, setSelectedTaskId] = useState<number | null>(null);
406
463
  const [taskFormOpen, setTaskFormOpen] = useState(false);
@@ -543,18 +600,20 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
543
600
  );
544
601
 
545
602
  const allocationChartData = useMemo(() => {
603
+ if (projectStats?.allocationByCollaborator?.length) {
604
+ return projectStats.allocationByCollaborator;
605
+ }
546
606
  if (!project) {
547
607
  return [];
548
608
  }
549
-
550
609
  return project.assignments.slice(0, 6).map((assignment) => ({
551
610
  name: getInitials(assignment.collaboratorName),
552
611
  allocation:
553
612
  typeof assignment.allocationPercent === 'number'
554
- ? Math.round(assignment.allocationPercent * 100)
613
+ ? Math.round(assignment.allocationPercent)
555
614
  : 0,
556
615
  }));
557
- }, [project]);
616
+ }, [project, projectStats]);
558
617
 
559
618
  const sensors = useSensors(
560
619
  useSensor(PointerSensor, {
@@ -563,22 +622,15 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
563
622
  );
564
623
 
565
624
  const velocityChartData = useMemo(() => {
566
- if (!project) {
567
- return [];
625
+ if (projectStats?.weeklyVelocity?.length) {
626
+ return projectStats.weeklyVelocity.map((row) => ({
627
+ week: row.weekLabel,
628
+ loggedHours: row.loggedHours,
629
+ completedTasks: 0,
630
+ }));
568
631
  }
569
-
570
- const totalHours = Math.max(project.timesheetSummary.totalHours ?? 0, 8);
571
- const closedBase = Math.max(
572
- project.operationalIndicators.billableAssignments,
573
- 1
574
- );
575
-
576
- return [0, 1, 2, 3, 4, 5].map((index) => ({
577
- week: `S${index + 1}`,
578
- loggedHours: Math.round((totalHours / 6) * (0.8 + index * 0.08)),
579
- completedTasks: Math.round((closedBase / 6) * (0.6 + index * 0.1)),
580
- }));
581
- }, [project]);
632
+ return [];
633
+ }, [projectStats]);
582
634
 
583
635
  const findColumnByTask = (taskId: number) => {
584
636
  const match = KANBAN_COLUMNS.find((column) =>
@@ -781,7 +833,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
781
833
  </dt>
782
834
  <dd className="font-medium">
783
835
  <StatusBadge
784
- label={formatEnumLabel(project.status)}
836
+ label={getProjectStatusLabel(project.status)}
785
837
  className={getStatusBadgeClass(project.status)}
786
838
  />
787
839
  </dd>
@@ -826,7 +878,11 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
826
878
  </dt>
827
879
  <dd className="font-medium">
828
880
  {project.budgetAmount
829
- ? formatCurrency(project.budgetAmount)
881
+ ? formatCurrency(
882
+ project.budgetAmount,
883
+ getSettingValue,
884
+ currentLocaleCode
885
+ )
830
886
  : commonT('labels.notAvailable')}
831
887
  </dd>
832
888
  </div>
@@ -858,7 +914,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
858
914
  <dd className="font-medium">
859
915
  {project.contractStatus ? (
860
916
  <StatusBadge
861
- label={formatEnumLabel(project.contractStatus)}
917
+ label={getContractStatusLabel(project.contractStatus)}
862
918
  className={getStatusBadgeClass(project.contractStatus)}
863
919
  />
864
920
  ) : (
@@ -895,7 +951,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
895
951
  </div>
896
952
  </div>
897
953
  <StatusBadge
898
- label={formatEnumLabel(project.relatedContract.status)}
954
+ label={getContractStatusLabel(project.relatedContract.status)}
899
955
  className={getStatusBadgeClass(
900
956
  project.relatedContract.status
901
957
  )}
@@ -908,7 +964,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
908
964
  </dt>
909
965
  <dd className="font-medium">
910
966
  {project.relatedContract.contractCategory
911
- ? formatEnumLabel(
967
+ ? getContractCategoryLabel(
912
968
  project.relatedContract.contractCategory
913
969
  )
914
970
  : commonT('labels.notAvailable')}
@@ -920,7 +976,9 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
920
976
  </dt>
921
977
  <dd className="font-medium">
922
978
  {project.relatedContract.contractType
923
- ? formatEnumLabel(project.relatedContract.contractType)
979
+ ? getContractTypeLabel(
980
+ project.relatedContract.contractType
981
+ )
924
982
  : commonT('labels.notAvailable')}
925
983
  </dd>
926
984
  </div>
@@ -951,7 +1009,9 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
951
1009
  </dt>
952
1010
  <dd className="font-medium">
953
1011
  {project.relatedContract.signatureStatus
954
- ? formatEnumLabel(project.relatedContract.signatureStatus)
1012
+ ? getSignatureStatusLabel(
1013
+ project.relatedContract.signatureStatus
1014
+ )
955
1015
  : commonT('labels.notAvailable')}
956
1016
  </dd>
957
1017
  </div>
@@ -961,7 +1021,11 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
961
1021
  </dt>
962
1022
  <dd className="font-medium">
963
1023
  {project.relatedContract.budgetAmount
964
- ? formatCurrency(project.relatedContract.budgetAmount)
1024
+ ? formatCurrency(
1025
+ project.relatedContract.budgetAmount,
1026
+ getSettingValue,
1027
+ currentLocaleCode
1028
+ )
965
1029
  : commonT('labels.notAvailable')}
966
1030
  </dd>
967
1031
  </div>
@@ -984,15 +1048,15 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
984
1048
 
985
1049
  <div className="grid gap-4 xl:grid-cols-12">
986
1050
  <SectionCard
987
- title="Saude da entrega"
988
- description="Leitura visual de alocacao e ritmo operacional da equipe."
1051
+ title={t('sections.deliveryHealth')}
1052
+ description={t('sections.deliveryHealthDescription')}
989
1053
  className="rounded-xl border bg-card p-4 shadow-sm xl:col-span-7"
990
1054
  >
991
1055
  <div className="grid gap-4 lg:grid-cols-2">
992
1056
  <div className="rounded-lg border bg-muted/10 p-3">
993
1057
  <div className="mb-2 flex items-center gap-2 text-sm font-medium">
994
1058
  <BarChart3 className="size-4 text-sky-700" />
995
- Alocacao por colaborador
1059
+ {t('charts.allocationByCollaborator')}
996
1060
  </div>
997
1061
  <ChartContainer className="h-60 w-full" config={boardChartConfig}>
998
1062
  <BarChart data={allocationChartData}>
@@ -1012,7 +1076,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1012
1076
  <div className="rounded-lg border bg-muted/10 p-3">
1013
1077
  <div className="mb-2 flex items-center gap-2 text-sm font-medium">
1014
1078
  <Rocket className="size-4 text-emerald-700" />
1015
- Velocidade semanal
1079
+ {t('charts.weeklyVelocity')}
1016
1080
  </div>
1017
1081
  <ChartContainer className="h-60 w-full" config={boardChartConfig}>
1018
1082
  <LineChart data={velocityChartData}>
@@ -1041,38 +1105,43 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1041
1105
  </SectionCard>
1042
1106
 
1043
1107
  <SectionCard
1044
- title="Radar rapido"
1045
- description="Sinais para tomada de decisao no curto prazo."
1108
+ title={t('sections.quickRadar')}
1109
+ description={t('sections.quickRadarDescription')}
1046
1110
  className="rounded-xl border bg-card p-4 shadow-sm xl:col-span-5"
1047
1111
  >
1048
1112
  <div className="space-y-3">
1049
1113
  <div className="rounded-lg border bg-emerald-50/50 p-3">
1050
1114
  <div className="flex items-center justify-between text-sm">
1051
1115
  <span className="text-muted-foreground">
1052
- Atribuicoes ativas
1116
+ {t('quickRadar.activeAssignments')}
1053
1117
  </span>
1054
1118
  <span className="font-semibold text-emerald-700">
1055
- {project.operationalIndicators.activeAssignments}
1119
+ {projectStats?.quickRadar?.activeAssignments ??
1120
+ project.operationalIndicators.activeAssignments}
1056
1121
  </span>
1057
1122
  </div>
1058
1123
  </div>
1059
1124
  <div className="rounded-lg border bg-amber-50/50 p-3">
1060
1125
  <div className="flex items-center justify-between text-sm">
1061
1126
  <span className="text-muted-foreground">
1062
- Pendencias de timesheet
1127
+ {t('quickRadar.timesheetPendencies')}
1063
1128
  </span>
1064
1129
  <span className="font-semibold text-amber-700">
1065
- {project.timesheetSummary.pendingTimesheets}
1130
+ {projectStats?.quickRadar?.pendingTimesheets ??
1131
+ project.timesheetSummary.pendingTimesheets}
1066
1132
  </span>
1067
1133
  </div>
1068
1134
  </div>
1069
1135
  <div className="rounded-lg border bg-sky-50/50 p-3">
1070
1136
  <div className="flex items-center justify-between text-sm">
1071
1137
  <span className="text-muted-foreground">
1072
- Horas semanais planejadas
1138
+ {t('quickRadar.plannedWeeklyHours')}
1073
1139
  </span>
1074
1140
  <span className="font-semibold text-sky-700">
1075
- {formatHours(project.operationalIndicators.totalWeeklyHours)}
1141
+ {formatHours(
1142
+ projectStats?.quickRadar?.totalWeeklyHours ??
1143
+ project.operationalIndicators.totalWeeklyHours
1144
+ )}
1076
1145
  </span>
1077
1146
  </div>
1078
1147
  </div>
@@ -1081,8 +1150,8 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1081
1150
  </div>
1082
1151
 
1083
1152
  <SectionCard
1084
- title="Quadro de tarefas"
1085
- description="Board estilo Kanban com arraste entre colunas e detalhe lateral da tarefa."
1153
+ title={t('sections.taskBoard')}
1154
+ description={t('sections.taskBoardDescription')}
1086
1155
  className="rounded-xl border bg-card p-4 shadow-sm"
1087
1156
  actions={
1088
1157
  <Button
@@ -1091,7 +1160,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1091
1160
  onClick={() => openCreateTaskForm()}
1092
1161
  >
1093
1162
  <Plus className="size-4" />
1094
- Nova tarefa
1163
+ {t('taskForm.titleNew')}
1095
1164
  </Button>
1096
1165
  }
1097
1166
  >
@@ -1182,6 +1251,38 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1182
1251
  : ''}
1183
1252
  </span>
1184
1253
  </div>
1254
+
1255
+ {task.assigneeName ? (
1256
+ <div className="mt-2 flex items-center gap-1.5">
1257
+ <div className="flex size-5 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-[9px] font-semibold uppercase text-muted-foreground ring-1 ring-border">
1258
+ {(() => {
1259
+ const photoUrl = getUserPhotoUrl(
1260
+ task.assigneeUserPhotoId
1261
+ );
1262
+ const avatarUrl =
1263
+ task.assigneePersonAvatarId
1264
+ ? getPersonAvatarUrl(
1265
+ task.assigneePersonAvatarId
1266
+ )
1267
+ : null;
1268
+ const imgSrc = photoUrl ?? avatarUrl;
1269
+ return imgSrc ? (
1270
+ // eslint-disable-next-line @next/next/no-img-element
1271
+ <img
1272
+ src={imgSrc}
1273
+ alt={task.assigneeName}
1274
+ className="size-full object-cover"
1275
+ />
1276
+ ) : (
1277
+ getInitials(task.assigneeName)
1278
+ );
1279
+ })()}
1280
+ </div>
1281
+ <span className="truncate text-[11px] text-muted-foreground">
1282
+ {task.assigneeName}
1283
+ </span>
1284
+ </div>
1285
+ ) : null}
1185
1286
  </button>
1186
1287
  )}
1187
1288
  </DraggableTaskCard>
@@ -1291,10 +1392,10 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1291
1392
  </div>
1292
1393
  <div>
1293
1394
  <dt className="text-muted-foreground">
1294
- {t('indicators.billableAssignments')}
1395
+ {t('indicators.completedAssignments')}
1295
1396
  </dt>
1296
1397
  <dd className="font-medium">
1297
- {project.operationalIndicators.billableAssignments}
1398
+ {project.operationalIndicators.completedAssignments}
1298
1399
  </dd>
1299
1400
  </div>
1300
1401
  <div>
@@ -1518,16 +1619,16 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1518
1619
  <DialogContent className="sm:max-w-lg">
1519
1620
  <DialogHeader>
1520
1621
  <DialogTitle>
1521
- {editingTaskId ? 'Editar tarefa' : 'Nova tarefa'}
1622
+ {editingTaskId ? t('taskForm.titleEdit') : t('taskForm.titleNew')}
1522
1623
  </DialogTitle>
1523
1624
  </DialogHeader>
1524
1625
 
1525
1626
  <div className="space-y-4">
1526
1627
  <div className="space-y-1.5">
1527
- <Label htmlFor="task-name">Nome *</Label>
1628
+ <Label htmlFor="task-name">{t('taskForm.nameLabel')} *</Label>
1528
1629
  <Input
1529
1630
  id="task-name"
1530
- placeholder="Nome da tarefa"
1631
+ placeholder={t('taskForm.namePlaceholder')}
1531
1632
  value={taskFormData.name}
1532
1633
  onChange={(e) =>
1533
1634
  setTaskFormData((prev) => ({ ...prev, name: e.target.value }))
@@ -1536,10 +1637,12 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1536
1637
  </div>
1537
1638
 
1538
1639
  <div className="space-y-1.5">
1539
- <Label htmlFor="task-description">Descricao</Label>
1640
+ <Label htmlFor="task-description">
1641
+ {t('taskForm.descriptionLabel')}
1642
+ </Label>
1540
1643
  <Textarea
1541
1644
  id="task-description"
1542
- placeholder="Descricao opcional"
1645
+ placeholder={t('taskForm.descriptionPlaceholder')}
1543
1646
  rows={3}
1544
1647
  value={taskFormData.description}
1545
1648
  onChange={(e) =>
@@ -1553,7 +1656,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1553
1656
 
1554
1657
  <div className="grid grid-cols-2 gap-3">
1555
1658
  <div className="space-y-1.5">
1556
- <Label>Prioridade</Label>
1659
+ <Label>{t('taskForm.priorityLabel')}</Label>
1557
1660
  <Select
1558
1661
  value={taskFormData.priority}
1559
1662
  onValueChange={(v) =>
@@ -1563,7 +1666,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1563
1666
  }))
1564
1667
  }
1565
1668
  >
1566
- <SelectTrigger>
1669
+ <SelectTrigger className="w-full">
1567
1670
  <SelectValue />
1568
1671
  </SelectTrigger>
1569
1672
  <SelectContent>
@@ -1581,7 +1684,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1581
1684
  </div>
1582
1685
 
1583
1686
  <div className="space-y-1.5">
1584
- <Label>Coluna</Label>
1687
+ <Label>{t('taskForm.columnLabel')}</Label>
1585
1688
  <Select
1586
1689
  value={taskFormData.status}
1587
1690
  onValueChange={(v) =>
@@ -1591,7 +1694,7 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1591
1694
  }))
1592
1695
  }
1593
1696
  >
1594
- <SelectTrigger>
1697
+ <SelectTrigger className="w-full">
1595
1698
  <SelectValue />
1596
1699
  </SelectTrigger>
1597
1700
  <SelectContent>
@@ -1634,7 +1737,9 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1634
1737
 
1635
1738
  <div className="grid grid-cols-2 gap-3">
1636
1739
  <div className="space-y-1.5">
1637
- <Label htmlFor="task-due-date">Prazo</Label>
1740
+ <Label htmlFor="task-due-date">
1741
+ {t('taskForm.deadlineLabel')}
1742
+ </Label>
1638
1743
  <Input
1639
1744
  id="task-due-date"
1640
1745
  type="date"
@@ -1649,7 +1754,9 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1649
1754
  </div>
1650
1755
 
1651
1756
  <div className="space-y-1.5">
1652
- <Label htmlFor="task-estimate">Estimativa (h)</Label>
1757
+ <Label htmlFor="task-estimate">
1758
+ {t('taskForm.estimateLabel')}
1759
+ </Label>
1653
1760
  <Input
1654
1761
  id="task-estimate"
1655
1762
  type="number"
@@ -1668,10 +1775,10 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1668
1775
  </div>
1669
1776
 
1670
1777
  <div className="space-y-1.5">
1671
- <Label htmlFor="task-tags">Etiquetas</Label>
1778
+ <Label htmlFor="task-tags">{t('taskForm.tagsLabel')}</Label>
1672
1779
  <Input
1673
1780
  id="task-tags"
1674
- placeholder="planejamento, cliente, design (separadas por virgula)"
1781
+ placeholder={t('taskForm.tagsPlaceholder')}
1675
1782
  value={taskFormData.tags}
1676
1783
  onChange={(e) =>
1677
1784
  setTaskFormData((prev) => ({ ...prev, tags: e.target.value }))
@@ -1690,17 +1797,17 @@ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
1690
1797
  }}
1691
1798
  disabled={taskFormLoading}
1692
1799
  >
1693
- Cancelar
1800
+ {commonT('actions.cancel')}
1694
1801
  </Button>
1695
1802
  <Button
1696
1803
  onClick={() => void handleTaskFormSubmit()}
1697
1804
  disabled={taskFormLoading || !taskFormData.name.trim()}
1698
1805
  >
1699
1806
  {taskFormLoading
1700
- ? 'Salvando...'
1807
+ ? t('taskForm.saving')
1701
1808
  : editingTaskId
1702
- ? 'Salvar'
1703
- : 'Criar'}
1809
+ ? commonT('actions.save')
1810
+ : commonT('actions.create')}
1704
1811
  </Button>
1705
1812
  </DialogFooter>
1706
1813
  </DialogContent>