@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
@@ -1,30 +1,30 @@
1
- import { PaginationDTO } from '@hed-hog/api-pagination';
2
- import { Transform } from 'class-transformer';
3
- import { IsInt, IsOptional, IsString } from 'class-validator';
4
-
5
- export class ListCollaboratorsDto extends PaginationDTO {
6
- @IsOptional()
7
- @IsString()
8
- status?: string;
9
-
10
- @IsOptional()
11
- @Transform(({ value }) =>
12
- value === '' || value === undefined || value === null ? undefined : Number(value)
13
- )
14
- @IsInt()
15
- collaboratorTypeId?: number;
16
-
17
- @IsOptional()
18
- @Transform(({ value }) =>
19
- value === '' || value === undefined || value === null ? undefined : Number(value)
20
- )
21
- @IsInt()
22
- departmentId?: number;
23
-
24
- @IsOptional()
25
- @Transform(({ value }) =>
26
- value === '' || value === undefined || value === null ? undefined : Number(value)
27
- )
28
- @IsInt()
29
- jobTitleId?: number;
30
- }
1
+ import { PaginationDTO } from '@hed-hog/api-pagination';
2
+ import { Transform } from 'class-transformer';
3
+ import { IsInt, IsOptional, IsString } from 'class-validator';
4
+
5
+ export class ListCollaboratorsDto extends PaginationDTO {
6
+ @IsOptional()
7
+ @IsString()
8
+ status?: string;
9
+
10
+ @IsOptional()
11
+ @Transform(({ value }) =>
12
+ value === '' || value === undefined || value === null ? undefined : Number(value)
13
+ )
14
+ @IsInt()
15
+ collaboratorTypeId?: number;
16
+
17
+ @IsOptional()
18
+ @Transform(({ value }) =>
19
+ value === '' || value === undefined || value === null ? undefined : Number(value)
20
+ )
21
+ @IsInt()
22
+ departmentId?: number;
23
+
24
+ @IsOptional()
25
+ @Transform(({ value }) =>
26
+ value === '' || value === undefined || value === null ? undefined : Number(value)
27
+ )
28
+ @IsInt()
29
+ jobTitleId?: number;
30
+ }
@@ -18,8 +18,8 @@ export class ListOperationsTasksDto extends PaginationDTO {
18
18
  projectAssignmentId?: number;
19
19
 
20
20
  @IsOptional()
21
- @IsIn(['active', 'completed', 'archived'], {
22
- message: 'status must be active, completed, or archived',
21
+ @IsIn(['todo', 'doing', 'review', 'done'], {
22
+ message: 'status must be todo, doing, review, or done',
23
23
  })
24
- status?: 'active' | 'completed' | 'archived';
24
+ status?: 'todo' | 'doing' | 'review' | 'done';
25
25
  }
@@ -1,3 +1,4 @@
1
- import { CreateCollaboratorTypeDto } from './create-collaborator-type.dto';
2
-
3
- export class UpdateCollaboratorTypeDto extends CreateCollaboratorTypeDto {}
1
+ import { PartialType } from '@nestjs/mapped-types';
2
+ import { CreateCollaboratorTypeDto } from './create-collaborator-type.dto';
3
+
4
+ export class UpdateCollaboratorTypeDto extends PartialType(CreateCollaboratorTypeDto) {}
@@ -1,3 +1,3 @@
1
- import { CreateCollaboratorDto } from './create-collaborator.dto';
2
-
3
- export class UpdateCollaboratorDto extends CreateCollaboratorDto {}
1
+ import { CreateCollaboratorDto } from './create-collaborator.dto';
2
+
3
+ export class UpdateCollaboratorDto extends CreateCollaboratorDto {}
@@ -1,5 +1,12 @@
1
1
  import { Transform } from 'class-transformer';
2
- import { IsIn, IsInt, IsOptional, IsString, MaxLength } from 'class-validator';
2
+ import {
3
+ IsIn,
4
+ IsInt,
5
+ IsNumber,
6
+ IsOptional,
7
+ IsString,
8
+ MaxLength,
9
+ } from 'class-validator';
3
10
 
4
11
  export class UpdateOperationsTaskDto {
5
12
  @IsOptional()
@@ -16,6 +23,13 @@ export class UpdateOperationsTaskDto {
16
23
  @IsInt({ message: 'projectAssignmentId must be an integer' })
17
24
  projectAssignmentId?: number;
18
25
 
26
+ @IsOptional()
27
+ @Transform(({ value }) =>
28
+ value === '' || value === undefined || value === null ? undefined : Number(value)
29
+ )
30
+ @IsInt({ message: 'assigneeCollaboratorId must be an integer' })
31
+ assigneeCollaboratorId?: number;
32
+
19
33
  @IsOptional()
20
34
  @IsString({ message: 'name must be a string' })
21
35
  @MaxLength(180, { message: 'name must have at most 180 characters' })
@@ -23,14 +37,40 @@ export class UpdateOperationsTaskDto {
23
37
 
24
38
  @IsOptional()
25
39
  @IsString({ message: 'description must be a string' })
26
- @MaxLength(500, {
27
- message: 'description must have at most 500 characters',
28
- })
40
+ @MaxLength(2000, { message: 'description must have at most 2000 characters' })
29
41
  description?: string;
30
42
 
31
43
  @IsOptional()
32
- @IsIn(['active', 'completed', 'archived'], {
33
- message: 'status must be active, completed, or archived',
44
+ @IsIn(['low', 'medium', 'high'], {
45
+ message: 'priority must be low, medium, or high',
34
46
  })
35
- status?: 'active' | 'completed' | 'archived';
47
+ priority?: 'low' | 'medium' | 'high';
48
+
49
+ @IsOptional()
50
+ @IsIn(['todo', 'doing', 'review', 'done'], {
51
+ message: 'status must be todo, doing, review, or done',
52
+ })
53
+ status?: 'todo' | 'doing' | 'review' | 'done';
54
+
55
+ @IsOptional()
56
+ @IsString({ message: 'dueDate must be a string' })
57
+ dueDate?: string;
58
+
59
+ @IsOptional()
60
+ @Transform(({ value }) =>
61
+ value === '' || value === undefined || value === null ? undefined : Number(value)
62
+ )
63
+ @IsNumber({}, { message: 'estimateHours must be a number' })
64
+ estimateHours?: number;
65
+
66
+ @IsOptional()
67
+ @Transform(({ value }) =>
68
+ value === '' || value === undefined || value === null ? undefined : Number(value)
69
+ )
70
+ @IsInt({ message: 'position must be an integer' })
71
+ position?: number;
72
+
73
+ @IsOptional()
74
+ @IsString({ message: 'tags must be a string' })
75
+ tags?: string;
36
76
  }
@@ -472,6 +472,102 @@ describe('OperationsService quick-entry timesheets', () => {
472
472
  expect((service as any).refreshTimesheetTotal).toHaveBeenCalledWith(tx, 55);
473
473
  });
474
474
 
475
+ it('updates quick entries and moves them to the correct weekly timesheet', async () => {
476
+ const tx = {
477
+ $executeRawUnsafe: jest.fn().mockResolvedValue(undefined),
478
+ $queryRawUnsafe: jest.fn().mockResolvedValue([]),
479
+ };
480
+
481
+ (service as any).prisma.$transaction.mockImplementation(
482
+ async (callback: (client: unknown) => unknown) => callback(tx),
483
+ );
484
+
485
+ jest
486
+ .spyOn(service as any, 'getTimesheetEntryByIdForActor')
487
+ .mockResolvedValueOnce({
488
+ id: 93,
489
+ timesheetId: 55,
490
+ collaboratorId: 7,
491
+ projectId: 11,
492
+ projectAssignmentId: 33,
493
+ taskId: 44,
494
+ status: 'draft',
495
+ weekStartDate: '2026-04-06',
496
+ weekEndDate: '2026-04-12',
497
+ workDate: '2026-04-09',
498
+ hours: 1,
499
+ durationMinutes: 60,
500
+ })
501
+ .mockResolvedValueOnce({
502
+ id: 93,
503
+ timesheetId: 77,
504
+ collaboratorId: 7,
505
+ projectId: 12,
506
+ projectAssignmentId: 34,
507
+ taskId: 45,
508
+ status: 'draft',
509
+ weekStartDate: '2026-04-13',
510
+ weekEndDate: '2026-04-19',
511
+ workDate: '2026-04-14',
512
+ hours: 2,
513
+ durationMinutes: 120,
514
+ taskName: 'Review backlog',
515
+ });
516
+ jest.spyOn(service as any, 'resolveOwnedProjectAssignment').mockResolvedValue({
517
+ id: 34,
518
+ projectId: 12,
519
+ projectName: 'Project Boreal',
520
+ projectCode: 'OPS-12',
521
+ roleLabel: 'Engineer',
522
+ });
523
+ jest.spyOn(service as any, 'getOwnedTaskRecord').mockResolvedValue({
524
+ id: 45,
525
+ name: 'Review backlog',
526
+ projectAssignmentId: 34,
527
+ projectId: 12,
528
+ projectName: 'Project Boreal',
529
+ projectCode: 'OPS-12',
530
+ });
531
+ jest.spyOn(service as any, 'getOrCreateTimesheetForWorkDate').mockResolvedValue(77);
532
+ jest.spyOn(service as any, 'refreshTimesheetTotal').mockResolvedValue(undefined);
533
+ jest.spyOn(service as any, 'cleanupEmptyEditableTimesheet').mockResolvedValue(undefined);
534
+
535
+ const result = await service.updateTimesheetEntry(15, 93, {
536
+ projectId: 12,
537
+ projectAssignmentId: 34,
538
+ taskId: 45,
539
+ workDate: '2026-04-14',
540
+ duration: 2,
541
+ unit: 'hours',
542
+ description: 'Backlog refinement',
543
+ } as any);
544
+
545
+ expect(result).toEqual(
546
+ expect.objectContaining({
547
+ id: 93,
548
+ timesheetId: 77,
549
+ }),
550
+ );
551
+ expect(tx.$executeRawUnsafe).toHaveBeenCalledWith(
552
+ expect.stringContaining('UPDATE operations_timesheet_entry'),
553
+ 77,
554
+ 34,
555
+ 45,
556
+ 'Review backlog',
557
+ '2026-04-14',
558
+ 120,
559
+ 2,
560
+ 'Backlog refinement',
561
+ 93,
562
+ );
563
+ expect((service as any).refreshTimesheetTotal).toHaveBeenCalledWith(tx, 55);
564
+ expect((service as any).refreshTimesheetTotal).toHaveBeenCalledWith(tx, 77);
565
+ expect((service as any).cleanupEmptyEditableTimesheet).toHaveBeenCalledWith(
566
+ tx,
567
+ 55,
568
+ );
569
+ });
570
+
475
571
  it('rejects deleting entries from submitted timesheets', async () => {
476
572
  jest.spyOn(service as any, 'getTimesheetEntryByIdForActor').mockResolvedValue({
477
573
  id: 92,