From f79d995fddcb9215255983d9bee00ad16fc65844 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Tue, 24 Jun 2025 18:10:39 +0200 Subject: [PATCH] make sure empty duration input updates v-model to null, fixes #816 --- e2e/projects.spec.ts | 45 ++++++++++++++++++- .../packages/ui/src/EstimatedTimeSection.vue | 3 +- .../packages/ui/src/Input/DurationInput.vue | 7 +++ .../js/packages/ui/src/Input/TextInput.vue | 2 + 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/e2e/projects.spec.ts b/e2e/projects.spec.ts index 156d66e6..d30ac382 100644 --- a/e2e/projects.spec.ts +++ b/e2e/projects.spec.ts @@ -102,7 +102,7 @@ test('test that updating billable rate works with existing time entries', async await page.getByRole('row').first().getByRole('button').click(); await page.getByRole('menuitem').getByText('Edit').first().click(); - await page.getByText('Non-Billable').click(); + await page.getByText('Non-Billable').click(); await page.getByText('Custom Rate').click(); await page .getByPlaceholder('Billable Rate') @@ -136,6 +136,49 @@ test('test that updating billable rate works with existing time entries', async ).toBeVisible(); }); +test('test that creating and updating project time estimate works', async ({ page }) => { + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); + const timeEstimate = '10'; + + await goToProjectsOverview(page); + await page.getByRole('button', { name: 'Create Project' }).click(); + await page.getByLabel('Project Name').fill(newProjectName); + await page.getByLabel('Time Estimated').fill(timeEstimate); + + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).click(), + page.waitForResponse( + async (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 && + (await response.json()).data.estimated_time === parseInt(timeEstimate) * 60 * 60 + ), + ]); + + // Check that time estimate is displayed in the projects table + await expect(page.getByTestId('project_table')).toContainText(timeEstimate + 'h'); + + // Edit project to remove time estimate + await page.getByRole('row').first().getByRole('button').click(); + await page.getByRole('menuitem').getByText('Edit').first().click(); + await page.getByLabel('Time Estimated').fill(''); + + await Promise.all([ + page.getByRole('button', { name: 'Update Project' }).click(), + page.waitForResponse( + async (response) => + response.url().includes('/projects') && + response.request().method() === 'PUT' && + response.status() === 200 && + (await response.json()).data.estimated_time === null + ), + ]); + + // Check that time estimate is no longer displayed + await expect(page.getByTestId('project_table')).not.toContainText(timeEstimate + 'h'); +}); + // Create new project with new Client // Create new project with existing Client diff --git a/resources/js/packages/ui/src/EstimatedTimeSection.vue b/resources/js/packages/ui/src/EstimatedTimeSection.vue index 1e59dffa..9020c728 100644 --- a/resources/js/packages/ui/src/EstimatedTimeSection.vue +++ b/resources/js/packages/ui/src/EstimatedTimeSection.vue @@ -11,9 +11,10 @@ const emit = defineEmits(['submit']);
- +
diff --git a/resources/js/packages/ui/src/Input/DurationInput.vue b/resources/js/packages/ui/src/Input/DurationInput.vue index 184e303a..efafd53d 100644 --- a/resources/js/packages/ui/src/Input/DurationInput.vue +++ b/resources/js/packages/ui/src/Input/DurationInput.vue @@ -2,6 +2,10 @@ import { computed, ref } from 'vue'; import { TextInput } from '@/packages/ui/src'; +defineProps<{ + id?: string; +}>(); + const model = defineModel({ default: null, }); @@ -16,6 +20,8 @@ function updateDuration() { const hours = parseInt(temporaryCustomTimerEntry.value); if (!isNaN(hours)) { model.value = hours * 60 * 60; + } else { + model.value = null; } temporaryCustomTimerEntry.value = ''; } @@ -54,6 +60,7 @@ function updateAndSubmit() {