| 1 | import type { Metadata } from 'next' |
| 2 | import { headers } |
| 3 | import { redirect } |
| 4 | import { resolveProjectContext } |
| 5 | import { projectsService } |
| 6 | import { requirementsService } |
| 7 | import { teamService } |
| 8 | import { timesheetService } |
| 9 | |
| 10 | import { createMetadata } |
| 11 | import { TimeTrackingClient } |
| 12 | import type { ClientReportWithEntries |
| 13 | |
| 14 | const LOG_MINUTES_RE = / |
| 15 | |
| 16 | export const metadata: |
| 17 | title: 'Timesheets', |
| 18 | description: 'Log hours and track team productivity across the project.', |
| 19 | openGraph: { |
| 20 | images: ['/api/og?page=Timesheets'], |
| 21 | }, |
| 22 | twitter: { |
| 23 | images: ['/api/og?page=Timesheets'], |
| 24 | }, |
| 25 | }) |
| 26 | |
| 27 | export default async function |
| 28 | params, |
| 29 | searchParams, |
| 30 | }: PageProps< |
| 31 | const { org, |
| 32 | const { logMinutes |
| 33 | let initialLogMinutes: |
| 34 | if (typeof logMinutes |
| 35 | const parsed = |
| 36 | if (Number.isSafeInteger |
| 37 | initialLogMinutes = Math. |
| 38 | } |
| 39 | } |
| 40 | const { |
| 41 | organization, |
| 42 | project: currentProject, |
| 43 | orgMember, |
| 44 | role, |
| 45 | } = await resolveProjectContext |
| 46 | |
| 47 | const isClient = |
| 48 | const isAdmin = |
| 49 | |
| 50 | const canReadTimeEntries = |
| 51 | const canReadReports = |
| 52 | timesheet_report: ['read'], |
| 53 | }).success |
| 54 | |
| 55 | if (!(canReadTimeEntries |
| 56 | redirect( |
| 57 | `/error/403?message=${encodeURIComponent('You do not have permission to view timesheets |
| 58 | ) |
| 59 | } |
| 60 | |
| 61 | const h = |
| 62 | |
| 63 | const [ |
| 64 | entries, |
| 65 | requirementsList, |
| 66 | projectMembers, |
| 67 | budgetStatus, |
| 68 | rates, |
| 69 | reports, |
| 70 | clients, |
| 71 | settings, |
| 72 | projectCustomFields, |
| 73 | ] = await Promise |
| 74 | canReadTimeEntries |
| 75 | ? timesheetService.listByProject( |
| 76 | : Promise.resolve([]) |
| 77 | requirementsService.listByProject(currentProject |
| 78 | teamService.getProjectMembers(currentProject |
| 79 | timesheetService.getProjectBudgetStatus(currentProject |
| 80 | isAdmin |
| 81 | ? timesheetService.getMemberRates( |
| 82 | : Promise.resolve([]) |
| 83 | isAdmin |
| 84 | ? timesheetService.listReportsByProject( |
| 85 | : Promise.resolve([]) |
| 86 | isAdmin |
| 87 | ? teamService.getProjectClients( |
| 88 | : Promise.resolve([]) |
| 89 | projectsService.getSettings(organization |
| 90 | timesheetService.getProjectCustomFields(currentProject |
| 91 | ]) |
| 92 | |
| 93 | const reportIds = |
| 94 | |
| 95 | const [reportEntriesMap, |
| 96 | isAdmin && reportIds. |
| 97 | ? timesheetService.getReportEntriesBatch( |
| 98 | : Promise.resolve( |
| 99 | isAdmin && reportIds. |
| 100 | ? timesheetService.getReportRecipientsBatch( |
| 101 | : Promise.resolve( |
| 102 | ]) |
| 103 | |
| 104 | let clientReports: |
| 105 | |
| 106 | if (isClient) { |
| 107 | const rawReports = |
| 108 | orgMember.id, |
| 109 | currentProject.id |
| 110 | ) |
| 111 | const clientReportIds = |
| 112 | const entriesMap = |
| 113 | await timesheetService.getReportEntriesBatch |
| 114 | clientReports = rawReports. |
| 115 | report, |
| 116 | entries: entriesMap[report |
| 117 | })) |
| 118 | } |
| 119 | return ( |
| 120 | <TimeTrackingClient |
| 121 | budgetStatus={budgetStatus} |
| 122 | clientReports={clientReports} |
| 123 | clients={clients} |
| 124 | currentMemberId={orgMember.id |
| 125 | customFields={projectCustomFields} |
| 126 | defaultCurrency={settings.currency |
| 127 | entries={entries} |
| 128 | initialLogMinutes={initialLogMinutes} |
| 129 | isAdmin={isAdmin} |
| 130 | isClient={isClient} |
| 131 | isClientInvolved={settings.clientInvolvement |
| 132 | memberRates={rates} |
| 133 | orgSlug={org} |
| 134 | projectId={currentProject.id |
| 135 | projectMembers={projectMembers.map |
| 136 | id: m.memberId |
| 137 | name: m.userName |
| 138 | email: m.userEmail |
| 139 | }))} |
| 140 | projectName={currentProject.name |
| 141 | projectSlug={projectSlug} |
| 142 | reportEntriesMap={reportEntriesMap} |
| 143 | reportRecipientsMap={reportRecipientsMap} |
| 144 | requirements={requirementsList} |
| 145 | timesheetDuration={settings.timesheetDuration |
| 146 | timesheetReports={reports} |
| 147 | /> |
| 148 | ) |
| 149 | } |