@hed-hog/operations 0.0.303 → 0.0.305

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 (178) hide show
  1. package/README.md +200 -43
  2. package/dist/controllers/operations-approvals.controller.d.ts +9 -0
  3. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -0
  4. package/dist/controllers/operations-approvals.controller.js +64 -0
  5. package/dist/controllers/operations-approvals.controller.js.map +1 -0
  6. package/dist/controllers/operations-collaborators.controller.d.ts +223 -0
  7. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -0
  8. package/dist/controllers/operations-collaborators.controller.js +96 -0
  9. package/dist/controllers/operations-collaborators.controller.js.map +1 -0
  10. package/dist/controllers/operations-contracts.controller.d.ts +683 -0
  11. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -0
  12. package/dist/controllers/operations-contracts.controller.js +198 -0
  13. package/dist/controllers/operations-contracts.controller.js.map +1 -0
  14. package/dist/controllers/operations-org-structure.controller.d.ts +108 -0
  15. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -0
  16. package/dist/controllers/operations-org-structure.controller.js +143 -0
  17. package/dist/controllers/operations-org-structure.controller.js.map +1 -0
  18. package/dist/controllers/operations-projects.controller.d.ts +184 -0
  19. package/dist/controllers/operations-projects.controller.d.ts.map +1 -0
  20. package/dist/controllers/operations-projects.controller.js +87 -0
  21. package/dist/controllers/operations-projects.controller.js.map +1 -0
  22. package/dist/controllers/operations-tasks.controller.d.ts +85 -0
  23. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -0
  24. package/dist/controllers/operations-tasks.controller.js +90 -0
  25. package/dist/controllers/operations-tasks.controller.js.map +1 -0
  26. package/dist/controllers/operations-timesheets.controller.d.ts +99 -0
  27. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -0
  28. package/dist/controllers/operations-timesheets.controller.js +154 -0
  29. package/dist/controllers/operations-timesheets.controller.js.map +1 -0
  30. package/dist/dto/create-collaborator-type.dto.d.ts +10 -0
  31. package/dist/dto/create-collaborator-type.dto.d.ts.map +1 -0
  32. package/dist/dto/create-collaborator-type.dto.js +56 -0
  33. package/dist/dto/create-collaborator-type.dto.js.map +1 -0
  34. package/dist/dto/create-collaborator.dto.d.ts +42 -0
  35. package/dist/dto/create-collaborator.dto.d.ts.map +1 -0
  36. package/dist/dto/create-collaborator.dto.js +228 -0
  37. package/dist/dto/create-collaborator.dto.js.map +1 -0
  38. package/dist/dto/create-schedule-adjustment-request.dto.d.ts +17 -0
  39. package/dist/dto/create-schedule-adjustment-request.dto.d.ts.map +1 -0
  40. package/dist/dto/create-schedule-adjustment-request.dto.js +89 -0
  41. package/dist/dto/create-schedule-adjustment-request.dto.js.map +1 -0
  42. package/dist/dto/create-task.dto.d.ts +14 -0
  43. package/dist/dto/create-task.dto.d.ts.map +1 -0
  44. package/dist/dto/create-task.dto.js +83 -0
  45. package/dist/dto/create-task.dto.js.map +1 -0
  46. package/dist/dto/create-time-off-request.dto.d.ts +9 -0
  47. package/dist/dto/create-time-off-request.dto.d.ts.map +1 -0
  48. package/dist/dto/create-time-off-request.dto.js +54 -0
  49. package/dist/dto/create-time-off-request.dto.js.map +1 -0
  50. package/dist/dto/create-timesheet-entry.dto.d.ts +12 -0
  51. package/dist/dto/create-timesheet-entry.dto.d.ts.map +1 -0
  52. package/dist/dto/create-timesheet-entry.dto.js +75 -0
  53. package/dist/dto/create-timesheet-entry.dto.js.map +1 -0
  54. package/dist/dto/list-collaborator-types.dto.d.ts +4 -0
  55. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -0
  56. package/dist/dto/list-collaborator-types.dto.js +29 -0
  57. package/dist/dto/list-collaborator-types.dto.js.map +1 -0
  58. package/dist/dto/list-collaborators.dto.d.ts +8 -0
  59. package/dist/dto/list-collaborators.dto.d.ts.map +1 -0
  60. package/dist/dto/list-collaborators.dto.js +42 -0
  61. package/dist/dto/list-collaborators.dto.js.map +1 -0
  62. package/dist/dto/list-project-options.dto.d.ts +4 -0
  63. package/dist/dto/list-project-options.dto.d.ts.map +1 -0
  64. package/dist/dto/list-project-options.dto.js +8 -0
  65. package/dist/dto/list-project-options.dto.js.map +1 -0
  66. package/dist/dto/list-tasks.dto.d.ts +7 -0
  67. package/dist/dto/list-tasks.dto.d.ts.map +1 -0
  68. package/dist/dto/list-tasks.dto.js +38 -0
  69. package/dist/dto/list-tasks.dto.js.map +1 -0
  70. package/dist/dto/list-timesheet-entries.dto.d.ts +10 -0
  71. package/dist/dto/list-timesheet-entries.dto.d.ts.map +1 -0
  72. package/dist/dto/list-timesheet-entries.dto.js +54 -0
  73. package/dist/dto/list-timesheet-entries.dto.js.map +1 -0
  74. package/dist/dto/update-collaborator-type.dto.d.ts +4 -0
  75. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -0
  76. package/dist/dto/update-collaborator-type.dto.js +8 -0
  77. package/dist/dto/update-collaborator-type.dto.js.map +1 -0
  78. package/dist/dto/update-collaborator.dto.d.ts +4 -0
  79. package/dist/dto/update-collaborator.dto.d.ts.map +1 -0
  80. package/dist/dto/update-collaborator.dto.js +8 -0
  81. package/dist/dto/update-collaborator.dto.js.map +1 -0
  82. package/dist/dto/update-task.dto.d.ts +14 -0
  83. package/dist/dto/update-task.dto.d.ts.map +1 -0
  84. package/dist/dto/update-task.dto.js +84 -0
  85. package/dist/dto/update-task.dto.js.map +1 -0
  86. package/dist/operations.controller.d.ts +0 -1045
  87. package/dist/operations.controller.d.ts.map +1 -1
  88. package/dist/operations.controller.js +0 -429
  89. package/dist/operations.controller.js.map +1 -1
  90. package/dist/operations.module.d.ts.map +1 -1
  91. package/dist/operations.module.js +23 -2
  92. package/dist/operations.module.js.map +1 -1
  93. package/dist/operations.service.d.ts +429 -8
  94. package/dist/operations.service.d.ts.map +1 -1
  95. package/dist/operations.service.js +1931 -165
  96. package/dist/operations.service.js.map +1 -1
  97. package/dist/operations.service.spec.js +315 -1
  98. package/dist/operations.service.spec.js.map +1 -1
  99. package/dist/services/shared/operations-access.service.d.ts +16 -0
  100. package/dist/services/shared/operations-access.service.d.ts.map +1 -0
  101. package/dist/services/shared/operations-access.service.js +48 -0
  102. package/dist/services/shared/operations-access.service.js.map +1 -0
  103. package/hedhog/data/dashboard.yaml +20 -0
  104. package/hedhog/data/dashboard_component.yaml +274 -0
  105. package/hedhog/data/dashboard_component_role.yaml +174 -0
  106. package/hedhog/data/dashboard_item.yaml +299 -0
  107. package/hedhog/data/dashboard_role.yaml +20 -0
  108. package/hedhog/data/menu.yaml +30 -13
  109. package/hedhog/data/operations_collaborator_type.yaml +76 -0
  110. package/hedhog/data/route.yaml +196 -0
  111. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +231 -0
  112. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +125 -40
  113. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +740 -106
  114. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +256 -256
  115. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +7 -7
  116. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +306 -306
  117. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +247 -247
  118. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +3520 -3520
  119. package/hedhog/frontend/app/_components/department-select-with-create.tsx.ejs +38 -16
  120. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +1504 -52
  121. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +1017 -649
  122. package/hedhog/frontend/app/_components/section-card.tsx.ejs +25 -18
  123. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +609 -0
  124. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +213 -0
  125. package/hedhog/frontend/app/_lib/api.ts.ejs +30 -1
  126. package/hedhog/frontend/app/_lib/types.ts.ejs +147 -39
  127. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +40 -9
  128. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +48 -1
  129. package/hedhog/frontend/app/approvals/page.tsx.ejs +116 -98
  130. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -0
  131. package/hedhog/frontend/app/collaborators/page.tsx.ejs +116 -72
  132. package/hedhog/frontend/app/contracts/page.tsx.ejs +938 -938
  133. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +11 -9
  134. package/hedhog/frontend/app/departments/page.tsx.ejs +1 -1
  135. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +1 -1
  136. package/hedhog/frontend/app/projects/page.tsx.ejs +364 -133
  137. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +244 -120
  138. package/hedhog/frontend/app/team/page.tsx.ejs +15 -2
  139. package/hedhog/frontend/app/time-off/page.tsx.ejs +158 -82
  140. package/hedhog/frontend/app/timesheets/page.tsx.ejs +814 -357
  141. package/hedhog/frontend/messages/en.json +268 -53
  142. package/hedhog/frontend/messages/pt.json +484 -271
  143. package/hedhog/table/operations_collaborator.yaml +26 -13
  144. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -0
  145. package/hedhog/table/operations_collaborator_type.yaml +33 -0
  146. package/hedhog/table/operations_job_title.yaml +24 -0
  147. package/hedhog/table/operations_project.yaml +9 -0
  148. package/hedhog/table/operations_project_assignment.yaml +9 -0
  149. package/hedhog/table/operations_project_role.yaml +39 -0
  150. package/hedhog/table/operations_task.yaml +69 -0
  151. package/hedhog/table/operations_timesheet_entry.yaml +12 -0
  152. package/package.json +6 -6
  153. package/src/controllers/operations-approvals.controller.ts +24 -0
  154. package/src/controllers/operations-collaborators.controller.ts +60 -0
  155. package/src/controllers/operations-contracts.controller.ts +138 -0
  156. package/src/controllers/operations-org-structure.controller.ts +92 -0
  157. package/src/controllers/operations-projects.controller.ts +50 -0
  158. package/src/controllers/operations-tasks.controller.ts +63 -0
  159. package/src/controllers/operations-timesheets.controller.ts +100 -0
  160. package/src/dto/create-collaborator-type.dto.ts +43 -0
  161. package/src/dto/create-collaborator.dto.ts +223 -0
  162. package/src/dto/create-schedule-adjustment-request.dto.ts +91 -0
  163. package/src/dto/create-task.dto.ts +75 -0
  164. package/src/dto/create-time-off-request.dto.ts +53 -0
  165. package/src/dto/create-timesheet-entry.dto.ts +67 -0
  166. package/src/dto/list-collaborator-types.dto.ts +15 -0
  167. package/src/dto/list-collaborators.dto.ts +30 -0
  168. package/src/dto/list-project-options.dto.ts +3 -0
  169. package/src/dto/list-tasks.dto.ts +25 -0
  170. package/src/dto/list-timesheet-entries.dto.ts +40 -0
  171. package/src/dto/update-collaborator-type.dto.ts +3 -0
  172. package/src/dto/update-collaborator.dto.ts +3 -0
  173. package/src/dto/update-task.dto.ts +76 -0
  174. package/src/operations.controller.ts +1 -278
  175. package/src/operations.module.ts +23 -2
  176. package/src/operations.service.spec.ts +450 -0
  177. package/src/operations.service.ts +4507 -1561
  178. package/src/services/shared/operations-access.service.ts +52 -0
@@ -0,0 +1,213 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Form,
5
+ FormControl,
6
+ FormField,
7
+ FormItem,
8
+ FormLabel,
9
+ FormMessage,
10
+ } from '@/components/ui/form';
11
+ import { FormActions } from '@/components/ui/form-actions';
12
+ import { Input } from '@/components/ui/input';
13
+ import {
14
+ Sheet,
15
+ SheetContent,
16
+ SheetDescription,
17
+ SheetHeader,
18
+ SheetTitle,
19
+ } from '@/components/ui/sheet';
20
+ import { Textarea } from '@/components/ui/textarea';
21
+ import { useApp } from '@hed-hog/next-app-provider';
22
+ import { zodResolver } from '@hookform/resolvers/zod';
23
+ import { Layers3 } from 'lucide-react';
24
+ import { useTranslations } from 'next-intl';
25
+ import { useEffect, useMemo } from 'react';
26
+ import { useForm } from 'react-hook-form';
27
+ import { z } from 'zod';
28
+
29
+ import { getOperationsErrorMessage, mutateOperations } from '../_lib/api';
30
+ import type {
31
+ OperationsProjectOption,
32
+ OperationsTaskOption,
33
+ } from '../_lib/types';
34
+ import { trimToNull } from '../_lib/utils/forms';
35
+
36
+ type TimesheetTaskCreateSheetProps = {
37
+ open: boolean;
38
+ onOpenChange: (open: boolean) => void;
39
+ project: OperationsProjectOption | null;
40
+ onCreated: (task: OperationsTaskOption) => void;
41
+ };
42
+
43
+ type TaskFormValues = {
44
+ name: string;
45
+ description: string;
46
+ };
47
+
48
+ export function TimesheetTaskCreateSheet({
49
+ open,
50
+ onOpenChange,
51
+ project,
52
+ onCreated,
53
+ }: TimesheetTaskCreateSheetProps) {
54
+ const t = useTranslations('operations.TimesheetsPage');
55
+ const commonT = useTranslations('operations.Common');
56
+ const { request, showToastHandler } = useApp();
57
+
58
+ const taskSchema = useMemo(
59
+ () =>
60
+ z.object({
61
+ name: z
62
+ .string()
63
+ .trim()
64
+ .min(2, t('sheet.task.validation.nameMinLength')),
65
+ description: z
66
+ .string()
67
+ .trim()
68
+ .max(500, t('sheet.task.validation.descriptionMaxLength'))
69
+ .optional()
70
+ .or(z.literal('')),
71
+ }),
72
+ [t]
73
+ );
74
+
75
+ const form = useForm<TaskFormValues>({
76
+ resolver: zodResolver(taskSchema),
77
+ defaultValues: {
78
+ name: '',
79
+ description: '',
80
+ },
81
+ });
82
+
83
+ useEffect(() => {
84
+ if (!open) {
85
+ form.reset({
86
+ name: '',
87
+ description: '',
88
+ });
89
+ }
90
+ }, [form, open]);
91
+
92
+ const handleSubmit = async (values: TaskFormValues) => {
93
+ if (!project?.id) {
94
+ showToastHandler?.('error', t('messages.selectProjectFirst'));
95
+ return;
96
+ }
97
+
98
+ try {
99
+ const createdTask = await mutateOperations<OperationsTaskOption>(
100
+ request,
101
+ '/operations/tasks',
102
+ 'POST',
103
+ {
104
+ projectId: project.id,
105
+ name: values.name.trim(),
106
+ description: trimToNull(values.description),
107
+ }
108
+ );
109
+
110
+ onCreated(createdTask);
111
+ form.reset({
112
+ name: '',
113
+ description: '',
114
+ });
115
+ onOpenChange(false);
116
+ showToastHandler?.('success', t('messages.taskCreateSuccess'));
117
+ } catch (error) {
118
+ showToastHandler?.(
119
+ 'error',
120
+ getOperationsErrorMessage(error, t('messages.taskCreateError'))
121
+ );
122
+ }
123
+ };
124
+
125
+ const handleOpenChange = (nextOpen: boolean) => {
126
+ if (form.formState.isSubmitting && !nextOpen) {
127
+ return;
128
+ }
129
+
130
+ onOpenChange(nextOpen);
131
+ };
132
+
133
+ return (
134
+ <Sheet open={open} onOpenChange={handleOpenChange}>
135
+ <SheetContent className="flex h-full w-full flex-col gap-0 overflow-hidden p-0 sm:max-w-lg">
136
+ <SheetHeader className="border-b px-4 py-4 text-left">
137
+ <div className="flex items-start gap-3">
138
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-violet-500/10 text-violet-600">
139
+ <Layers3 className="size-5" />
140
+ </div>
141
+
142
+ <div className="space-y-1">
143
+ <SheetTitle>{t('sheet.task.title')}</SheetTitle>
144
+ <SheetDescription>{t('sheet.task.description')}</SheetDescription>
145
+ </div>
146
+ </div>
147
+ </SheetHeader>
148
+
149
+ <Form {...form}>
150
+ <form
151
+ onSubmit={form.handleSubmit(handleSubmit)}
152
+ className="flex min-h-0 flex-1 flex-col"
153
+ >
154
+ <div className="flex-1 space-y-4 overflow-y-auto px-4 py-4">
155
+ <div className="rounded-lg border bg-muted/30 p-3">
156
+ <div className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
157
+ {t('sheet.task.projectLabel')}
158
+ </div>
159
+ <div className="mt-1 text-sm font-medium text-foreground">
160
+ {project?.label ?? commonT('labels.notAssigned')}
161
+ </div>
162
+ </div>
163
+
164
+ <FormField
165
+ control={form.control}
166
+ name="name"
167
+ render={({ field }) => (
168
+ <FormItem>
169
+ <FormLabel>{t('sheet.task.name')}</FormLabel>
170
+ <FormControl>
171
+ <Input
172
+ {...field}
173
+ placeholder={t('sheet.task.placeholders.name')}
174
+ />
175
+ </FormControl>
176
+ <FormMessage />
177
+ </FormItem>
178
+ )}
179
+ />
180
+
181
+ <FormField
182
+ control={form.control}
183
+ name="description"
184
+ render={({ field }) => (
185
+ <FormItem>
186
+ <FormLabel>{t('sheet.task.descriptionLabel')}</FormLabel>
187
+ <FormControl>
188
+ <Textarea
189
+ {...field}
190
+ rows={4}
191
+ placeholder={t('sheet.task.placeholders.description')}
192
+ />
193
+ </FormControl>
194
+ <FormMessage />
195
+ </FormItem>
196
+ )}
197
+ />
198
+ </div>
199
+
200
+ <FormActions
201
+ sheet
202
+ onCancel={() => onOpenChange(false)}
203
+ cancelLabel={commonT('actions.cancel')}
204
+ submitType="submit"
205
+ submitLabel={t('sheet.task.submit')}
206
+ submitDisabled={form.formState.isSubmitting || !project}
207
+ />
208
+ </form>
209
+ </Form>
210
+ </SheetContent>
211
+ </Sheet>
212
+ );
213
+ }
@@ -4,6 +4,35 @@ type RequestFn = (input: {
4
4
  data?: unknown;
5
5
  }) => Promise<{ data: unknown }>;
6
6
 
7
+ export function getOperationsErrorMessage(error: unknown, fallback: string) {
8
+ if (error && typeof error === 'object') {
9
+ const requestError = error as {
10
+ message?: string;
11
+ response?: {
12
+ data?: {
13
+ message?: string | string[];
14
+ };
15
+ };
16
+ };
17
+
18
+ const message =
19
+ requestError.response?.data?.message ?? requestError.message;
20
+
21
+ if (Array.isArray(message)) {
22
+ const combined = message.filter(Boolean).join(' ');
23
+ if (combined.trim()) {
24
+ return combined;
25
+ }
26
+ }
27
+
28
+ if (typeof message === 'string' && message.trim()) {
29
+ return message;
30
+ }
31
+ }
32
+
33
+ return fallback;
34
+ }
35
+
7
36
  export async function fetchOperations<T>(request: RequestFn, url: string) {
8
37
  const response = await request({
9
38
  url,
@@ -16,7 +45,7 @@ export async function fetchOperations<T>(request: RequestFn, url: string) {
16
45
  export async function mutateOperations<T>(
17
46
  request: RequestFn,
18
47
  url: string,
19
- method: 'POST' | 'PATCH',
48
+ method: 'POST' | 'PATCH' | 'DELETE',
20
49
  data?: unknown
21
50
  ) {
22
51
  const response = await request({
@@ -1,3 +1,13 @@
1
+ export type PaginatedResponse<T> = {
2
+ total: number;
3
+ lastPage: number;
4
+ page: number;
5
+ pageSize: number;
6
+ prev?: number | null;
7
+ next?: number | null;
8
+ data: T[];
9
+ };
10
+
1
11
  export type OperationsDashboard = {
2
12
  actor: {
3
13
  roleScope: 'self' | 'team' | 'full';
@@ -31,13 +41,18 @@ export type OperationsCollaborator = {
31
41
  personName?: string | null;
32
42
  personAvatarId?: number | null;
33
43
  code: string;
34
- collaboratorType?: string;
44
+ collaboratorTypeId?: number | null;
45
+ collaboratorTypeSlug?: string | null;
46
+ collaboratorType?: string | null;
47
+ collaboratorTypeName?: string | null;
35
48
  displayName: string;
36
49
  departmentId?: number | null;
37
50
  department?: string | null;
51
+ jobTitleId?: number | null;
38
52
  title?: string | null;
39
53
  levelLabel?: string | null;
40
54
  weeklyCapacityHours?: number | null;
55
+ compensationAmount?: number | null;
41
56
  status: string;
42
57
  joinedAt?: string | null;
43
58
  leftAt?: string | null;
@@ -64,10 +79,76 @@ export type OperationsDepartment = {
64
79
  updatedAt?: string | null;
65
80
  };
66
81
 
82
+ export type OperationsJobTitle = {
83
+ id: number;
84
+ slug: string;
85
+ code?: string | null;
86
+ name: string;
87
+ description?: string | null;
88
+ status: 'active' | 'inactive';
89
+ collaboratorCount?: number;
90
+ createdAt?: string | null;
91
+ updatedAt?: string | null;
92
+ };
93
+
94
+ export type OperationsCollaboratorType = {
95
+ id: number;
96
+ slug: string;
97
+ name: string;
98
+ description?: string | null;
99
+ category?: string | null;
100
+ isActive?: boolean;
101
+ sortOrder?: number | null;
102
+ status: 'active' | 'inactive';
103
+ collaboratorCount?: number;
104
+ createdAt?: string | null;
105
+ updatedAt?: string | null;
106
+ };
107
+
108
+ export type OperationsProjectRole = {
109
+ id: number;
110
+ slug: string;
111
+ code?: string | null;
112
+ name: string;
113
+ description?: string | null;
114
+ isActive?: boolean;
115
+ sortOrder?: number;
116
+ createdAt?: string | null;
117
+ updatedAt?: string | null;
118
+ };
119
+
120
+ export type OperationsProjectOption = {
121
+ id: number;
122
+ label: string;
123
+ name: string;
124
+ code?: string | null;
125
+ clientName?: string | null;
126
+ projectAssignmentId?: number | null;
127
+ roleLabel?: string | null;
128
+ status: string;
129
+ startDate?: string | null;
130
+ endDate?: string | null;
131
+ };
132
+
133
+ export type OperationsTaskOption = {
134
+ id: number;
135
+ label: string;
136
+ name: string;
137
+ description?: string | null;
138
+ status: string;
139
+ projectId: number;
140
+ projectAssignmentId: number;
141
+ projectName: string;
142
+ projectCode?: string | null;
143
+ createdAt?: string | null;
144
+ };
145
+
67
146
  export type OperationsProject = {
68
147
  id: number;
69
148
  contractId?: number | null;
70
149
  managerCollaboratorId?: number | null;
150
+ clientPersonId?: number | null;
151
+ clientAvatarId?: number | null;
71
152
  code: string;
72
153
  name: string;
73
154
  clientName?: string | null;
@@ -87,16 +168,16 @@ export type OperationsProject = {
87
168
  teamSize?: number;
88
169
  };
89
170
 
90
- export type OperationsContract = {
91
- id: number;
92
- code: string;
93
- name?: string | null;
94
- contractCategory?: string;
95
- contractType?: string;
96
- clientName?: string | null;
97
- signatureStatus?: string;
98
- isActive?: boolean;
99
- billingModel: string;
171
+ export type OperationsContract = {
172
+ id: number;
173
+ code: string;
174
+ name?: string | null;
175
+ contractCategory?: string;
176
+ contractType?: string;
177
+ clientName?: string | null;
178
+ signatureStatus?: string;
179
+ isActive?: boolean;
180
+ billingModel: string;
100
181
  accountManagerCollaboratorId?: number | null;
101
182
  accountManagerName?: string | null;
102
183
  relatedCollaboratorId?: number | null;
@@ -104,26 +185,26 @@ export type OperationsContract = {
104
185
  contractTemplateId?: number | null;
105
186
  contractTemplateName?: string | null;
106
187
  contractTemplateSlug?: string | null;
107
- contractTemplateCode?: string | null;
108
- mainRelatedPartyName?: string | null;
109
- originType?: string | null;
110
- originId?: number | null;
111
- startDate?: string | null;
112
- endDate?: string | null;
113
- signedAt?: string | null;
114
- effectiveDate?: string | null;
188
+ contractTemplateCode?: string | null;
189
+ mainRelatedPartyName?: string | null;
190
+ originType?: string | null;
191
+ originId?: number | null;
192
+ startDate?: string | null;
193
+ endDate?: string | null;
194
+ signedAt?: string | null;
195
+ effectiveDate?: string | null;
115
196
  budgetAmount?: number | null;
116
197
  monthlyHourCap?: number | null;
117
198
  valueAmount?: number | null;
118
199
  paymentAmount?: number | null;
119
200
  revenueAmount?: number | null;
120
- fineAmount?: number | null;
121
- status: string;
122
- creationMode?: 'blank' | 'template' | 'upload' | 'duplicate' | null;
123
- wizardStep?: number | null;
124
- description?: string | null;
125
- contentHtml?: string | null;
126
- projectCount?: number;
201
+ fineAmount?: number | null;
202
+ status: string;
203
+ creationMode?: 'blank' | 'template' | 'upload' | 'duplicate' | null;
204
+ wizardStep?: number | null;
205
+ description?: string | null;
206
+ contentHtml?: string | null;
207
+ projectCount?: number;
127
208
  currentPdfFileName?: string | null;
128
209
  scheduleSummary?: OperationsWeeklyScheduleDay[];
129
210
  };
@@ -154,14 +235,35 @@ export type OperationsWeeklyScheduleDay = {
154
235
  breakMinutes?: number | null;
155
236
  };
156
237
 
238
+ export type OperationsCollaboratorEquityParticipation = {
239
+ participationType: string;
240
+ percentage?: number | null;
241
+ votingPower?: number | null;
242
+ startDate?: string | null;
243
+ endDate?: string | null;
244
+ notes?: string | null;
245
+ };
246
+
157
247
  export type OperationsTimesheetEntry = {
158
248
  id: number;
249
+ timesheetId?: number | null;
250
+ collaboratorId?: number | null;
251
+ collaboratorName?: string | null;
159
252
  projectAssignmentId?: number | null;
160
253
  projectId?: number | null;
254
+ projectCode?: string | null;
161
255
  projectName?: string | null;
162
256
  roleLabel?: string | null;
257
+ taskId?: number | null;
258
+ taskName?: string | null;
163
259
  activityLabel?: string | null;
260
+ label?: string | null;
261
+ status?: string | null;
262
+ weekStartDate?: string | null;
263
+ weekEndDate?: string | null;
264
+ createdAt?: string | null;
164
265
  workDate: string;
266
+ durationMinutes?: number | null;
165
267
  hours: number;
166
268
  description?: string | null;
167
269
  };
@@ -282,6 +384,7 @@ export type OperationsCollaboratorDetails = OperationsCollaborator & {
282
384
  code: string;
283
385
  name: string;
284
386
  status: string;
387
+ projectRoleId?: number | null;
285
388
  roleLabel?: string | null;
286
389
  allocationPercent?: number | null;
287
390
  weeklyHours?: number | null;
@@ -290,6 +393,7 @@ export type OperationsCollaboratorDetails = OperationsCollaborator & {
290
393
  }>;
291
394
  relatedContracts: OperationsContract[];
292
395
  weeklySchedule: OperationsWeeklyScheduleDay[];
396
+ equityParticipation?: OperationsCollaboratorEquityParticipation | null;
293
397
  timesheetSummary: {
294
398
  totalTimesheets: number;
295
399
  pendingTimesheets: number;
@@ -314,7 +418,11 @@ export type OperationsProjectDetails = OperationsProject & {
314
418
  assignments: Array<{
315
419
  id: number;
316
420
  collaboratorId: number;
421
+ userId?: number | null;
422
+ personAvatarId?: number | null;
423
+ userPhotoId?: number | null;
317
424
  collaboratorName: string;
425
+ projectRoleId?: number | null;
318
426
  roleLabel?: string | null;
319
427
  allocationPercent?: number | null;
320
428
  weeklyHours?: number | null;
@@ -371,19 +479,19 @@ export type OperationsContractDetails = OperationsContract & {
371
479
  dueDay?: number | null;
372
480
  notes?: string | null;
373
481
  }>;
374
- documents: Array<{
375
- id: number;
376
- documentType: string;
377
- fileId?: number | null;
378
- fileName: string;
379
- mimeType: string;
380
- fileContentBase64?: string | null;
381
- isCurrent: boolean;
382
- extractionStatus?: string | null;
383
- extractionSummary?: string | null;
384
- notes?: string | null;
385
- createdAt: string;
386
- }>;
482
+ documents: Array<{
483
+ id: number;
484
+ documentType: string;
485
+ fileId?: number | null;
486
+ fileName: string;
487
+ mimeType: string;
488
+ fileContentBase64?: string | null;
489
+ isCurrent: boolean;
490
+ extractionStatus?: string | null;
491
+ extractionSummary?: string | null;
492
+ notes?: string | null;
493
+ createdAt: string;
494
+ }>;
387
495
  revisions: Array<{
388
496
  id: number;
389
497
  revisionType: string;
@@ -20,7 +20,7 @@ function resolveLocale(currentLocaleCode: string) {
20
20
  export function formatDate(
21
21
  value?: string | null,
22
22
  getSettingValue?: ((key: string) => unknown) | null,
23
- currentLocaleCode = 'en-US'
23
+ currentLocaleCode = 'pt-BR'
24
24
  ) {
25
25
  if (!value) {
26
26
  return '-';
@@ -47,8 +47,8 @@ export function formatDate(
47
47
  }
48
48
 
49
49
  return parsedDate.toLocaleDateString(resolveLocale(currentLocaleCode), {
50
- month: 'short',
51
- day: 'numeric',
50
+ day: '2-digit',
51
+ month: '2-digit',
52
52
  year: 'numeric',
53
53
  });
54
54
  }
@@ -56,7 +56,7 @@ export function formatDate(
56
56
  export function formatDateTime(
57
57
  value?: string | null,
58
58
  getSettingValue?: ((key: string) => unknown) | null,
59
- currentLocaleCode = 'en-US'
59
+ currentLocaleCode = 'pt-BR'
60
60
  ) {
61
61
  if (!value) {
62
62
  return '-';
@@ -81,8 +81,8 @@ export function formatDateTime(
81
81
  }
82
82
 
83
83
  return parsedDate.toLocaleString(resolveLocale(currentLocaleCode), {
84
- month: 'short',
85
- day: 'numeric',
84
+ day: '2-digit',
85
+ month: '2-digit',
86
86
  year: 'numeric',
87
87
  hour: '2-digit',
88
88
  minute: '2-digit',
@@ -135,11 +135,41 @@ export function formatEnumLabel(value?: string | null) {
135
135
  .replace(/\b\w/g, (match) => match.toUpperCase());
136
136
  }
137
137
 
138
+ const weekdayLabels: Record<
139
+ string,
140
+ {
141
+ 'pt-BR': string;
142
+ 'en-US': string;
143
+ }
144
+ > = {
145
+ monday: { 'pt-BR': 'Segunda-feira', 'en-US': 'Monday' },
146
+ tuesday: { 'pt-BR': 'Terça-feira', 'en-US': 'Tuesday' },
147
+ wednesday: { 'pt-BR': 'Quarta-feira', 'en-US': 'Wednesday' },
148
+ thursday: { 'pt-BR': 'Quinta-feira', 'en-US': 'Thursday' },
149
+ friday: { 'pt-BR': 'Sexta-feira', 'en-US': 'Friday' },
150
+ saturday: { 'pt-BR': 'Sábado', 'en-US': 'Saturday' },
151
+ sunday: { 'pt-BR': 'Domingo', 'en-US': 'Sunday' },
152
+ };
153
+
154
+ export function formatWeekdayLabel(
155
+ value?: string | null,
156
+ currentLocaleCode = 'en-US'
157
+ ) {
158
+ if (!value) {
159
+ return '-';
160
+ }
161
+
162
+ const locale = resolveLocale(currentLocaleCode) as 'pt-BR' | 'en-US';
163
+ const normalizedValue = String(value).trim().toLowerCase();
164
+
165
+ return weekdayLabels[normalizedValue]?.[locale] ?? formatEnumLabel(value);
166
+ }
167
+
138
168
  export function formatDateRange(
139
169
  startDate?: string | null,
140
170
  endDate?: string | null,
141
171
  getSettingValue?: ((key: string) => unknown) | null,
142
- currentLocaleCode = 'en-US'
172
+ currentLocaleCode = 'pt-BR'
143
173
  ) {
144
174
  if (!startDate && !endDate) {
145
175
  return '-';
@@ -186,7 +216,8 @@ export function summarizeScheduleDays(
186
216
  isWorkingDay: boolean;
187
217
  startTime?: string | null;
188
218
  endTime?: string | null;
189
- }>
219
+ }>,
220
+ currentLocaleCode = 'en-US'
190
221
  ) {
191
222
  if (!days.length) {
192
223
  return '-';
@@ -195,7 +226,7 @@ export function summarizeScheduleDays(
195
226
  return days
196
227
  .filter((day) => day.isWorkingDay)
197
228
  .map((day) => {
198
- const label = formatEnumLabel(day.weekday);
229
+ const label = formatWeekdayLabel(day.weekday, currentLocaleCode);
199
230
  if (day.startTime && day.endTime) {
200
231
  return `${label}: ${formatTimeValue(day.startTime)}-${formatTimeValue(day.endTime)}`;
201
232
  }
@@ -4,10 +4,57 @@ export function parseNumberInput(value: string) {
4
4
  return null;
5
5
  }
6
6
 
7
- const parsed = Number(trimmed);
7
+ const normalized = /[.,]/.test(trimmed)
8
+ ? trimmed.replace(/\./g, '').replace(',', '.')
9
+ : trimmed;
10
+
11
+ const parsed = Number(normalized);
8
12
  return Number.isFinite(parsed) ? parsed : null;
9
13
  }
10
14
 
15
+ export function normalizePercentInput(value: string) {
16
+ const trimmed = value.trim();
17
+
18
+ if (!trimmed) {
19
+ return '';
20
+ }
21
+
22
+ const withNormalizedSeparator = trimmed.replace(/,/g, '.');
23
+ const digitsAndSeparatorsOnly = withNormalizedSeparator.replace(
24
+ /[^0-9.]/g,
25
+ ''
26
+ );
27
+
28
+ if (!digitsAndSeparatorsOnly) {
29
+ return '';
30
+ }
31
+
32
+ const separatorIndex = digitsAndSeparatorsOnly.indexOf('.');
33
+ const normalized =
34
+ separatorIndex === -1
35
+ ? digitsAndSeparatorsOnly
36
+ : `${digitsAndSeparatorsOnly.slice(0, separatorIndex + 1)}${digitsAndSeparatorsOnly
37
+ .slice(separatorIndex + 1)
38
+ .replace(/\./g, '')}`;
39
+
40
+ const prefixedValue = normalized.startsWith('.') ? `0${normalized}` : normalized;
41
+ const parsed = Number(prefixedValue);
42
+
43
+ if (!Number.isFinite(parsed)) {
44
+ return '';
45
+ }
46
+
47
+ if (parsed <= 0) {
48
+ return prefixedValue === '0.' ? prefixedValue : '0';
49
+ }
50
+
51
+ if (parsed >= 100) {
52
+ return '100';
53
+ }
54
+
55
+ return prefixedValue;
56
+ }
57
+
11
58
  export function trimToNull(value?: string | null) {
12
59
  const trimmed = value?.trim() ?? '';
13
60
  return trimmed ? trimmed : null;