@hed-hog/operations 0.0.332 → 0.0.338

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 (109) hide show
  1. package/dist/controllers/operations-collaborators.controller.d.ts +0 -54
  2. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-collaborators.controller.js +0 -100
  4. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  5. package/dist/controllers/operations-contracts.controller.d.ts +12 -12
  6. package/dist/operations.service.d.ts +0 -76
  7. package/dist/operations.service.d.ts.map +1 -1
  8. package/dist/operations.service.js +7 -230
  9. package/dist/operations.service.js.map +1 -1
  10. package/dist/operations.service.spec.js +6 -0
  11. package/dist/operations.service.spec.js.map +1 -1
  12. package/hedhog/data/menu.yaml +8 -27
  13. package/hedhog/data/route.yaml +0 -72
  14. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +476 -0
  15. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +3 -39
  16. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +261 -0
  17. package/hedhog/frontend/app/_components/collaborator-tasks-tab.tsx.ejs +358 -358
  18. package/hedhog/frontend/app/_components/collaborator-timesheets-tab.tsx.ejs +6 -6
  19. package/hedhog/frontend/app/_components/contract-content-editor.tsx.ejs +258 -0
  20. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +5 -4
  21. package/hedhog/frontend/app/_components/person-select-with-create.tsx.ejs +1 -0
  22. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +10 -218
  23. package/hedhog/frontend/app/_components/project-cost-report-screen.tsx.ejs +23 -23
  24. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +24 -708
  25. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +38 -158
  26. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +1 -5
  27. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -629
  28. package/hedhog/frontend/app/_lib/api.ts.ejs +0 -151
  29. package/hedhog/frontend/app/_lib/types.ts.ejs +0 -1
  30. package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +0 -18
  31. package/hedhog/frontend/app/my-projects/page.tsx.ejs +2 -16
  32. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +86 -24
  33. package/hedhog/frontend/app/projects/page.tsx.ejs +6 -42
  34. package/hedhog/frontend/messages/en.json +2 -96
  35. package/hedhog/frontend/messages/operations/operations/en.json +2100 -0
  36. package/hedhog/frontend/messages/operations/operations/pt.json +2111 -0
  37. package/hedhog/frontend/messages/pt.json +2 -96
  38. package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +16 -16
  39. package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +16 -16
  40. package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +16 -16
  41. package/hedhog/frontend/widgets/index.ts.ejs +25 -25
  42. package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +16 -16
  43. package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +16 -16
  44. package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +16 -16
  45. package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +16 -16
  46. package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +16 -16
  47. package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +16 -16
  48. package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +16 -16
  49. package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +16 -16
  50. package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +16 -16
  51. package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +16 -16
  52. package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +16 -16
  53. package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +16 -16
  54. package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +16 -16
  55. package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +16 -16
  56. package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +169 -169
  57. package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +16 -16
  58. package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +16 -16
  59. package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +16 -16
  60. package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +16 -16
  61. package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +16 -16
  62. package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +16 -16
  63. package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +16 -16
  64. package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +16 -16
  65. package/hedhog/table/operations_collaborator.yaml +8 -8
  66. package/hedhog/table/operations_task.yaml +76 -76
  67. package/hedhog/table/operations_task_activity.yaml +51 -51
  68. package/package.json +6 -6
  69. package/src/controllers/operations-collaborators.controller.ts +8 -117
  70. package/src/controllers/operations-tasks.controller.ts +156 -156
  71. package/src/dashboard/widgets/MyQuickActions.tsx +22 -22
  72. package/src/dto/create-collaborator.dto.ts +4 -4
  73. package/src/operations.service.spec.ts +1006 -988
  74. package/src/operations.service.ts +7 -323
  75. package/dist/dto/create-collaborator-invoice.dto.d.ts +0 -11
  76. package/dist/dto/create-collaborator-invoice.dto.d.ts.map +0 -1
  77. package/dist/dto/create-collaborator-invoice.dto.js +0 -55
  78. package/dist/dto/create-collaborator-invoice.dto.js.map +0 -1
  79. package/dist/dto/create-collaborator-payment.dto.d.ts +0 -10
  80. package/dist/dto/create-collaborator-payment.dto.d.ts.map +0 -1
  81. package/dist/dto/create-collaborator-payment.dto.js +0 -50
  82. package/dist/dto/create-collaborator-payment.dto.js.map +0 -1
  83. package/dist/dto/list-collaborator-invoice.dto.d.ts +0 -4
  84. package/dist/dto/list-collaborator-invoice.dto.d.ts.map +0 -1
  85. package/dist/dto/list-collaborator-invoice.dto.js +0 -8
  86. package/dist/dto/list-collaborator-invoice.dto.js.map +0 -1
  87. package/dist/dto/list-collaborator-payment.dto.d.ts +0 -4
  88. package/dist/dto/list-collaborator-payment.dto.d.ts.map +0 -1
  89. package/dist/dto/list-collaborator-payment.dto.js +0 -8
  90. package/dist/dto/list-collaborator-payment.dto.js.map +0 -1
  91. package/dist/dto/update-collaborator-invoice.dto.d.ts +0 -6
  92. package/dist/dto/update-collaborator-invoice.dto.d.ts.map +0 -1
  93. package/dist/dto/update-collaborator-invoice.dto.js +0 -9
  94. package/dist/dto/update-collaborator-invoice.dto.js.map +0 -1
  95. package/dist/dto/update-collaborator-payment.dto.d.ts +0 -6
  96. package/dist/dto/update-collaborator-payment.dto.d.ts.map +0 -1
  97. package/dist/dto/update-collaborator-payment.dto.js +0 -9
  98. package/dist/dto/update-collaborator-payment.dto.js.map +0 -1
  99. package/hedhog/frontend/app/_components/collaborator-invoices-tab.tsx.ejs +0 -443
  100. package/hedhog/frontend/app/_components/collaborator-payment-history-tab.tsx.ejs +0 -429
  101. package/hedhog/frontend/app/tasks-gantt/page.tsx.ejs +0 -953
  102. package/hedhog/table/operations_collaborator_invoice.yaml +0 -35
  103. package/hedhog/table/operations_collaborator_payment.yaml +0 -32
  104. package/src/dto/create-collaborator-invoice.dto.ts +0 -39
  105. package/src/dto/create-collaborator-payment.dto.ts +0 -35
  106. package/src/dto/list-collaborator-invoice.dto.ts +0 -3
  107. package/src/dto/list-collaborator-payment.dto.ts +0 -3
  108. package/src/dto/update-collaborator-invoice.dto.ts +0 -6
  109. package/src/dto/update-collaborator-payment.dto.ts +0 -6
@@ -18,7 +18,6 @@ import {
18
18
  CommandItem,
19
19
  CommandList,
20
20
  } from '@/components/ui/command';
21
- import { EntityPicker } from '@/components/ui/entity-picker';
22
21
  import {
23
22
  Form,
24
23
  FormControl,
@@ -50,7 +49,12 @@ import {
50
49
  SheetHeader,
51
50
  SheetTitle,
52
51
  } from '@/components/ui/sheet';
53
- import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
52
+ import {
53
+ Tabs,
54
+ TabsContent,
55
+ TabsList,
56
+ TabsTrigger,
57
+ } from '@/components/ui/tabs';
54
58
  import { Textarea } from '@/components/ui/textarea';
55
59
  import {
56
60
  Tooltip,
@@ -96,36 +100,12 @@ import {
96
100
  trimToNull,
97
101
  } from '../_lib/utils/forms';
98
102
  import { ContractFormScreen } from './contract-form-screen';
103
+ import { DepartmentPicker } from './department-picker';
99
104
  import { OperationsHeader } from './operations-header';
105
+ import { ProjectFileAttachments } from './project-file-attachments';
100
106
 
101
107
  const OPTION_PAGE_SIZE = 12;
102
108
 
103
- function getPersonAvatarUrl(avatarId?: number | null) {
104
- return typeof avatarId === 'number' && avatarId > 0
105
- ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${avatarId}`
106
- : undefined;
107
- }
108
-
109
- function getUserPhotoUrl(photoId?: number | null) {
110
- return typeof photoId === 'number' && photoId > 0
111
- ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/user/avatar/${photoId}`
112
- : undefined;
113
- }
114
-
115
- function getInitials(value?: string | null) {
116
- const parts = String(value ?? '')
117
- .trim()
118
- .split(/\s+/)
119
- .filter(Boolean)
120
- .slice(0, 2);
121
-
122
- if (!parts.length) {
123
- return '??';
124
- }
125
-
126
- return parts.map((part) => part[0]?.toUpperCase() ?? '').join('');
127
- }
128
-
129
109
  type TeamAssignmentState = {
130
110
  collaboratorId: number;
131
111
  selected: boolean;
@@ -665,7 +645,6 @@ export function ProjectFormScreen({
665
645
  const t = useTranslations('operations.ProjectFormPage');
666
646
  const commonT = useTranslations('operations.Common');
667
647
  const contractT = useTranslations('operations.ContractFormPage');
668
- const collaboratorFormT = useTranslations('operations.CollaboratorFormPage');
669
648
  const { request, showToastHandler, currentLocaleCode } = useApp();
670
649
  const access = useOperationsAccess();
671
650
  const router = useRouter();
@@ -673,8 +652,6 @@ export function ProjectFormScreen({
673
652
  const personSheetModeRef = useRef<'create' | 'edit'>('edit');
674
653
  const [personSheetOpen, setPersonSheetOpen] = useState(false);
675
654
  const [personToEdit, setPersonToEdit] = useState<Person | null>(null);
676
- const [createdManagerCollaborators, setCreatedManagerCollaborators] =
677
- useState<OperationsCollaborator[]>([]);
678
655
  const isSheetMode = Boolean(onCancel);
679
656
  const isCreateMode = !projectId;
680
657
  const [codeAutoMode, setCodeAutoMode] = useState(isCreateMode);
@@ -769,37 +746,6 @@ export function ProjectFormScreen({
769
746
  [rawCollaborators]
770
747
  );
771
748
 
772
- const createManagerCollaborator = async (values: Record<string, string>) => {
773
- const displayName = values.displayName?.trim() ?? '';
774
- const weeklyCapacityHours = parseNumberInput(
775
- values.weeklyCapacityHours ?? ''
776
- );
777
-
778
- if (!displayName) {
779
- return null;
780
- }
781
-
782
- const created = await mutateOperations<OperationsCollaborator>(
783
- request,
784
- '/operations/collaborators',
785
- 'POST',
786
- {
787
- displayName,
788
- weeklyCapacityHours,
789
- status: 'active',
790
- autoGenerateContractDraft: false,
791
- }
792
- );
793
-
794
- setCreatedManagerCollaborators((current) => {
795
- const next = current.filter((item) => item.id !== created.id);
796
- next.unshift(created);
797
- return next;
798
- });
799
-
800
- return created;
801
- };
802
-
803
749
  const { data: contracts = [], refetch: refetchContracts } = useQuery<
804
750
  OperationsContract[]
805
751
  >({
@@ -873,10 +819,6 @@ export function ProjectFormScreen({
873
819
  byId.set(collaborator.id, collaborator);
874
820
  }
875
821
 
876
- for (const collaborator of createdManagerCollaborators) {
877
- byId.set(collaborator.id, collaborator);
878
- }
879
-
880
822
  if (
881
823
  project?.managerCollaboratorId &&
882
824
  !byId.has(project.managerCollaboratorId)
@@ -905,7 +847,7 @@ export function ProjectFormScreen({
905
847
  }
906
848
 
907
849
  return Array.from(byId.values());
908
- }, [collaborators, createdManagerCollaborators, project]);
850
+ }, [collaborators, project]);
909
851
 
910
852
  const availableContracts = useMemo(() => {
911
853
  if (!project?.relatedContract) {
@@ -938,9 +880,10 @@ export function ProjectFormScreen({
938
880
  .filter(Boolean)
939
881
  .join(' • '),
940
882
  avatarUrl:
941
- getUserPhotoUrl(collaborator.userPhotoId) ??
942
- getPersonAvatarUrl(collaborator.personAvatarId) ??
943
- null,
883
+ typeof collaborator.personAvatarId === 'number' &&
884
+ collaborator.personAvatarId > 0
885
+ ? `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${collaborator.personAvatarId}`
886
+ : null,
944
887
  })),
945
888
  [availableCollaborators]
946
889
  );
@@ -1321,100 +1264,20 @@ export function ProjectFormScreen({
1321
1264
  )}
1322
1265
  </p>
1323
1266
  </div>
1324
- <div className="grid min-w-0 gap-3 md:grid-cols-2 xl:grid-cols-3">
1267
+ <div className="grid min-w-0 gap-3 md:grid-cols-2 xl:grid-cols-5">
1325
1268
  <div className="min-w-0 space-y-2">
1326
1269
  <FieldLabel label={commonT('labels.manager')} />
1327
- <EntityPicker
1328
- value={
1329
- form.managerCollaboratorId === 'none'
1330
- ? null
1331
- : Number(form.managerCollaboratorId)
1332
- }
1270
+ <SearchableSelect
1271
+ label=""
1272
+ value={form.managerCollaboratorId}
1333
1273
  options={managerOptions}
1334
- getOptionValue={(option) => option.id}
1335
- getOptionLabel={(option) => option.title}
1336
- getOptionDescription={(option) =>
1337
- option.description || undefined
1338
- }
1339
- renderOption={({ option }) => (
1340
- <div className="flex min-w-0 items-center gap-2.5">
1341
- <Avatar className="size-6 shrink-0">
1342
- <AvatarImage
1343
- src={option.avatarUrl ?? undefined}
1344
- alt={option.title}
1345
- />
1346
- <AvatarFallback className="text-[10px]">
1347
- {getInitials(option.title)}
1348
- </AvatarFallback>
1349
- </Avatar>
1350
- <div className="min-w-0">
1351
- <div className="truncate text-sm">{option.title}</div>
1352
- {option.description ? (
1353
- <div className="truncate text-xs text-muted-foreground">
1354
- {option.description}
1355
- </div>
1356
- ) : null}
1357
- </div>
1358
- </div>
1359
- )}
1360
- renderSelectedValue={({ option, label }) =>
1361
- option ? (
1362
- <div className="flex min-w-0 items-center gap-2">
1363
- <Avatar className="size-5 shrink-0">
1364
- <AvatarImage
1365
- src={option.avatarUrl ?? undefined}
1366
- alt={option.title}
1367
- />
1368
- <AvatarFallback className="text-[10px]">
1369
- {getInitials(option.title)}
1370
- </AvatarFallback>
1371
- </Avatar>
1372
- <span className="truncate">{option.title}</span>
1373
- </div>
1374
- ) : (
1375
- <span className="text-muted-foreground">{label}</span>
1376
- )
1377
- }
1378
1274
  placeholder={commonT('labels.notAssigned')}
1379
1275
  searchPlaceholder={t('placeholders.managerSearch')}
1380
- emptySelectionLabel={commonT('labels.notAssigned')}
1381
- valueType="number"
1382
- clearable
1383
- allowEmptySelection
1384
- showCreateButton
1385
- entityLabel={commonT('labels.manager').toLowerCase()}
1386
- createActionLabel={`${commonT('actions.create')} ${commonT(
1387
- 'labels.manager'
1388
- ).toLowerCase()}`}
1389
- createTitle={`${commonT('actions.create')} ${commonT(
1390
- 'labels.manager'
1391
- ).toLowerCase()}`}
1392
- createDescription={collaboratorFormT(
1393
- 'sections.employmentInfoCreateDescription'
1394
- )}
1395
- createFields={[
1396
- {
1397
- name: 'displayName',
1398
- label: collaboratorFormT('fields.displayName'),
1399
- placeholder: collaboratorFormT('fields.displayName'),
1400
- required: true,
1401
- },
1402
- {
1403
- name: 'weeklyCapacityHours',
1404
- label: collaboratorFormT('fields.weeklyCapacityHours'),
1405
- placeholder: '40',
1406
- type: 'number',
1407
- },
1408
- ]}
1409
- mapSearchToCreateValues={(search) => ({
1410
- displayName: search,
1411
- weeklyCapacityHours: '40',
1412
- })}
1413
- onCreate={createManagerCollaborator}
1276
+ emptyLabel={commonT('labels.notAssigned')}
1414
1277
  onChange={(value) =>
1415
1278
  setForm((current) => ({
1416
1279
  ...current,
1417
- managerCollaboratorId: value ? String(value) : 'none',
1280
+ managerCollaboratorId: value,
1418
1281
  }))
1419
1282
  }
1420
1283
  />
@@ -1509,11 +1372,12 @@ export function ProjectFormScreen({
1509
1372
  {!isCreateMode ? (
1510
1373
  <>
1511
1374
  <Tabs defaultValue="financials" className="space-y-3">
1512
- <TabsList className="grid w-full grid-cols-2">
1375
+ <TabsList className="grid w-full grid-cols-3">
1513
1376
  <TabsTrigger value="financials">
1514
1377
  {t('sections.financials')}
1515
1378
  </TabsTrigger>
1516
1379
  <TabsTrigger value="team">{t('sections.team')}</TabsTrigger>
1380
+ <TabsTrigger value="files">{t('sections.files')}</TabsTrigger>
1517
1381
  </TabsList>
1518
1382
 
1519
1383
  <TabsContent value="financials" className="space-y-3">
@@ -1623,7 +1487,9 @@ export function ProjectFormScreen({
1623
1487
  }}
1624
1488
  initialValues={{
1625
1489
  code: form.code ? `PRJ-${form.code}` : '',
1626
- name: form.name ? `${form.name} Service Agreement` : '',
1490
+ name: form.name
1491
+ ? `${form.name} Service Agreement`
1492
+ : '',
1627
1493
  clientName: form.clientName,
1628
1494
  contractCategory: 'client',
1629
1495
  contractType: 'service_agreement',
@@ -1800,6 +1666,20 @@ export function ProjectFormScreen({
1800
1666
  </div>
1801
1667
  </div>
1802
1668
  </TabsContent>
1669
+
1670
+ <TabsContent value="files" className="space-y-3">
1671
+ <div className="space-y-0.5">
1672
+ <h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
1673
+ {t('sections.files')}
1674
+ </h3>
1675
+ <p className="text-[11px] text-muted-foreground/80">
1676
+ {t('sections.filesDescription')}
1677
+ </p>
1678
+ </div>
1679
+ {projectId ? (
1680
+ <ProjectFileAttachments projectId={projectId} />
1681
+ ) : null}
1682
+ </TabsContent>
1803
1683
  </Tabs>
1804
1684
  </>
1805
1685
  ) : (
@@ -581,7 +581,6 @@ export function TaskDetailSheet({
581
581
  open,
582
582
  onOpenChange,
583
583
  statusLabel,
584
- footer,
585
584
  defaultTab = 'comments',
586
585
  }: Props) {
587
586
  const detailT = useTranslations('operations.ProjectDetailsPage');
@@ -649,7 +648,7 @@ export function TaskDetailSheet({
649
648
  <p className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
650
649
  {detailT('taskForm.descriptionLabel')}
651
650
  </p>
652
- <CommentContent content={task.description} />
651
+ <p className="text-sm leading-relaxed">{task.description}</p>
653
652
  </div>
654
653
  ) : null}
655
654
 
@@ -789,9 +788,6 @@ export function TaskDetailSheet({
789
788
  </div>
790
789
  </aside>
791
790
  </div>
792
- {footer ? (
793
- <div className="shrink-0 border-t px-5 py-4">{footer}</div>
794
- ) : null}
795
791
  </>
796
792
  ) : null}
797
793
  </SheetContent>