From 58fdcaba2c8b88e343673fbb5591826e89060f68 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Mon, 8 Apr 2024 18:41:04 +0200 Subject: [PATCH] add projects detail page, task create / delete --- .eslintrc.cjs | 6 +- e2e/tasks.spec.ts | 109 ++++++++++++++++++ package-lock.json | 31 +++++ package.json | 1 + .../Components/Common/Client/ClientTable.vue | 2 +- .../Common/Client/ClientTableHeading.vue | 28 ++--- .../Common/Client/ClientTableRow.vue | 39 ++++--- .../Components/Common/Member/MemberTable.vue | 2 +- .../Common/Member/MemberTableHeading.vue | 50 ++++---- .../Common/Member/MemberTableRow.vue | 67 ++++++----- .../Common/Project/ProjectCreateModal.vue | 1 + .../Project/ProjectMoreOptionsDropdown.vue | 2 +- .../Common/Project/ProjectTable.vue | 2 +- .../Common/Project/ProjectTableHeading.vue | 42 +++---- .../Common/Project/ProjectTableRow.vue | 97 ++++++++-------- .../js/Components/Common/TableHeading.vue | 10 ++ .../Components/Common/Tag/TagCreateModal.vue | 1 + .../js/Components/Common/Tag/TagTable.vue | 2 +- .../Components/Common/Tag/TagTableHeading.vue | 21 ++-- .../js/Components/Common/Tag/TagTableRow.vue | 27 +++-- .../Common/Task/TaskCreateModal.vue | 74 ++++++++++++ .../Common/Task/TaskMoreOptionsDropdown.vue | 44 +++++++ .../js/Components/Common/Task/TaskTable.vue | 53 +++++++++ .../Common/Task/TaskTableHeading.vue | 20 ++++ .../Components/Common/Task/TaskTableRow.vue | 39 +++++++ resources/js/Components/Dropdown.vue | 4 +- resources/js/Components/TableRow.vue | 24 ++++ resources/js/Pages/ProjectShow.vue | 72 ++++++++++++ resources/js/utils/api.ts | 2 + resources/js/utils/useTasks.ts | 4 +- routes/web.php | 4 + vite-module-loader.js | 82 +++++++------ 32 files changed, 740 insertions(+), 222 deletions(-) create mode 100644 e2e/tasks.spec.ts create mode 100644 resources/js/Components/Common/TableHeading.vue create mode 100644 resources/js/Components/Common/Task/TaskCreateModal.vue create mode 100644 resources/js/Components/Common/Task/TaskMoreOptionsDropdown.vue create mode 100644 resources/js/Components/Common/Task/TaskTable.vue create mode 100644 resources/js/Components/Common/Task/TaskTableHeading.vue create mode 100644 resources/js/Components/Common/Task/TaskTableRow.vue create mode 100644 resources/js/Components/TableRow.vue create mode 100644 resources/js/Pages/ProjectShow.vue diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2bb9b68b..f7297ff6 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,5 +5,9 @@ module.exports = { extends: ['plugin:vue/vue3-essential', '@vue/eslint-config-typescript/recommended', '@vue/eslint-config-prettier'], rules: { 'vue/multi-word-component-names': 'off', - } + "@typescript-eslint/no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": "error", + }, + plugins: ['unused-imports'], } diff --git a/e2e/tasks.spec.ts b/e2e/tasks.spec.ts new file mode 100644 index 00000000..4a8196b4 --- /dev/null +++ b/e2e/tasks.spec.ts @@ -0,0 +1,109 @@ +import { expect, Page } from '@playwright/test'; +import { PLAYWRIGHT_BASE_URL } from '../playwright/config'; +import { test } from '../playwright/fixtures'; + +async function goToProjectsOverview(page: Page) { + await page.goto(PLAYWRIGHT_BASE_URL + '/projects'); +} + +// Create new project via modal +test('test that creating and deleting a new tag in a new project works', async ({ + page, +}) => { + const newProjectName = + 'New Project ' + Math.floor(1 + Math.random() * 10000); + await goToProjectsOverview(page); + await page.getByRole('button', { name: 'Create Project' }).click(); + await page.getByPlaceholder('Project Name').fill(newProjectName); + await Promise.all([ + page.getByRole('button', { name: 'Create Project' }).nth(1).click(), + page.waitForResponse( + async (response) => + response.url().includes('/projects') && + response.request().method() === 'POST' && + response.status() === 201 && + (await response.json()).data.id !== null && + (await response.json()).data.color !== null && + (await response.json()).data.client_id === null && + (await response.json()).data.name === newProjectName + ), + ]); + + await expect(page.getByTestId('project_table')).toContainText( + newProjectName + ); + + await page.getByText(newProjectName).click(); + + const newTaskName = 'New Project ' + Math.floor(1 + Math.random() * 10000); + + await page.getByRole('button', { name: 'Create Task' }).click(); + await page.getByPlaceholder('Task Name').fill(newTaskName); + + await Promise.all([ + page.getByRole('button', { name: 'Create Task' }).nth(1).click(), + page.waitForResponse( + async (response) => + response.url().includes('/tasks') && + response.request().method() === 'POST' && + response.status() === 201 && + (await response.json()).data.id !== null && + (await response.json()).data.project_id !== null && + (await response.json()).data.name === newTaskName + ), + ]); + + await expect(page.getByTestId('task_table')).toContainText(newTaskName); + + const taskMoreButton = page.locator( + "[aria-label='Actions for Task " + newTaskName + "']" + ); + taskMoreButton.click(); + const taskDeleteButton = page.locator( + "[aria-label='Delete Task " + newTaskName + "']" + ); + + await Promise.all([ + taskDeleteButton.click(), + page.waitForResponse( + async (response) => + response.url().includes('/tasks') && + response.request().method() === 'DELETE' && + response.status() === 204 + ), + ]); + await expect(page.getByTestId('task_table')).not.toContainText(newTaskName); + + await goToProjectsOverview(page); + + const moreButton = page.locator( + "[aria-label='Actions for Project " + newProjectName + "']" + ); + moreButton.click(); + const deleteButton = page.locator( + "[aria-label='Delete Project " + newProjectName + "']" + ); + + await Promise.all([ + deleteButton.click(), + page.waitForResponse( + async (response) => + response.url().includes('/projects') && + response.request().method() === 'DELETE' && + response.status() === 204 + ), + ]); + await expect(page.getByTestId('project_table')).not.toContainText( + newProjectName + ); +}); + +// Create new project with new Client + +// Create new project with existing Client + +// Delete project via More Options + +// Test that project task count is displayed correctly + +// Test that active / archive / all filter works (once implemented) diff --git a/package-lock.json b/package-lock.json index 4402dff0..f063c924 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,6 +31,7 @@ "@vue/tsconfig": "^0.5.1", "autoprefixer": "^10.4.7", "axios": "^1.6.4", + "eslint-plugin-unused-imports": "^3.1.0", "laravel-vite-plugin": "^1.0.0", "openapi-zod-client": "^1.16.2", "postcss": "^8.4.14", @@ -2859,6 +2860,27 @@ } } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.1.0.tgz", + "integrity": "sha512-9l1YFCzXKkw1qtAru1RWUtG2EVDZY0a0eChKXcL+EZ5jitG7qxdctu4RnvhOJHv4xfmUf7h+JJPINlVpGhZMrw==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "6 - 7", + "eslint": "8" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, "node_modules/eslint-plugin-vue": { "version": "9.24.0", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz", @@ -2894,6 +2916,15 @@ "node": ">=4" } }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", diff --git a/package.json b/package.json index 3a45efa1..95b4798e 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@vue/tsconfig": "^0.5.1", "autoprefixer": "^10.4.7", "axios": "^1.6.4", + "eslint-plugin-unused-imports": "^3.1.0", "laravel-vite-plugin": "^1.0.0", "openapi-zod-client": "^1.16.2", "postcss": "^8.4.14", diff --git a/resources/js/Components/Common/Client/ClientTable.vue b/resources/js/Components/Common/Client/ClientTable.vue index 374d61d4..25ba9578 100644 --- a/resources/js/Components/Common/Client/ClientTable.vue +++ b/resources/js/Components/Common/Client/ClientTable.vue @@ -20,7 +20,7 @@ const createClient = ref(false);
+ diff --git a/resources/js/Components/Common/Client/ClientTableRow.vue b/resources/js/Components/Common/Client/ClientTableRow.vue index 8b0fe700..0e3a76f6 100644 --- a/resources/js/Components/Common/Client/ClientTableRow.vue +++ b/resources/js/Components/Common/Client/ClientTableRow.vue @@ -6,6 +6,7 @@ import { useClientsStore } from '@/utils/useClients'; import { storeToRefs } from 'pinia'; import ClientMoreOptionsDropdown from '@/Components/Common/Client/ClientMoreOptionsDropdown.vue'; import { useProjectsStore } from '@/utils/useProjects'; +import TableRow from '@/Components/TableRow.vue'; const { projects } = storeToRefs(useProjectsStore()); @@ -25,24 +26,26 @@ const projectCount = computed(() => { diff --git a/resources/js/Components/Common/Member/MemberTable.vue b/resources/js/Components/Common/Member/MemberTable.vue index 17944131..f316582e 100644 --- a/resources/js/Components/Common/Member/MemberTable.vue +++ b/resources/js/Components/Common/Member/MemberTable.vue @@ -16,7 +16,7 @@ const createClient = ref(false);