From 81d9561656d11a8f2f0e778077c816b894f93c33 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Wed, 14 Jan 2026 17:01:45 +0100 Subject: [PATCH] refactor timeentries queries and mutations, improve activitygraph, add dashboard reporting table --- .../Controllers/Api/V1/ChartController.php | 2 +- e2e/clients.spec.ts | 2 +- e2e/project-members.spec.ts | 12 +- e2e/projects.spec.ts | 38 ++- .../Dashboard/ActivityGraphCard.vue | 67 ++++- .../js/Components/Dashboard/DashboardCard.vue | 6 +- .../Dashboard/DayOverviewCardEntry.vue | 7 +- .../Dashboard/RecentlyTrackedTasksCard.vue | 2 +- .../RecentlyTrackedTasksCardEntry.vue | 6 +- .../Dashboard/TeamActivityCardEntry.vue | 4 +- .../Components/Dashboard/ThisWeekOverview.vue | 10 + .../Dashboard/ThisWeekReportingTable.vue | 194 ++++++++++++++ resources/js/Components/NavLink.vue | 2 +- resources/js/Components/TimeTracker.vue | 39 +-- resources/js/Layouts/AppLayout.vue | 2 - resources/js/Pages/Calendar.vue | 91 +++---- resources/js/Pages/ReportingDetailed.vue | 40 +-- resources/js/Pages/Time.vue | 43 ++- resources/js/packages/ui/src/CardTitle.vue | 7 +- .../ui/src/TimeEntry/TimeEntryRowHeading.vue | 2 +- .../src/TimeTracker/TimeTrackerControls.vue | 2 +- .../TimeTracker/TimeTrackerRangeSelector.vue | 2 +- resources/js/packages/ui/styles.css | 14 +- resources/js/utils/init.ts | 2 - resources/js/utils/prefetch.ts | 25 +- resources/js/utils/useCurrentTimeEntry.ts | 5 +- resources/js/utils/useTimeEntries.ts | 246 ------------------ .../js/utils/useTimeEntriesCalendarQuery.ts | 65 +++++ .../js/utils/useTimeEntriesInfiniteQuery.ts | 51 ++++ resources/js/utils/useTimeEntriesMutations.ts | 151 +++++++++++ .../js/utils/useTimeEntriesReportQuery.ts | 21 ++ 31 files changed, 723 insertions(+), 437 deletions(-) create mode 100644 resources/js/Components/Dashboard/ThisWeekReportingTable.vue delete mode 100644 resources/js/utils/useTimeEntries.ts create mode 100644 resources/js/utils/useTimeEntriesCalendarQuery.ts create mode 100644 resources/js/utils/useTimeEntriesInfiniteQuery.ts create mode 100644 resources/js/utils/useTimeEntriesMutations.ts create mode 100644 resources/js/utils/useTimeEntriesReportQuery.ts diff --git a/app/Http/Controllers/Api/V1/ChartController.php b/app/Http/Controllers/Api/V1/ChartController.php index 3e034df9..167b9ed1 100644 --- a/app/Http/Controllers/Api/V1/ChartController.php +++ b/app/Http/Controllers/Api/V1/ChartController.php @@ -102,7 +102,7 @@ class ChartController extends Controller $this->checkPermission($organization, 'charts:view:own'); $user = $this->user(); - $dailyTrackedHours = $dashboardService->getDailyTrackedHours($user, $organization, 60); + $dailyTrackedHours = $dashboardService->getDailyTrackedHours($user, $organization, 100); return response()->json($dailyTrackedHours); } diff --git a/e2e/clients.spec.ts b/e2e/clients.spec.ts index dc6ed8cc..280f081b 100644 --- a/e2e/clients.spec.ts +++ b/e2e/clients.spec.ts @@ -27,7 +27,7 @@ test('test that creating and deleting a new client via the modal works', async ( await expect(page.getByTestId('client_table')).toContainText(newClientName); const moreButton = page.locator("[aria-label='Actions for Client " + newClientName + "']"); - moreButton.click(); + await moreButton.click(); const deleteButton = page.locator("[aria-label='Delete Client " + newClientName + "']"); await Promise.all([ diff --git a/e2e/project-members.spec.ts b/e2e/project-members.spec.ts index 958014ac..555112e9 100644 --- a/e2e/project-members.spec.ts +++ b/e2e/project-members.spec.ts @@ -17,8 +17,16 @@ test('test that updating project member billable rate works for existing time en await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); - await page.getByRole('button', { name: 'Create Project' }).click(); - await expect(page.getByText(newProjectName)).toBeVisible(); + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).click(), + page.waitForResponse( + (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 + ), + ]); + await expect(page.getByText(newProjectName)).toBeVisible({ timeout: 10000 }); await page.getByText(newProjectName).click(); await page.getByRole('button', { name: 'Add Member' }).click(); diff --git a/e2e/projects.spec.ts b/e2e/projects.spec.ts index 1c953a95..7b5e1bfc 100644 --- a/e2e/projects.spec.ts +++ b/e2e/projects.spec.ts @@ -38,7 +38,7 @@ test('test that creating and deleting a new project via the modal works', async await expect(page.getByTestId('project_table')).toContainText(newProjectName); const moreButton = page.locator("[aria-label='Actions for Project " + newProjectName + "']"); - moreButton.click(); + await moreButton.click(); const deleteButton = page.locator("[aria-label='Delete Project " + newProjectName + "']"); await Promise.all([ @@ -79,8 +79,16 @@ test('test that archiving and unarchiving projects works', async ({ page }) => { await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); - await page.getByRole('button', { name: 'Create Project' }).click(); - await expect(page.getByText(newProjectName)).toBeVisible(); + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).click(), + page.waitForResponse( + (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 + ), + ]); + await expect(page.getByText(newProjectName)).toBeVisible({ timeout: 10000 }); // Archive the project await page.getByRole('row').first().getByRole('button').click(); @@ -118,8 +126,16 @@ test('test that updating billable rate works with existing time entries', async await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); - await page.getByRole('button', { name: 'Create Project' }).click(); - await expect(page.getByText(newProjectName)).toBeVisible(); + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).click(), + page.waitForResponse( + (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 + ), + ]); + await expect(page.getByText(newProjectName)).toBeVisible({ timeout: 10000 }); await page.getByRole('row').first().getByRole('button').click(); await page.getByRole('menuitem').getByText('Edit').first().click(); @@ -224,8 +240,16 @@ test('test that filtering projects by status works', async ({ page }) => { // Create a new project await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); - await page.getByRole('button', { name: 'Create Project' }).click(); - await expect(page.getByText(newProjectName)).toBeVisible(); + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).click(), + page.waitForResponse( + (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 + ), + ]); + await expect(page.getByText(newProjectName)).toBeVisible({ timeout: 10000 }); // Archive the project await page.getByRole('row').first().getByRole('button').click(); diff --git a/resources/js/Components/Dashboard/ActivityGraphCard.vue b/resources/js/Components/Dashboard/ActivityGraphCard.vue index 7871983b..19ad0471 100644 --- a/resources/js/Components/Dashboard/ActivityGraphCard.vue +++ b/resources/js/Components/Dashboard/ActivityGraphCard.vue @@ -1,7 +1,8 @@