@hed-hog/operations 0.0.303 → 0.0.304
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.
- package/README.md +200 -43
- package/dist/controllers/operations-approvals.controller.d.ts +9 -0
- package/dist/controllers/operations-approvals.controller.d.ts.map +1 -0
- package/dist/controllers/operations-approvals.controller.js +64 -0
- package/dist/controllers/operations-approvals.controller.js.map +1 -0
- package/dist/controllers/operations-collaborators.controller.d.ts +223 -0
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -0
- package/dist/controllers/operations-collaborators.controller.js +96 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -0
- package/dist/controllers/operations-contracts.controller.d.ts +683 -0
- package/dist/controllers/operations-contracts.controller.d.ts.map +1 -0
- package/dist/controllers/operations-contracts.controller.js +198 -0
- package/dist/controllers/operations-contracts.controller.js.map +1 -0
- package/dist/controllers/operations-org-structure.controller.d.ts +108 -0
- package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -0
- package/dist/controllers/operations-org-structure.controller.js +143 -0
- package/dist/controllers/operations-org-structure.controller.js.map +1 -0
- package/dist/controllers/operations-projects.controller.d.ts +169 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -0
- package/dist/controllers/operations-projects.controller.js +87 -0
- package/dist/controllers/operations-projects.controller.js.map +1 -0
- package/dist/controllers/operations-tasks.controller.d.ts +54 -0
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -0
- package/dist/controllers/operations-tasks.controller.js +79 -0
- package/dist/controllers/operations-tasks.controller.js.map +1 -0
- package/dist/controllers/operations-timesheets.controller.d.ts +99 -0
- package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -0
- package/dist/controllers/operations-timesheets.controller.js +154 -0
- package/dist/controllers/operations-timesheets.controller.js.map +1 -0
- package/dist/dto/create-collaborator-type.dto.d.ts +10 -0
- package/dist/dto/create-collaborator-type.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-type.dto.js +56 -0
- package/dist/dto/create-collaborator-type.dto.js.map +1 -0
- package/dist/dto/create-collaborator.dto.d.ts +42 -0
- package/dist/dto/create-collaborator.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator.dto.js +228 -0
- package/dist/dto/create-collaborator.dto.js.map +1 -0
- package/dist/dto/create-schedule-adjustment-request.dto.d.ts +17 -0
- package/dist/dto/create-schedule-adjustment-request.dto.d.ts.map +1 -0
- package/dist/dto/create-schedule-adjustment-request.dto.js +89 -0
- package/dist/dto/create-schedule-adjustment-request.dto.js.map +1 -0
- package/dist/dto/create-task.dto.d.ts +8 -0
- package/dist/dto/create-task.dto.d.ts.map +1 -0
- package/dist/dto/create-task.dto.js +50 -0
- package/dist/dto/create-task.dto.js.map +1 -0
- package/dist/dto/create-time-off-request.dto.d.ts +9 -0
- package/dist/dto/create-time-off-request.dto.d.ts.map +1 -0
- package/dist/dto/create-time-off-request.dto.js +54 -0
- package/dist/dto/create-time-off-request.dto.js.map +1 -0
- package/dist/dto/create-timesheet-entry.dto.d.ts +12 -0
- package/dist/dto/create-timesheet-entry.dto.d.ts.map +1 -0
- package/dist/dto/create-timesheet-entry.dto.js +75 -0
- package/dist/dto/create-timesheet-entry.dto.js.map +1 -0
- package/dist/dto/list-collaborator-types.dto.d.ts +4 -0
- package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-types.dto.js +29 -0
- package/dist/dto/list-collaborator-types.dto.js.map +1 -0
- package/dist/dto/list-collaborators.dto.d.ts +8 -0
- package/dist/dto/list-collaborators.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborators.dto.js +42 -0
- package/dist/dto/list-collaborators.dto.js.map +1 -0
- package/dist/dto/list-project-options.dto.d.ts +4 -0
- package/dist/dto/list-project-options.dto.d.ts.map +1 -0
- package/dist/dto/list-project-options.dto.js +8 -0
- package/dist/dto/list-project-options.dto.js.map +1 -0
- package/dist/dto/list-tasks.dto.d.ts +7 -0
- package/dist/dto/list-tasks.dto.d.ts.map +1 -0
- package/dist/dto/list-tasks.dto.js +38 -0
- package/dist/dto/list-tasks.dto.js.map +1 -0
- package/dist/dto/list-timesheet-entries.dto.d.ts +10 -0
- package/dist/dto/list-timesheet-entries.dto.d.ts.map +1 -0
- package/dist/dto/list-timesheet-entries.dto.js +54 -0
- package/dist/dto/list-timesheet-entries.dto.js.map +1 -0
- package/dist/dto/update-collaborator-type.dto.d.ts +4 -0
- package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-type.dto.js +8 -0
- package/dist/dto/update-collaborator-type.dto.js.map +1 -0
- package/dist/dto/update-collaborator.dto.d.ts +4 -0
- package/dist/dto/update-collaborator.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator.dto.js +8 -0
- package/dist/dto/update-collaborator.dto.js.map +1 -0
- package/dist/dto/update-task.dto.d.ts +8 -0
- package/dist/dto/update-task.dto.d.ts.map +1 -0
- package/dist/dto/update-task.dto.js +51 -0
- package/dist/dto/update-task.dto.js.map +1 -0
- package/dist/operations.controller.d.ts +0 -1045
- package/dist/operations.controller.d.ts.map +1 -1
- package/dist/operations.controller.js +0 -429
- package/dist/operations.controller.js.map +1 -1
- package/dist/operations.module.d.ts.map +1 -1
- package/dist/operations.module.js +23 -2
- package/dist/operations.module.js.map +1 -1
- package/dist/operations.service.d.ts +373 -8
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1598 -111
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +315 -1
- package/dist/operations.service.spec.js.map +1 -1
- package/dist/services/shared/operations-access.service.d.ts +16 -0
- package/dist/services/shared/operations-access.service.d.ts.map +1 -0
- package/dist/services/shared/operations-access.service.js +48 -0
- package/dist/services/shared/operations-access.service.js.map +1 -0
- package/hedhog/data/dashboard.yaml +20 -0
- package/hedhog/data/dashboard_component.yaml +274 -0
- package/hedhog/data/dashboard_component_role.yaml +174 -0
- package/hedhog/data/dashboard_item.yaml +299 -0
- package/hedhog/data/dashboard_role.yaml +20 -0
- package/hedhog/data/menu.yaml +30 -13
- package/hedhog/data/operations_collaborator_type.yaml +76 -0
- package/hedhog/data/route.yaml +183 -0
- package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +231 -0
- package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +134 -49
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +772 -93
- package/hedhog/frontend/app/_components/department-select-with-create.tsx.ejs +38 -16
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +875 -632
- package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +213 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +30 -1
- package/hedhog/frontend/app/_lib/types.ts.ejs +142 -39
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +33 -2
- package/hedhog/frontend/app/approvals/page.tsx.ejs +116 -98
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -0
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +109 -68
- package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +11 -9
- package/hedhog/frontend/app/departments/page.tsx.ejs +1 -1
- package/hedhog/frontend/app/projects/page.tsx.ejs +5 -1
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +244 -120
- package/hedhog/frontend/app/team/page.tsx.ejs +15 -2
- package/hedhog/frontend/app/time-off/page.tsx.ejs +158 -82
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +814 -357
- package/hedhog/frontend/messages/en.json +243 -51
- package/hedhog/frontend/messages/pt.json +458 -268
- package/hedhog/table/operations_collaborator.yaml +26 -13
- package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -0
- package/hedhog/table/operations_collaborator_type.yaml +33 -0
- package/hedhog/table/operations_job_title.yaml +24 -0
- package/hedhog/table/operations_project_assignment.yaml +9 -0
- package/hedhog/table/operations_project_role.yaml +39 -0
- package/hedhog/table/operations_task.yaml +30 -0
- package/hedhog/table/operations_timesheet_entry.yaml +12 -0
- package/package.json +6 -6
- package/src/controllers/operations-approvals.controller.ts +24 -0
- package/src/controllers/operations-collaborators.controller.ts +60 -0
- package/src/controllers/operations-contracts.controller.ts +138 -0
- package/src/controllers/operations-org-structure.controller.ts +92 -0
- package/src/controllers/operations-projects.controller.ts +50 -0
- package/src/controllers/operations-tasks.controller.ts +52 -0
- package/src/controllers/operations-timesheets.controller.ts +100 -0
- package/src/dto/create-collaborator-type.dto.ts +43 -0
- package/src/dto/create-collaborator.dto.ts +223 -0
- package/src/dto/create-schedule-adjustment-request.dto.ts +91 -0
- package/src/dto/create-task.dto.ts +35 -0
- package/src/dto/create-time-off-request.dto.ts +53 -0
- package/src/dto/create-timesheet-entry.dto.ts +67 -0
- package/src/dto/list-collaborator-types.dto.ts +15 -0
- package/src/dto/list-collaborators.dto.ts +30 -0
- package/src/dto/list-project-options.dto.ts +3 -0
- package/src/dto/list-tasks.dto.ts +25 -0
- package/src/dto/list-timesheet-entries.dto.ts +40 -0
- package/src/dto/update-collaborator-type.dto.ts +3 -0
- package/src/dto/update-collaborator.dto.ts +3 -0
- package/src/dto/update-task.dto.ts +36 -0
- package/src/operations.controller.ts +1 -278
- package/src/operations.module.ts +23 -2
- package/src/operations.service.spec.ts +450 -0
- package/src/operations.service.ts +4641 -2163
- package/src/services/shared/operations-access.service.ts +52 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { EmptyState, Page, SearchBar } from '@/components/entity-list';
|
|
4
4
|
import { Button } from '@/components/ui/button';
|
|
5
|
+
import { FormActions } from '@/components/ui/form-actions';
|
|
5
6
|
import { Input } from '@/components/ui/input';
|
|
6
7
|
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
7
8
|
import {
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
SelectTrigger,
|
|
12
13
|
SelectValue,
|
|
13
14
|
} from '@/components/ui/select';
|
|
14
|
-
import { Switch } from '@/components/ui/switch';
|
|
15
15
|
import {
|
|
16
16
|
Sheet,
|
|
17
17
|
SheetContent,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
SheetHeader,
|
|
20
20
|
SheetTitle,
|
|
21
21
|
} from '@/components/ui/sheet';
|
|
22
|
+
import { Switch } from '@/components/ui/switch';
|
|
22
23
|
import {
|
|
23
24
|
Table,
|
|
24
25
|
TableBody,
|
|
@@ -30,8 +31,8 @@ import {
|
|
|
30
31
|
import { Textarea } from '@/components/ui/textarea';
|
|
31
32
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
32
33
|
import { CalendarRange, Plus } from 'lucide-react';
|
|
33
|
-
import { useMemo, useState } from 'react';
|
|
34
34
|
import { useTranslations } from 'next-intl';
|
|
35
|
+
import { useMemo, useState } from 'react';
|
|
35
36
|
import { OperationsHeader } from '../_components/operations-header';
|
|
36
37
|
import { StatusBadge } from '../_components/status-badge';
|
|
37
38
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
@@ -40,13 +41,14 @@ import type {
|
|
|
40
41
|
OperationsScheduleAdjustmentDay,
|
|
41
42
|
OperationsScheduleAdjustmentRequest,
|
|
42
43
|
} from '../_lib/types';
|
|
43
|
-
import { parseNumberInput, trimToNull } from '../_lib/utils/forms';
|
|
44
44
|
import {
|
|
45
45
|
formatDateRange,
|
|
46
46
|
formatEnumLabel,
|
|
47
|
+
formatWeekdayLabel,
|
|
47
48
|
getStatusBadgeClass,
|
|
48
49
|
summarizeScheduleDays,
|
|
49
50
|
} from '../_lib/utils/format';
|
|
51
|
+
import { parseNumberInput, trimToNull } from '../_lib/utils/forms';
|
|
50
52
|
|
|
51
53
|
const weekdays = [
|
|
52
54
|
'monday',
|
|
@@ -86,33 +88,42 @@ const emptyForm: ScheduleFormState = {
|
|
|
86
88
|
})),
|
|
87
89
|
};
|
|
88
90
|
|
|
89
|
-
export default function OperationsScheduleAdjustmentsPage() {
|
|
90
|
-
const t = useTranslations('operations.ScheduleAdjustmentsPage');
|
|
91
|
-
const commonT = useTranslations('operations.Common');
|
|
92
|
-
const { request, showToastHandler, currentLocaleCode } = useApp();
|
|
93
|
-
const access = useOperationsAccess();
|
|
94
|
-
const [search, setSearch] = useState('');
|
|
95
|
-
const [statusFilter, setStatusFilter] = useState('all');
|
|
96
|
-
const [isSheetOpen, setIsSheetOpen] = useState(false);
|
|
97
|
-
const [form, setForm] = useState<ScheduleFormState>(emptyForm);
|
|
98
|
-
|
|
99
|
-
const getRequestScopeLabel = (value?: string | null) => {
|
|
100
|
-
if (!value) {
|
|
101
|
-
return '-';
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const key = `options.requestScopes.${value}`;
|
|
105
|
-
return t.has(key) ? t(key) : formatEnumLabel(value);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const getStatusLabel = (value?: string | null) => {
|
|
109
|
-
if (!value) {
|
|
110
|
-
return '-';
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const key = `options.statuses.${value}`;
|
|
114
|
-
return t.has(key) ? t(key) : formatEnumLabel(value);
|
|
115
|
-
};
|
|
91
|
+
export default function OperationsScheduleAdjustmentsPage() {
|
|
92
|
+
const t = useTranslations('operations.ScheduleAdjustmentsPage');
|
|
93
|
+
const commonT = useTranslations('operations.Common');
|
|
94
|
+
const { request, showToastHandler, currentLocaleCode } = useApp();
|
|
95
|
+
const access = useOperationsAccess();
|
|
96
|
+
const [search, setSearch] = useState('');
|
|
97
|
+
const [statusFilter, setStatusFilter] = useState('all');
|
|
98
|
+
const [isSheetOpen, setIsSheetOpen] = useState(false);
|
|
99
|
+
const [form, setForm] = useState<ScheduleFormState>(emptyForm);
|
|
100
|
+
|
|
101
|
+
const getRequestScopeLabel = (value?: string | null) => {
|
|
102
|
+
if (!value) {
|
|
103
|
+
return '-';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const key = `options.requestScopes.${value}`;
|
|
107
|
+
return t.has(key) ? t(key) : formatEnumLabel(value);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const getStatusLabel = (value?: string | null) => {
|
|
111
|
+
if (!value) {
|
|
112
|
+
return '-';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const key = `options.statuses.${value}`;
|
|
116
|
+
return t.has(key) ? t(key) : formatEnumLabel(value);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const getStatusDescription = (value?: string | null) => {
|
|
120
|
+
if (!value) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const key = `statusHelp.${value}`;
|
|
125
|
+
return t.has(key) ? t(key) : null;
|
|
126
|
+
};
|
|
116
127
|
|
|
117
128
|
const { data: requests = [], refetch } = useQuery<
|
|
118
129
|
OperationsScheduleAdjustmentRequest[]
|
|
@@ -139,7 +150,9 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
139
150
|
]
|
|
140
151
|
.filter(Boolean)
|
|
141
152
|
.some((value) =>
|
|
142
|
-
String(value)
|
|
153
|
+
String(value)
|
|
154
|
+
.toLowerCase()
|
|
155
|
+
.includes(search.trim().toLowerCase())
|
|
143
156
|
);
|
|
144
157
|
const matchesStatus =
|
|
145
158
|
statusFilter === 'all' ? true : item.status === statusFilter;
|
|
@@ -152,20 +165,34 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
152
165
|
{
|
|
153
166
|
key: 'submitted',
|
|
154
167
|
title: t('cards.submitted'),
|
|
168
|
+
description: t('cards.submittedDescription'),
|
|
155
169
|
value: requests.filter((item) => item.status === 'submitted').length,
|
|
156
170
|
},
|
|
157
171
|
{
|
|
158
172
|
key: 'approved',
|
|
159
173
|
title: t('cards.approved'),
|
|
174
|
+
description: t('cards.approvedDescription'),
|
|
160
175
|
value: requests.filter((item) => item.status === 'approved').length,
|
|
161
176
|
},
|
|
162
177
|
{
|
|
163
178
|
key: 'permanent',
|
|
164
179
|
title: t('cards.permanent'),
|
|
165
|
-
|
|
180
|
+
description: t('cards.permanentDescription'),
|
|
181
|
+
value: requests.filter((item) => item.requestScope === 'permanent')
|
|
182
|
+
.length,
|
|
166
183
|
},
|
|
167
184
|
];
|
|
168
185
|
|
|
186
|
+
const requestedScheduleSummary = useMemo(
|
|
187
|
+
() => summarizeScheduleDays(form.days, currentLocaleCode),
|
|
188
|
+
[form.days, currentLocaleCode]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const workingDaysCount = useMemo(
|
|
192
|
+
() => form.days.filter((day) => day.isWorkingDay).length,
|
|
193
|
+
[form.days]
|
|
194
|
+
);
|
|
195
|
+
|
|
169
196
|
const updateDay = (
|
|
170
197
|
weekday: string,
|
|
171
198
|
patch: Partial<ScheduleFormState['days'][number]>
|
|
@@ -185,19 +212,26 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
try {
|
|
188
|
-
await mutateOperations(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
await mutateOperations(
|
|
216
|
+
request,
|
|
217
|
+
'/operations/schedule-adjustments',
|
|
218
|
+
'POST',
|
|
219
|
+
{
|
|
220
|
+
requestScope: form.requestScope,
|
|
221
|
+
effectiveStartDate: form.effectiveStartDate,
|
|
222
|
+
effectiveEndDate: trimToNull(form.effectiveEndDate),
|
|
223
|
+
reason: trimToNull(form.reason),
|
|
224
|
+
days: form.days.map((day) => ({
|
|
225
|
+
weekday: day.weekday,
|
|
226
|
+
isWorkingDay: day.isWorkingDay,
|
|
227
|
+
startTime: day.isWorkingDay ? trimToNull(day.startTime) : null,
|
|
228
|
+
endTime: day.isWorkingDay ? trimToNull(day.endTime) : null,
|
|
229
|
+
breakMinutes: day.isWorkingDay
|
|
230
|
+
? parseNumberInput(day.breakMinutes)
|
|
231
|
+
: 0,
|
|
232
|
+
})),
|
|
233
|
+
}
|
|
234
|
+
);
|
|
201
235
|
|
|
202
236
|
showToastHandler?.('success', t('messages.saveSuccess'));
|
|
203
237
|
setIsSheetOpen(false);
|
|
@@ -236,12 +270,12 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
236
270
|
value: statusFilter,
|
|
237
271
|
onChange: setStatusFilter,
|
|
238
272
|
placeholder: commonT('labels.status'),
|
|
239
|
-
options: [
|
|
240
|
-
{ value: 'all', label: commonT('filters.allStatuses') },
|
|
241
|
-
{ value: 'submitted', label: getStatusLabel('submitted') },
|
|
242
|
-
{ value: 'approved', label: getStatusLabel('approved') },
|
|
243
|
-
{ value: 'rejected', label: getStatusLabel('rejected') },
|
|
244
|
-
],
|
|
273
|
+
options: [
|
|
274
|
+
{ value: 'all', label: commonT('filters.allStatuses') },
|
|
275
|
+
{ value: 'submitted', label: getStatusLabel('submitted') },
|
|
276
|
+
{ value: 'approved', label: getStatusLabel('approved') },
|
|
277
|
+
{ value: 'rejected', label: getStatusLabel('rejected') },
|
|
278
|
+
],
|
|
245
279
|
},
|
|
246
280
|
]}
|
|
247
281
|
/>
|
|
@@ -257,7 +291,7 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
257
291
|
<TableHead>{commonT('labels.requestScope')}</TableHead>
|
|
258
292
|
<TableHead>{commonT('labels.timeline')}</TableHead>
|
|
259
293
|
<TableHead>{t('table.currentSchedule')}</TableHead>
|
|
260
|
-
<TableHead>{
|
|
294
|
+
<TableHead>{t('table.requestedSchedule')}</TableHead>
|
|
261
295
|
<TableHead>{commonT('labels.approver')}</TableHead>
|
|
262
296
|
<TableHead>{commonT('labels.status')}</TableHead>
|
|
263
297
|
<TableHead>{commonT('labels.notes')}</TableHead>
|
|
@@ -267,7 +301,9 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
267
301
|
{filteredRows.map((requestItem) => (
|
|
268
302
|
<TableRow key={requestItem.id}>
|
|
269
303
|
<TableCell>{requestItem.collaboratorName}</TableCell>
|
|
270
|
-
<TableCell>
|
|
304
|
+
<TableCell>
|
|
305
|
+
{getRequestScopeLabel(requestItem.requestScope)}
|
|
306
|
+
</TableCell>
|
|
271
307
|
<TableCell>
|
|
272
308
|
{formatDateRange(
|
|
273
309
|
requestItem.effectiveStartDate,
|
|
@@ -275,21 +311,33 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
275
311
|
)}
|
|
276
312
|
</TableCell>
|
|
277
313
|
<TableCell>
|
|
278
|
-
{summarizeScheduleDays(
|
|
314
|
+
{summarizeScheduleDays(
|
|
315
|
+
requestItem.currentSchedule ?? [],
|
|
316
|
+
currentLocaleCode
|
|
317
|
+
)}
|
|
279
318
|
</TableCell>
|
|
280
319
|
<TableCell>
|
|
281
320
|
{summarizeScheduleDays(
|
|
282
|
-
(requestItem.days ??
|
|
321
|
+
(requestItem.days ??
|
|
322
|
+
[]) as OperationsScheduleAdjustmentDay[],
|
|
323
|
+
currentLocaleCode
|
|
283
324
|
)}
|
|
284
325
|
</TableCell>
|
|
285
326
|
<TableCell>
|
|
286
327
|
{requestItem.approverName || commonT('labels.notAssigned')}
|
|
287
328
|
</TableCell>
|
|
288
329
|
<TableCell>
|
|
289
|
-
<
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
330
|
+
<div className="space-y-1">
|
|
331
|
+
<StatusBadge
|
|
332
|
+
label={getStatusLabel(requestItem.status)}
|
|
333
|
+
className={getStatusBadgeClass(requestItem.status)}
|
|
334
|
+
/>
|
|
335
|
+
{getStatusDescription(requestItem.status) ? (
|
|
336
|
+
<p className="max-w-40 text-xs text-muted-foreground">
|
|
337
|
+
{getStatusDescription(requestItem.status)}
|
|
338
|
+
</p>
|
|
339
|
+
) : null}
|
|
340
|
+
</div>
|
|
293
341
|
</TableCell>
|
|
294
342
|
<TableCell>
|
|
295
343
|
{requestItem.approverNote || commonT('labels.noNotes')}
|
|
@@ -304,8 +352,16 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
304
352
|
icon={<CalendarRange className="size-12" />}
|
|
305
353
|
title={commonT('states.emptyTitle')}
|
|
306
354
|
description={t('emptyDescription')}
|
|
307
|
-
actionLabel={
|
|
308
|
-
|
|
355
|
+
actionLabel={
|
|
356
|
+
access.isCollaborator
|
|
357
|
+
? commonT('actions.create')
|
|
358
|
+
: commonT('actions.refresh')
|
|
359
|
+
}
|
|
360
|
+
onAction={
|
|
361
|
+
access.isCollaborator
|
|
362
|
+
? () => setIsSheetOpen(true)
|
|
363
|
+
: () => void refetch()
|
|
364
|
+
}
|
|
309
365
|
/>
|
|
310
366
|
)}
|
|
311
367
|
|
|
@@ -316,31 +372,35 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
316
372
|
<SheetDescription>{t('sheet.description')}</SheetDescription>
|
|
317
373
|
</SheetHeader>
|
|
318
374
|
|
|
319
|
-
<div className="mt-6 grid gap-4">
|
|
375
|
+
<div className="mt-6 grid gap-4 px-4 pb-20">
|
|
320
376
|
<div className="grid gap-4 md:grid-cols-3">
|
|
321
377
|
<div className="space-y-2">
|
|
322
|
-
<label className="text-sm font-medium">
|
|
378
|
+
<label className="text-sm font-medium">
|
|
379
|
+
{commonT('labels.requestScope')}
|
|
380
|
+
</label>
|
|
323
381
|
<Select
|
|
324
382
|
value={form.requestScope}
|
|
325
383
|
onValueChange={(value) =>
|
|
326
384
|
setForm((current) => ({ ...current, requestScope: value }))
|
|
327
385
|
}
|
|
328
386
|
>
|
|
329
|
-
<SelectTrigger>
|
|
330
|
-
<SelectValue />
|
|
331
|
-
</SelectTrigger>
|
|
332
|
-
<SelectContent>
|
|
333
|
-
<SelectItem value="temporary">
|
|
334
|
-
{getRequestScopeLabel('temporary')}
|
|
335
|
-
</SelectItem>
|
|
336
|
-
<SelectItem value="permanent">
|
|
337
|
-
{getRequestScopeLabel('permanent')}
|
|
338
|
-
</SelectItem>
|
|
339
|
-
</SelectContent>
|
|
340
|
-
</Select>
|
|
387
|
+
<SelectTrigger className="w-full">
|
|
388
|
+
<SelectValue />
|
|
389
|
+
</SelectTrigger>
|
|
390
|
+
<SelectContent>
|
|
391
|
+
<SelectItem value="temporary">
|
|
392
|
+
{getRequestScopeLabel('temporary')}
|
|
393
|
+
</SelectItem>
|
|
394
|
+
<SelectItem value="permanent">
|
|
395
|
+
{getRequestScopeLabel('permanent')}
|
|
396
|
+
</SelectItem>
|
|
397
|
+
</SelectContent>
|
|
398
|
+
</Select>
|
|
341
399
|
</div>
|
|
342
400
|
<div className="space-y-2">
|
|
343
|
-
<label className="text-sm font-medium">
|
|
401
|
+
<label className="text-sm font-medium">
|
|
402
|
+
{commonT('labels.startDate')}
|
|
403
|
+
</label>
|
|
344
404
|
<Input
|
|
345
405
|
type="date"
|
|
346
406
|
value={form.effectiveStartDate}
|
|
@@ -353,7 +413,9 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
353
413
|
/>
|
|
354
414
|
</div>
|
|
355
415
|
<div className="space-y-2">
|
|
356
|
-
<label className="text-sm font-medium">
|
|
416
|
+
<label className="text-sm font-medium">
|
|
417
|
+
{commonT('labels.endDate')}
|
|
418
|
+
</label>
|
|
357
419
|
<Input
|
|
358
420
|
type="date"
|
|
359
421
|
value={form.effectiveEndDate}
|
|
@@ -368,71 +430,133 @@ export default function OperationsScheduleAdjustmentsPage() {
|
|
|
368
430
|
</div>
|
|
369
431
|
|
|
370
432
|
<div className="space-y-2">
|
|
371
|
-
<label className="text-sm font-medium">
|
|
433
|
+
<label className="text-sm font-medium">
|
|
434
|
+
{commonT('labels.reason')}
|
|
435
|
+
</label>
|
|
372
436
|
<Textarea
|
|
373
437
|
rows={3}
|
|
438
|
+
placeholder={t('sheet.reasonPlaceholder')}
|
|
374
439
|
value={form.reason}
|
|
375
440
|
onChange={(event) =>
|
|
376
|
-
setForm((current) => ({
|
|
441
|
+
setForm((current) => ({
|
|
442
|
+
...current,
|
|
443
|
+
reason: event.target.value,
|
|
444
|
+
}))
|
|
377
445
|
}
|
|
378
446
|
/>
|
|
379
447
|
</div>
|
|
380
448
|
|
|
381
449
|
<div className="space-y-3">
|
|
382
|
-
<div className="text-sm font-medium">
|
|
450
|
+
<div className="text-sm font-medium">
|
|
451
|
+
{commonT('labels.weeklySchedule')}
|
|
452
|
+
</div>
|
|
453
|
+
<div className="rounded-lg border bg-muted/30 p-3 text-sm">
|
|
454
|
+
<div className="font-medium text-foreground">
|
|
455
|
+
{t('scheduleEditor.summaryLabel')}
|
|
456
|
+
</div>
|
|
457
|
+
<p className="mt-1 text-muted-foreground">
|
|
458
|
+
{requestedScheduleSummary}
|
|
459
|
+
</p>
|
|
460
|
+
<p className="mt-2 text-xs text-muted-foreground">
|
|
461
|
+
{t('scheduleEditor.daysPerWeek', {
|
|
462
|
+
count: workingDaysCount,
|
|
463
|
+
})}
|
|
464
|
+
</p>
|
|
465
|
+
</div>
|
|
466
|
+
<p className="text-sm text-muted-foreground">
|
|
467
|
+
{t('scheduleEditor.helper')}
|
|
468
|
+
</p>
|
|
383
469
|
<div className="space-y-3">
|
|
384
470
|
{form.days.map((day) => (
|
|
385
471
|
<div
|
|
386
472
|
key={day.weekday}
|
|
387
|
-
className="grid gap-3 rounded-lg border p-4 lg:grid-cols-[
|
|
473
|
+
className="grid gap-3 rounded-lg border p-4 lg:grid-cols-[minmax(0,1fr)_minmax(0,2fr)]"
|
|
388
474
|
>
|
|
389
|
-
<div>
|
|
390
|
-
<div
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
475
|
+
<div className="space-y-2">
|
|
476
|
+
<div>
|
|
477
|
+
<div className="font-medium">
|
|
478
|
+
{formatWeekdayLabel(day.weekday, currentLocaleCode)}
|
|
479
|
+
</div>
|
|
480
|
+
<div className="text-xs text-muted-foreground">
|
|
481
|
+
{day.isWorkingDay
|
|
482
|
+
? `${commonT('labels.workingDay')} • ${day.startTime || '--:--'}-${day.endTime || '--:--'}`
|
|
483
|
+
: commonT('labels.dayOff')}
|
|
484
|
+
</div>
|
|
395
485
|
</div>
|
|
486
|
+
<label className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
487
|
+
<Switch
|
|
488
|
+
checked={day.isWorkingDay}
|
|
489
|
+
onCheckedChange={(checked) =>
|
|
490
|
+
updateDay(day.weekday, { isWorkingDay: checked })
|
|
491
|
+
}
|
|
492
|
+
/>
|
|
493
|
+
<span>
|
|
494
|
+
{day.isWorkingDay
|
|
495
|
+
? commonT('labels.workingDay')
|
|
496
|
+
: commonT('labels.dayOff')}
|
|
497
|
+
</span>
|
|
498
|
+
</label>
|
|
396
499
|
</div>
|
|
397
|
-
<div className="
|
|
398
|
-
<
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
500
|
+
<div className="grid gap-3 sm:grid-cols-3">
|
|
501
|
+
<div className="space-y-1">
|
|
502
|
+
<div className="text-xs text-muted-foreground">
|
|
503
|
+
{t('scheduleEditor.startTime')}
|
|
504
|
+
</div>
|
|
505
|
+
<Input
|
|
506
|
+
type="time"
|
|
507
|
+
value={day.startTime}
|
|
508
|
+
disabled={!day.isWorkingDay}
|
|
509
|
+
onChange={(event) =>
|
|
510
|
+
updateDay(day.weekday, {
|
|
511
|
+
startTime: event.target.value,
|
|
512
|
+
})
|
|
513
|
+
}
|
|
514
|
+
/>
|
|
515
|
+
</div>
|
|
516
|
+
<div className="space-y-1">
|
|
517
|
+
<div className="text-xs text-muted-foreground">
|
|
518
|
+
{t('scheduleEditor.endTime')}
|
|
519
|
+
</div>
|
|
520
|
+
<Input
|
|
521
|
+
type="time"
|
|
522
|
+
value={day.endTime}
|
|
523
|
+
disabled={!day.isWorkingDay}
|
|
524
|
+
onChange={(event) =>
|
|
525
|
+
updateDay(day.weekday, {
|
|
526
|
+
endTime: event.target.value,
|
|
527
|
+
})
|
|
528
|
+
}
|
|
529
|
+
/>
|
|
530
|
+
</div>
|
|
531
|
+
<div className="space-y-1">
|
|
532
|
+
<div className="text-xs text-muted-foreground">
|
|
533
|
+
{t('scheduleEditor.breakMinutes')}
|
|
534
|
+
</div>
|
|
535
|
+
<Input
|
|
536
|
+
type="number"
|
|
537
|
+
value={day.breakMinutes}
|
|
538
|
+
disabled={!day.isWorkingDay}
|
|
539
|
+
onChange={(event) =>
|
|
540
|
+
updateDay(day.weekday, {
|
|
541
|
+
breakMinutes: event.target.value,
|
|
542
|
+
})
|
|
543
|
+
}
|
|
544
|
+
/>
|
|
545
|
+
</div>
|
|
404
546
|
</div>
|
|
405
|
-
<Input
|
|
406
|
-
type="time"
|
|
407
|
-
value={day.startTime}
|
|
408
|
-
disabled={!day.isWorkingDay}
|
|
409
|
-
onChange={(event) =>
|
|
410
|
-
updateDay(day.weekday, { startTime: event.target.value })
|
|
411
|
-
}
|
|
412
|
-
/>
|
|
413
|
-
<Input
|
|
414
|
-
type="time"
|
|
415
|
-
value={day.endTime}
|
|
416
|
-
disabled={!day.isWorkingDay}
|
|
417
|
-
onChange={(event) =>
|
|
418
|
-
updateDay(day.weekday, { endTime: event.target.value })
|
|
419
|
-
}
|
|
420
|
-
/>
|
|
421
|
-
<Input
|
|
422
|
-
type="number"
|
|
423
|
-
value={day.breakMinutes}
|
|
424
|
-
disabled={!day.isWorkingDay}
|
|
425
|
-
onChange={(event) =>
|
|
426
|
-
updateDay(day.weekday, { breakMinutes: event.target.value })
|
|
427
|
-
}
|
|
428
|
-
/>
|
|
429
547
|
</div>
|
|
430
548
|
))}
|
|
431
549
|
</div>
|
|
432
550
|
</div>
|
|
433
|
-
|
|
434
|
-
<Button onClick={() => void onSubmit()}>{commonT('actions.save')}</Button>
|
|
435
551
|
</div>
|
|
552
|
+
|
|
553
|
+
<FormActions
|
|
554
|
+
sheet
|
|
555
|
+
cancelLabel={commonT('actions.cancel')}
|
|
556
|
+
onCancel={() => setIsSheetOpen(false)}
|
|
557
|
+
onSubmit={() => void onSubmit()}
|
|
558
|
+
submitLabel={commonT('actions.save')}
|
|
559
|
+
/>
|
|
436
560
|
</SheetContent>
|
|
437
561
|
</Sheet>
|
|
438
562
|
</Page>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { EmptyState, Page } from '@/components/entity-list';
|
|
4
|
-
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
5
4
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
5
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
6
6
|
import {
|
|
7
7
|
Table,
|
|
8
8
|
TableBody,
|
|
@@ -61,31 +61,37 @@ export default function OperationsTeamPage() {
|
|
|
61
61
|
{
|
|
62
62
|
key: 'members',
|
|
63
63
|
title: t('cards.members'),
|
|
64
|
+
description: t('cards.membersDescription'),
|
|
64
65
|
value: team?.teamMembers.length ?? 0,
|
|
65
66
|
},
|
|
66
67
|
{
|
|
67
68
|
key: 'projects',
|
|
68
69
|
title: t('cards.projects'),
|
|
70
|
+
description: t('cards.projectsDescription'),
|
|
69
71
|
value: team?.projectCount ?? 0,
|
|
70
72
|
},
|
|
71
73
|
{
|
|
72
74
|
key: 'approvals',
|
|
73
75
|
title: t('cards.pendingApprovals'),
|
|
76
|
+
description: t('cards.pendingApprovalsDescription'),
|
|
74
77
|
value: team?.pendingApprovals ?? 0,
|
|
75
78
|
},
|
|
76
79
|
{
|
|
77
80
|
key: 'timeOff',
|
|
78
81
|
title: t('cards.timeOff'),
|
|
82
|
+
description: t('cards.timeOffDescription'),
|
|
79
83
|
value: team?.pendingItems?.timeOffRequests ?? 0,
|
|
80
84
|
},
|
|
81
85
|
{
|
|
82
86
|
key: 'schedule',
|
|
83
87
|
title: t('cards.scheduleAdjustments'),
|
|
88
|
+
description: t('cards.scheduleAdjustmentsDescription'),
|
|
84
89
|
value: team?.pendingItems?.scheduleAdjustmentRequests ?? 0,
|
|
85
90
|
},
|
|
86
91
|
{
|
|
87
92
|
key: 'timesheets',
|
|
88
93
|
title: t('cards.timesheets'),
|
|
94
|
+
description: t('cards.timesheetsDescription'),
|
|
89
95
|
value: team?.pendingItems?.timesheets ?? 0,
|
|
90
96
|
},
|
|
91
97
|
];
|
|
@@ -120,7 +126,14 @@ export default function OperationsTeamPage() {
|
|
|
120
126
|
<TableCell>
|
|
121
127
|
<div className="font-medium">{member.displayName}</div>
|
|
122
128
|
<div className="text-xs text-muted-foreground">
|
|
123
|
-
{[
|
|
129
|
+
{[
|
|
130
|
+
member.code,
|
|
131
|
+
member.collaboratorType ||
|
|
132
|
+
formatEnumLabel(
|
|
133
|
+
member.collaboratorTypeSlug ??
|
|
134
|
+
member.collaboratorType
|
|
135
|
+
),
|
|
136
|
+
]
|
|
124
137
|
.filter(Boolean)
|
|
125
138
|
.join(' • ')}
|
|
126
139
|
</div>
|