@open-mercato/core 0.8.1-develop.7274.1.21a0083828 → 0.8.1-develop.7294.1.0ef99fc518
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/.turbo/turbo-build.log +1 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +21 -3
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +6 -4
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +6 -4
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +5 -3
- package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +6 -4
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/page.js +6 -4
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +9 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +4 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/lookups.js +7 -3
- package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js +5 -2
- package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js +4 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +8 -5
- package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +10 -7
- package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js +9 -0
- package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/index.js +3 -1
- package/dist/modules/customers/components/detail/schedule/index.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +11 -4
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +17 -9
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/data/validators.js +12 -7
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/lib/assignableStaff.js +103 -22
- package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
- package/dist/modules/customers/setup.js +10 -2
- package/dist/modules/customers/setup.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/[id]/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
- package/dist/modules/data_sync/api/schedules/route.js +5 -0
- package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +29 -5
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +48 -11
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/data/validators.js +13 -2
- package/dist/modules/data_sync/data/validators.js.map +2 -2
- package/dist/modules/data_sync/lib/schedule-value.js +30 -0
- package/dist/modules/data_sync/lib/schedule-value.js.map +7 -0
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +3 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +2 -1
- package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -4
- package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
- package/dist/modules/portal/lib/navigation.js +3 -4
- package/dist/modules/portal/lib/navigation.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +79 -14
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/page.js +122 -86
- package/dist/modules/staff/backend/staff/time-tracking/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +38 -17
- package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +45 -18
- package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/extension-points.js +16 -0
- package/dist/modules/staff/extension-points.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +2 -2
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +3 -4
- package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +14 -13
- package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +2 -2
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +60 -41
- package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -2
- package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +3 -4
- package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js +3 -3
- package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/assign/route.js +3 -3
- package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -2
- package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js +2 -2
- package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/events/route.js +4 -4
- package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/external/claims/route.js +3 -3
- package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js +2 -2
- package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -2
- package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js +2 -2
- package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js +2 -2
- package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js +2 -2
- package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/settings-general/route.js +4 -4
- package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js +2 -2
- package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/submit/route.js +3 -3
- package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/transition/route.js +3 -3
- package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js +3 -3
- package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -2
- package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +4 -2
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +9 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js +4 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js +3 -4
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +56 -1
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/auth/api/users/acl/route.ts +4 -3
- package/src/modules/auth/components/AclEditor.tsx +38 -2
- package/src/modules/auth/i18n/de.json +5 -1
- package/src/modules/auth/i18n/en.json +5 -1
- package/src/modules/auth/i18n/es.json +5 -1
- package/src/modules/auth/i18n/ko.json +5 -1
- package/src/modules/auth/i18n/pl.json +5 -1
- package/src/modules/customers/backend/customers/companies/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/page.tsx +6 -4
- package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +5 -3
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +6 -4
- package/src/modules/customers/backend/customers/people/page.tsx +6 -4
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +9 -2
- package/src/modules/customers/components/calendar/editor/hooks.ts +4 -2
- package/src/modules/customers/components/calendar/editor/lookups.ts +12 -4
- package/src/modules/customers/components/detail/AssignRoleDialog.tsx +4 -1
- package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +4 -2
- package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +15 -7
- package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +16 -7
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +4 -2
- package/src/modules/customers/components/detail/schedule/fieldConfig.ts +15 -0
- package/src/modules/customers/components/detail/schedule/index.ts +1 -1
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -9
- package/src/modules/customers/components/formConfig.tsx +35 -12
- package/src/modules/customers/data/validators.ts +14 -8
- package/src/modules/customers/i18n/de.json +1 -0
- package/src/modules/customers/i18n/en.json +1 -0
- package/src/modules/customers/i18n/es.json +1 -0
- package/src/modules/customers/i18n/ko.json +1 -0
- package/src/modules/customers/i18n/pl.json +1 -0
- package/src/modules/customers/lib/assignableStaff.ts +172 -34
- package/src/modules/customers/setup.ts +8 -0
- package/src/modules/data_sync/api/schedules/[id]/route.ts +5 -0
- package/src/modules/data_sync/api/schedules/route.ts +5 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +48 -4
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +61 -2
- package/src/modules/data_sync/data/validators.ts +26 -2
- package/src/modules/data_sync/i18n/de.json +2 -0
- package/src/modules/data_sync/i18n/en.json +2 -0
- package/src/modules/data_sync/i18n/es.json +2 -0
- package/src/modules/data_sync/i18n/ko.json +2 -0
- package/src/modules/data_sync/i18n/pl.json +2 -0
- package/src/modules/data_sync/lib/schedule-value.ts +45 -0
- package/src/modules/eudr/data/validators.ts +3 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +5 -4
- package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +3 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +2 -1
- package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +5 -5
- package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +5 -4
- package/src/modules/portal/i18n/de.json +1 -0
- package/src/modules/portal/i18n/en.json +1 -0
- package/src/modules/portal/i18n/es.json +1 -0
- package/src/modules/portal/i18n/ko.json +1 -0
- package/src/modules/portal/i18n/pl.json +1 -0
- package/src/modules/portal/lib/navigation.ts +8 -3
- package/src/modules/query_index/lib/engine.ts +111 -16
- package/src/modules/staff/backend/staff/time-tracking/page.tsx +161 -130
- package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +55 -43
- package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +61 -38
- package/src/modules/staff/extension-points.ts +16 -0
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +3 -4
- package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +3 -4
- package/src/modules/staff/i18n/de.json +3 -0
- package/src/modules/staff/i18n/en.json +3 -0
- package/src/modules/staff/i18n/es.json +3 -0
- package/src/modules/staff/i18n/ko.json +3 -0
- package/src/modules/staff/i18n/pl.json +3 -0
- package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +32 -29
- package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +89 -79
- package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -2
- package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -3
- package/src/modules/warranty_claims/api/ai/suggest/route.ts +3 -3
- package/src/modules/warranty_claims/api/assign/route.ts +3 -3
- package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -2
- package/src/modules/warranty_claims/api/entitlement/route.ts +2 -2
- package/src/modules/warranty_claims/api/events/route.ts +4 -4
- package/src/modules/warranty_claims/api/external/claims/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/events/route.ts +2 -2
- package/src/modules/warranty_claims/api/receiving/route.ts +2 -2
- package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -2
- package/src/modules/warranty_claims/api/return-label/route.ts +2 -2
- package/src/modules/warranty_claims/api/risk/route.ts +2 -2
- package/src/modules/warranty_claims/api/sales-return/route.ts +2 -2
- package/src/modules/warranty_claims/api/settings-general/route.ts +4 -4
- package/src/modules/warranty_claims/api/stats/route.ts +2 -2
- package/src/modules/warranty_claims/api/submit/route.ts +3 -3
- package/src/modules/warranty_claims/api/transition/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery/route.ts +3 -3
- package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -2
- package/src/modules/warranty_claims/backend/page.tsx +4 -2
- package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +9 -2
- package/src/modules/warranty_claims/backend/warranty_claims/settings/page.tsx +4 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.tsx +5 -4
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.tsx +5 -2
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +5 -2
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +2 -2
- package/src/modules/workflows/i18n/de.json +1 -1
- package/src/modules/workflows/i18n/en.json +1 -1
- package/src/modules/workflows/i18n/es.json +1 -1
- package/src/modules/workflows/i18n/ko.json +1 -1
- package/src/modules/workflows/i18n/pl.json +1 -1
- package/src/modules/workflows/lib/event-trigger-service.ts +139 -0
|
@@ -31,11 +31,13 @@
|
|
|
31
31
|
|
|
32
32
|
import * as React from 'react'
|
|
33
33
|
import Link from 'next/link'
|
|
34
|
+
import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
34
35
|
import { CalendarDays, Copy, Lock, Pencil, Plus } from 'lucide-react'
|
|
35
36
|
import { Page, PageBody, PageHeader } from '@open-mercato/ui/backend/Page'
|
|
36
37
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
37
38
|
import { Progress } from '@open-mercato/ui/primitives/progress'
|
|
38
39
|
import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
|
|
40
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
39
41
|
import {
|
|
40
42
|
Select,
|
|
41
43
|
SelectContent,
|
|
@@ -335,6 +337,89 @@ export default function TimeTrackingMyWorkPage() {
|
|
|
335
337
|
|
|
336
338
|
const todayTotal = data.entries.reduce((sum, entry) => sum + entry.durationMinutes, 0)
|
|
337
339
|
|
|
340
|
+
const todayEntriesColumns = React.useMemo<ColumnDef<MyWorkEntry>[]>(
|
|
341
|
+
() => [
|
|
342
|
+
{
|
|
343
|
+
id: 'task',
|
|
344
|
+
header: t('staff.time_tracking.myWork.today.columns.task', 'Task'),
|
|
345
|
+
cell: ({ row }) => {
|
|
346
|
+
const entry = row.original
|
|
347
|
+
return (
|
|
348
|
+
<div>
|
|
349
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
350
|
+
<span className="font-medium">
|
|
351
|
+
{entry.taskTitle ??
|
|
352
|
+
entry.description ??
|
|
353
|
+
t('staff.time_tracking.entries.table.noTask', 'No task')}
|
|
354
|
+
</span>
|
|
355
|
+
{entry.isBillable ? null : (
|
|
356
|
+
<StatusBadge variant="neutral">
|
|
357
|
+
{t('staff.time_tracking.entries.badges.nonBillable', 'non-billable')}
|
|
358
|
+
</StatusBadge>
|
|
359
|
+
)}
|
|
360
|
+
{entry.isLocked ? (
|
|
361
|
+
<StatusBadge variant="neutral">
|
|
362
|
+
<Lock className="size-3" aria-hidden="true" />
|
|
363
|
+
{t('staff.time_tracking.entries.badges.locked', 'locked')}
|
|
364
|
+
</StatusBadge>
|
|
365
|
+
) : null}
|
|
366
|
+
</div>
|
|
367
|
+
<div className="text-xs text-muted-foreground">
|
|
368
|
+
{[entry.projectName, entry.taskTitle ? entry.description : null]
|
|
369
|
+
.filter((part): part is string => Boolean(part))
|
|
370
|
+
.join(' · ')}
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
)
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: 'clock',
|
|
378
|
+
header: t('staff.time_tracking.myWork.today.columns.time', 'Time'),
|
|
379
|
+
meta: { maxWidth: '8rem' },
|
|
380
|
+
cell: ({ row }) => (
|
|
381
|
+
<span className="text-xs text-muted-foreground">{formatClockRange(row.original) ?? '—'}</span>
|
|
382
|
+
),
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: 'duration',
|
|
386
|
+
header: () => (
|
|
387
|
+
<span className="block text-right">
|
|
388
|
+
{t('staff.time_tracking.myWork.today.columns.duration', 'Duration')}
|
|
389
|
+
</span>
|
|
390
|
+
),
|
|
391
|
+
meta: { maxWidth: '6rem' },
|
|
392
|
+
cell: ({ row }) => (
|
|
393
|
+
<span className="block text-right font-mono font-semibold tabular-nums">
|
|
394
|
+
{formatDuration(row.original.durationMinutes, 'clock')}
|
|
395
|
+
</span>
|
|
396
|
+
),
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
id: 'actions',
|
|
400
|
+
header: '',
|
|
401
|
+
meta: { maxWidth: '3rem' },
|
|
402
|
+
cell: ({ row }) => {
|
|
403
|
+
const entry = row.original
|
|
404
|
+
return canManageOwn && !entry.isLocked ? (
|
|
405
|
+
<div className="flex justify-end">
|
|
406
|
+
<Button
|
|
407
|
+
type="button"
|
|
408
|
+
variant="ghost"
|
|
409
|
+
size="2xs"
|
|
410
|
+
aria-label={t('staff.time_tracking.timesheet.list.edit', 'Edit')}
|
|
411
|
+
onClick={() => setDialog({ open: true, entryId: entry.id })}
|
|
412
|
+
>
|
|
413
|
+
<Pencil className="size-3.5" aria-hidden="true" />
|
|
414
|
+
</Button>
|
|
415
|
+
</div>
|
|
416
|
+
) : null
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
[canManageOwn, t],
|
|
421
|
+
)
|
|
422
|
+
|
|
338
423
|
const submitQuickEntry = React.useCallback(async () => {
|
|
339
424
|
if (!quickTargetKey || quickMinutes === null || quickMinutes <= 0 || quickSaving) return
|
|
340
425
|
const target = parseTargetKey(quickTargetKey)
|
|
@@ -568,136 +653,82 @@ export default function TimeTrackingMyWorkPage() {
|
|
|
568
653
|
{t('staff.time_tracking.myWork.today.empty', 'Nothing logged today yet.')}
|
|
569
654
|
</p>
|
|
570
655
|
) : (
|
|
571
|
-
|
|
572
|
-
<
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
<
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
</
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
</Select>
|
|
648
|
-
<div className="w-32">
|
|
649
|
-
<DurationInput
|
|
650
|
-
key={quickEpoch}
|
|
651
|
-
variant="compact"
|
|
652
|
-
value={quickMinutes}
|
|
653
|
-
onChange={(minutes) => setQuickMinutes(minutes)}
|
|
654
|
-
ariaLabel={t('staff.time_tracking.myWork.quickEntry.duration', 'Duration')}
|
|
655
|
-
/>
|
|
656
|
-
</div>
|
|
657
|
-
<Button
|
|
658
|
-
type="button"
|
|
659
|
-
size="sm"
|
|
660
|
-
disabled={quickSaving || quickMinutes === null || quickMinutes <= 0}
|
|
661
|
-
onClick={() => void submitQuickEntry()}
|
|
662
|
-
>
|
|
663
|
-
{t('staff.time_tracking.myWork.quickEntry.save', 'Save')}
|
|
664
|
-
</Button>
|
|
665
|
-
<span className="self-center text-xs text-muted-foreground">
|
|
666
|
-
{t('staff.time_tracking.entryDialog.shortcutSave', '⌘↵ to save')}
|
|
667
|
-
</span>
|
|
668
|
-
{/*
|
|
669
|
-
* The same block the task drawer offers, from the same
|
|
670
|
-
* component: a row that can log an hour but not say what
|
|
671
|
-
* it was for, or when, sends people to the full dialog
|
|
672
|
-
* for the one field they were missing.
|
|
673
|
-
*/}
|
|
674
|
-
<div className="w-full">
|
|
675
|
-
<EntryDetailsFields
|
|
676
|
-
idPrefix="my-work-quick"
|
|
677
|
-
value={quickDetails}
|
|
678
|
-
onChange={setQuickDetails}
|
|
679
|
-
tagOptions={tagOptions}
|
|
680
|
-
onCreateTag={createTag}
|
|
681
|
-
disabled={quickSaving}
|
|
682
|
-
/>
|
|
683
|
-
</div>
|
|
684
|
-
</div>
|
|
685
|
-
</td>
|
|
686
|
-
</tr>
|
|
687
|
-
) : null}
|
|
688
|
-
</tbody>
|
|
689
|
-
<tfoot>
|
|
690
|
-
<tr className="border-t bg-muted/40 font-medium">
|
|
691
|
-
<td className="p-3" colSpan={2}>
|
|
692
|
-
{t('staff.time_tracking.myWork.today.total', 'Total today')}
|
|
693
|
-
</td>
|
|
694
|
-
<td className="p-3 text-right font-mono tabular-nums">
|
|
695
|
-
{formatDuration(todayTotal, 'clock')}
|
|
696
|
-
</td>
|
|
697
|
-
<td />
|
|
698
|
-
</tr>
|
|
699
|
-
</tfoot>
|
|
700
|
-
</table>
|
|
656
|
+
<>
|
|
657
|
+
<DataTable<MyWorkEntry>
|
|
658
|
+
embedded
|
|
659
|
+
extensionTableId={extensionPoints.hosts.myWorkTodayEntriesTable.tableId}
|
|
660
|
+
disableRowClick
|
|
661
|
+
columns={todayEntriesColumns}
|
|
662
|
+
data={data.entries}
|
|
663
|
+
/>
|
|
664
|
+
{canManageOwn && quickTargets.length > 0 ? (
|
|
665
|
+
<div
|
|
666
|
+
className="flex flex-wrap items-start gap-2 border-t p-3"
|
|
667
|
+
onKeyDown={(event) => {
|
|
668
|
+
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
|
669
|
+
event.preventDefault()
|
|
670
|
+
void submitQuickEntry()
|
|
671
|
+
}
|
|
672
|
+
}}
|
|
673
|
+
>
|
|
674
|
+
<Select value={quickTargetKey} onValueChange={setQuickTargetKey}>
|
|
675
|
+
<SelectTrigger
|
|
676
|
+
className="w-72"
|
|
677
|
+
aria-label={t('staff.time_tracking.myWork.quickEntry.target', 'Project or task')}
|
|
678
|
+
>
|
|
679
|
+
<SelectValue />
|
|
680
|
+
</SelectTrigger>
|
|
681
|
+
<SelectContent>
|
|
682
|
+
{quickTargets.map((target) => (
|
|
683
|
+
<SelectItem key={target.key} value={target.key}>
|
|
684
|
+
{targetLabel(target)}
|
|
685
|
+
</SelectItem>
|
|
686
|
+
))}
|
|
687
|
+
</SelectContent>
|
|
688
|
+
</Select>
|
|
689
|
+
<div className="w-32">
|
|
690
|
+
<DurationInput
|
|
691
|
+
key={quickEpoch}
|
|
692
|
+
variant="compact"
|
|
693
|
+
value={quickMinutes}
|
|
694
|
+
onChange={(minutes) => setQuickMinutes(minutes)}
|
|
695
|
+
ariaLabel={t('staff.time_tracking.myWork.quickEntry.duration', 'Duration')}
|
|
696
|
+
/>
|
|
697
|
+
</div>
|
|
698
|
+
<Button
|
|
699
|
+
type="button"
|
|
700
|
+
size="sm"
|
|
701
|
+
disabled={quickSaving || quickMinutes === null || quickMinutes <= 0}
|
|
702
|
+
onClick={() => void submitQuickEntry()}
|
|
703
|
+
>
|
|
704
|
+
{t('staff.time_tracking.myWork.quickEntry.save', 'Save')}
|
|
705
|
+
</Button>
|
|
706
|
+
<span className="self-center text-xs text-muted-foreground">
|
|
707
|
+
{t('staff.time_tracking.entryDialog.shortcutSave', '⌘↵ to save')}
|
|
708
|
+
</span>
|
|
709
|
+
{/*
|
|
710
|
+
* The same block the task drawer offers, from the same
|
|
711
|
+
* component: a row that can log an hour but not say what
|
|
712
|
+
* it was for, or when, sends people to the full dialog
|
|
713
|
+
* for the one field they were missing.
|
|
714
|
+
*/}
|
|
715
|
+
<div className="w-full">
|
|
716
|
+
<EntryDetailsFields
|
|
717
|
+
idPrefix="my-work-quick"
|
|
718
|
+
value={quickDetails}
|
|
719
|
+
onChange={setQuickDetails}
|
|
720
|
+
tagOptions={tagOptions}
|
|
721
|
+
onCreateTag={createTag}
|
|
722
|
+
disabled={quickSaving}
|
|
723
|
+
/>
|
|
724
|
+
</div>
|
|
725
|
+
</div>
|
|
726
|
+
) : null}
|
|
727
|
+
<div className="flex items-center justify-between border-t bg-muted/40 p-3 text-sm font-medium">
|
|
728
|
+
<span>{t('staff.time_tracking.myWork.today.total', 'Total today')}</span>
|
|
729
|
+
<span className="font-mono tabular-nums">{formatDuration(todayTotal, 'clock')}</span>
|
|
730
|
+
</div>
|
|
731
|
+
</>
|
|
701
732
|
)}
|
|
702
733
|
</section>
|
|
703
734
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
4
5
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
6
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
5
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
8
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
7
9
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
@@ -330,6 +332,53 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
330
332
|
const activeCount = employees.filter((emp) => emp.status === 'active').length
|
|
331
333
|
const inactiveCount = employees.length - activeCount
|
|
332
334
|
|
|
335
|
+
const recentEntriesColumns = React.useMemo<ColumnDef<ProjectEntryRow>[]>(
|
|
336
|
+
() => [
|
|
337
|
+
{
|
|
338
|
+
accessorKey: 'date',
|
|
339
|
+
header: t('staff.timesheets.projects.detail.colDate', 'Date'),
|
|
340
|
+
cell: ({ row }) => <span className="whitespace-nowrap">{row.original.date}</span>,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
id: 'person',
|
|
344
|
+
header: t('staff.timesheets.projects.detail.colWho', 'Person'),
|
|
345
|
+
cell: ({ row }) => (
|
|
346
|
+
<span className="whitespace-nowrap">
|
|
347
|
+
{employeeNameById.get(row.original.staffMemberId ?? '') ?? '—'}
|
|
348
|
+
</span>
|
|
349
|
+
),
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
accessorKey: 'description',
|
|
353
|
+
header: t('staff.timesheets.projects.detail.colDescription', 'Description'),
|
|
354
|
+
cell: ({ row }) => (
|
|
355
|
+
<span className="flex items-center gap-2">
|
|
356
|
+
<span className="truncate">{row.original.description ?? '—'}</span>
|
|
357
|
+
{row.original.isBillable ? null : (
|
|
358
|
+
<Badge variant="neutral">
|
|
359
|
+
{t('staff.timesheets.projects.detail.nonBillableTag', 'non-billable')}
|
|
360
|
+
</Badge>
|
|
361
|
+
)}
|
|
362
|
+
</span>
|
|
363
|
+
),
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: 'hours',
|
|
367
|
+
header: () => (
|
|
368
|
+
<span className="block text-right">
|
|
369
|
+
{t('staff.timesheets.projects.detail.colHours', 'Hours')}
|
|
370
|
+
</span>
|
|
371
|
+
),
|
|
372
|
+
cell: ({ row }) => (
|
|
373
|
+
<span className="block whitespace-nowrap text-right font-mono tabular-nums">
|
|
374
|
+
{formatHours(row.original.durationMinutes)}
|
|
375
|
+
</span>
|
|
376
|
+
),
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
[employeeNameById, t],
|
|
380
|
+
)
|
|
381
|
+
|
|
333
382
|
// --- Load project ---
|
|
334
383
|
React.useEffect(() => {
|
|
335
384
|
if (!projectId) return
|
|
@@ -1131,49 +1180,12 @@ export default function TimesheetProjectDetailPage({ params }: { params?: { id?:
|
|
|
1131
1180
|
</p>
|
|
1132
1181
|
</div>
|
|
1133
1182
|
) : (
|
|
1134
|
-
<
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
</th>
|
|
1141
|
-
<th className="px-3 py-2 font-medium text-muted-foreground">
|
|
1142
|
-
{t('staff.timesheets.projects.detail.colWho', 'Person')}
|
|
1143
|
-
</th>
|
|
1144
|
-
<th className="px-3 py-2 font-medium text-muted-foreground">
|
|
1145
|
-
{t('staff.timesheets.projects.detail.colDescription', 'Description')}
|
|
1146
|
-
</th>
|
|
1147
|
-
<th className="px-3 py-2 text-right font-medium text-muted-foreground">
|
|
1148
|
-
{t('staff.timesheets.projects.detail.colHours', 'Hours')}
|
|
1149
|
-
</th>
|
|
1150
|
-
</tr>
|
|
1151
|
-
</thead>
|
|
1152
|
-
<tbody>
|
|
1153
|
-
{recentEntries.map((entry) => (
|
|
1154
|
-
<tr key={entry.id} className="border-b last:border-b-0">
|
|
1155
|
-
<td className="whitespace-nowrap px-3 py-2">{entry.date}</td>
|
|
1156
|
-
<td className="whitespace-nowrap px-3 py-2">
|
|
1157
|
-
{employeeNameById.get(entry.staffMemberId ?? '') ?? '—'}
|
|
1158
|
-
</td>
|
|
1159
|
-
<td className="px-3 py-2">
|
|
1160
|
-
<span className="flex items-center gap-2">
|
|
1161
|
-
<span className="truncate">{entry.description ?? '—'}</span>
|
|
1162
|
-
{entry.isBillable ? null : (
|
|
1163
|
-
<Badge variant="neutral">
|
|
1164
|
-
{t('staff.timesheets.projects.detail.nonBillableTag', 'non-billable')}
|
|
1165
|
-
</Badge>
|
|
1166
|
-
)}
|
|
1167
|
-
</span>
|
|
1168
|
-
</td>
|
|
1169
|
-
<td className="whitespace-nowrap px-3 py-2 text-right font-mono tabular-nums">
|
|
1170
|
-
{formatHours(entry.durationMinutes)}
|
|
1171
|
-
</td>
|
|
1172
|
-
</tr>
|
|
1173
|
-
))}
|
|
1174
|
-
</tbody>
|
|
1175
|
-
</table>
|
|
1176
|
-
</div>
|
|
1183
|
+
<DataTable<ProjectEntryRow>
|
|
1184
|
+
extensionTableId={extensionPoints.hosts.projectDetailTimeEntriesTable.tableId}
|
|
1185
|
+
disableRowClick
|
|
1186
|
+
columns={recentEntriesColumns}
|
|
1187
|
+
data={recentEntries}
|
|
1188
|
+
/>
|
|
1177
1189
|
)}
|
|
1178
1190
|
</TabsContent>
|
|
1179
1191
|
|
|
@@ -23,8 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
import * as React from 'react'
|
|
25
25
|
import { useSearchParams } from 'next/navigation'
|
|
26
|
+
import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
26
27
|
import { Download, Lock, LockOpen } from 'lucide-react'
|
|
27
28
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
29
|
+
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
28
30
|
import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
|
|
29
31
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
30
32
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -55,6 +57,7 @@ import {
|
|
|
55
57
|
parseReportSheet,
|
|
56
58
|
type ReportHistoryEvent,
|
|
57
59
|
type ReportSheetPayload,
|
|
60
|
+
type ReportSheetRow,
|
|
58
61
|
} from '../../../../../lib/timesheets-reports/reportSheetData'
|
|
59
62
|
import { formatReportMinutes } from '../../../../../lib/timesheets-reports/reportTotals'
|
|
60
63
|
|
|
@@ -205,6 +208,57 @@ export default function TimeTrackingReportDetailPage({ params }: { params?: { id
|
|
|
205
208
|
[sheet?.report.currencyCode],
|
|
206
209
|
)
|
|
207
210
|
|
|
211
|
+
const lockedEntriesColumns = React.useMemo<ColumnDef<ReportSheetRow>[]>(
|
|
212
|
+
() => [
|
|
213
|
+
{
|
|
214
|
+
accessorKey: 'date',
|
|
215
|
+
header: t('staff.time_tracking.reports.export.date', 'Date'),
|
|
216
|
+
cell: ({ row }) => <span className="text-muted-foreground">{row.original.date}</span>,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
accessorKey: 'taskLabel',
|
|
220
|
+
header: t('staff.time_tracking.reports.export.task', 'Task'),
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
accessorKey: 'personLabel',
|
|
224
|
+
header: t('staff.time_tracking.reports.export.person', 'Person'),
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: 'hours',
|
|
228
|
+
header: () => (
|
|
229
|
+
<span className="block text-right">
|
|
230
|
+
{t('staff.time_tracking.reports.sheet.time', 'Time')}
|
|
231
|
+
</span>
|
|
232
|
+
),
|
|
233
|
+
cell: ({ row }) => (
|
|
234
|
+
<span className="block font-mono tabular-nums text-right">{row.original.hours}</span>
|
|
235
|
+
),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: 'amount',
|
|
239
|
+
header: () => (
|
|
240
|
+
<span className="block text-right">
|
|
241
|
+
{t('staff.time_tracking.reports.sheet.amount', 'Amount')}
|
|
242
|
+
</span>
|
|
243
|
+
),
|
|
244
|
+
cell: ({ row }) => (
|
|
245
|
+
<span className="block font-mono tabular-nums text-right">{money(row.original.amount)}</span>
|
|
246
|
+
),
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: 'lock',
|
|
250
|
+
header: '',
|
|
251
|
+
meta: { maxWidth: '2.5rem' },
|
|
252
|
+
cell: () => (
|
|
253
|
+
<span className="text-muted-foreground">
|
|
254
|
+
<Lock aria-hidden="true" className="size-4" />
|
|
255
|
+
</span>
|
|
256
|
+
),
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
[money, t],
|
|
260
|
+
)
|
|
261
|
+
|
|
208
262
|
const roundingLabel = React.useMemo(() => {
|
|
209
263
|
const unit = sheet?.report.roundingUnitMinutes ?? 0
|
|
210
264
|
if (!unit) return t('staff.time_tracking.reports.rounding.off', 'no rounding')
|
|
@@ -470,44 +524,13 @@ export default function TimeTrackingReportDetailPage({ params }: { params?: { id
|
|
|
470
524
|
.replace('{hours}', formatReportMinutes(sheet.totals.billableMinutes))}
|
|
471
525
|
</span>
|
|
472
526
|
</header>
|
|
473
|
-
<
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
<th className="px-4 py-2 text-left font-medium">
|
|
481
|
-
{t('staff.time_tracking.reports.export.task', 'Task')}
|
|
482
|
-
</th>
|
|
483
|
-
<th className="px-4 py-2 text-left font-medium">
|
|
484
|
-
{t('staff.time_tracking.reports.export.person', 'Person')}
|
|
485
|
-
</th>
|
|
486
|
-
<th className="px-4 py-2 text-right font-medium">
|
|
487
|
-
{t('staff.time_tracking.reports.sheet.time', 'Time')}
|
|
488
|
-
</th>
|
|
489
|
-
<th className="px-4 py-2 text-right font-medium">
|
|
490
|
-
{t('staff.time_tracking.reports.sheet.amount', 'Amount')}
|
|
491
|
-
</th>
|
|
492
|
-
<th className="w-10 px-4 py-2" />
|
|
493
|
-
</tr>
|
|
494
|
-
</thead>
|
|
495
|
-
<tbody>
|
|
496
|
-
{sheet.rows.map((row) => (
|
|
497
|
-
<tr key={row.entryId} className="border-b border-border/60 last:border-b-0">
|
|
498
|
-
<td className="px-4 py-2 text-muted-foreground">{row.date}</td>
|
|
499
|
-
<td className="px-4 py-2">{row.taskLabel}</td>
|
|
500
|
-
<td className="px-4 py-2">{row.personLabel}</td>
|
|
501
|
-
<td className="px-4 py-2 text-right font-mono tabular-nums">{row.hours}</td>
|
|
502
|
-
<td className="px-4 py-2 text-right font-mono tabular-nums">{money(row.amount)}</td>
|
|
503
|
-
<td className="px-4 py-2 text-muted-foreground">
|
|
504
|
-
<Lock aria-hidden="true" className="size-4" />
|
|
505
|
-
</td>
|
|
506
|
-
</tr>
|
|
507
|
-
))}
|
|
508
|
-
</tbody>
|
|
509
|
-
</table>
|
|
510
|
-
</div>
|
|
527
|
+
<DataTable<ReportSheetRow>
|
|
528
|
+
embedded
|
|
529
|
+
extensionTableId={extensionPoints.hosts.reportDetailLockedEntriesTable.tableId}
|
|
530
|
+
disableRowClick
|
|
531
|
+
columns={lockedEntriesColumns}
|
|
532
|
+
data={sheet.rows}
|
|
533
|
+
/>
|
|
511
534
|
{sheet.rowsTruncated ? (
|
|
512
535
|
<p className="border-t border-border p-3 text-xs text-muted-foreground">
|
|
513
536
|
{t(
|
|
@@ -82,6 +82,22 @@ export const extensionPoints = defineModuleExtensionPoints({
|
|
|
82
82
|
tableId: 'staff.time_reports.list',
|
|
83
83
|
source: 'backend/staff/time-tracking/reports/page.tsx',
|
|
84
84
|
}),
|
|
85
|
+
myWorkTodayEntriesTable: dataTableExtensionHost({
|
|
86
|
+
tableId: 'staff.my_work.today_entries.list',
|
|
87
|
+
source: 'backend/staff/time-tracking/page.tsx',
|
|
88
|
+
}),
|
|
89
|
+
projectDetailTimeEntriesTable: dataTableExtensionHost({
|
|
90
|
+
tableId: 'staff.time_project_detail.entries.list',
|
|
91
|
+
source: 'backend/staff/time-tracking/projects/[id]/page.tsx',
|
|
92
|
+
}),
|
|
93
|
+
reportDetailLockedEntriesTable: dataTableExtensionHost({
|
|
94
|
+
tableId: 'staff.time_report_detail.locked_entries.list',
|
|
95
|
+
source: 'backend/staff/time-tracking/reports/[id]/page.tsx',
|
|
96
|
+
}),
|
|
97
|
+
taskBoardListViewTable: dataTableExtensionHost({
|
|
98
|
+
tableId: 'staff.time_task_board.list_view',
|
|
99
|
+
source: 'lib/time-tracking-ui/TaskListView.tsx',
|
|
100
|
+
}),
|
|
85
101
|
|
|
86
102
|
projectForm: crudFormExtensionHost({
|
|
87
103
|
entityId: 'staff.staff_time_project',
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
import * as React from 'react'
|
|
13
13
|
import Link from 'next/link'
|
|
14
|
-
import { useRouter } from 'next/navigation'
|
|
15
14
|
import { ArrowLeft } from 'lucide-react'
|
|
15
|
+
import { replaceWithPageReload } from '@open-mercato/shared/lib/navigation/pageReload'
|
|
16
16
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
17
17
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
18
18
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
@@ -50,7 +50,6 @@ type PortalTimeReportDetail = {
|
|
|
50
50
|
|
|
51
51
|
export default function PortalTimeReportDetailPage({ params }: Props) {
|
|
52
52
|
const t = useT()
|
|
53
|
-
const router = useRouter()
|
|
54
53
|
const { auth } = usePortalContext()
|
|
55
54
|
const { user, loading } = auth
|
|
56
55
|
|
|
@@ -60,8 +59,8 @@ export default function PortalTimeReportDetailPage({ params }: Props) {
|
|
|
60
59
|
const [error, setError] = React.useState<string | null>(null)
|
|
61
60
|
|
|
62
61
|
React.useEffect(() => {
|
|
63
|
-
if (!loading && !user)
|
|
64
|
-
}, [loading, user,
|
|
62
|
+
if (!loading && !user) replaceWithPageReload(`/${params.orgSlug}/portal/login`)
|
|
63
|
+
}, [loading, user, params.orgSlug])
|
|
65
64
|
|
|
66
65
|
const load = React.useCallback(async () => {
|
|
67
66
|
if (!user) return
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import * as React from 'react'
|
|
14
14
|
import Link from 'next/link'
|
|
15
|
-
import {
|
|
15
|
+
import { replaceWithPageReload } from '@open-mercato/shared/lib/navigation/pageReload'
|
|
16
16
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
17
17
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
18
18
|
import { Spinner } from '@open-mercato/ui/primitives/spinner'
|
|
@@ -50,7 +50,6 @@ const PAGE_SIZE = 20
|
|
|
50
50
|
|
|
51
51
|
export default function PortalTimeReportsPage({ params }: Props) {
|
|
52
52
|
const t = useT()
|
|
53
|
-
const router = useRouter()
|
|
54
53
|
const { auth } = usePortalContext()
|
|
55
54
|
const { user, loading } = auth
|
|
56
55
|
|
|
@@ -61,8 +60,8 @@ export default function PortalTimeReportsPage({ params }: Props) {
|
|
|
61
60
|
const [error, setError] = React.useState<string | null>(null)
|
|
62
61
|
|
|
63
62
|
React.useEffect(() => {
|
|
64
|
-
if (!loading && !user)
|
|
65
|
-
}, [loading, user,
|
|
63
|
+
if (!loading && !user) replaceWithPageReload(`/${params.orgSlug}/portal/login`)
|
|
64
|
+
}, [loading, user, params.orgSlug])
|
|
66
65
|
|
|
67
66
|
const load = React.useCallback(async () => {
|
|
68
67
|
if (!user) return
|