From 7fb58ea3417eae1731691795753fb5829c1917e2 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Thu, 6 Jun 2024 16:04:37 +0200 Subject: [PATCH] add is_billable support for create / update project and in timetracker, fixes ST-217 --- .env.example | 2 + .../Api/V1/OrganizationController.php | 2 + .../V1/Organization/OrganizationResource.php | 2 +- .../Resources/V1/Project/ProjectResource.php | 2 + openapi.json.client.ts | 14 ++-- playwright/fixtures.ts | 2 +- .../Components/Common/BillableRateInput.vue | 1 + .../Common/Project/ProjectBillableSelect.vue | 62 ++++++++++++++++ .../Common/Project/ProjectCreateModal.vue | 16 ++-- .../Common/Project/ProjectDropdown.vue | 1 + .../Project/ProjectEditBillableSection.vue | 74 +++++++++++++++++++ .../Common/Project/ProjectEditModal.vue | 13 ++-- .../Common/Project/ProjectTableRow.vue | 17 +++-- .../js/Components/Common/SelectDropdown.vue | 2 +- .../Components/Common/SelectDropdownItem.vue | 7 +- .../TimeTrackerProjectTaskDropdown.vue | 1 + resources/js/Components/TimeTracker.vue | 19 ++++- .../Partials/OrganizationBillableRate.vue | 59 +++++++++++++++ resources/js/Pages/Teams/Show.vue | 6 ++ resources/js/utils/api.ts | 15 ++++ resources/js/utils/useOrganization.ts | 56 ++++++++++++++ resources/js/utils/useProjects.ts | 2 + 22 files changed, 343 insertions(+), 32 deletions(-) create mode 100644 resources/js/Components/Common/Project/ProjectBillableSelect.vue create mode 100644 resources/js/Components/Common/Project/ProjectEditBillableSection.vue create mode 100644 resources/js/Pages/Teams/Partials/OrganizationBillableRate.vue diff --git a/.env.example b/.env.example index d9d1f897..11b68eda 100644 --- a/.env.example +++ b/.env.example @@ -73,3 +73,5 @@ NETWORK_NAME=reverse-proxy-docker-traefik_routing FORWARD_DB_PORT=5432 FORWARD_WEB_PORT=8083 + +PAGINATION_PER_PAGE_DEFAULT=500 diff --git a/app/Http/Controllers/Api/V1/OrganizationController.php b/app/Http/Controllers/Api/V1/OrganizationController.php index 8346e143..9859602b 100644 --- a/app/Http/Controllers/Api/V1/OrganizationController.php +++ b/app/Http/Controllers/Api/V1/OrganizationController.php @@ -14,6 +14,7 @@ class OrganizationController extends Controller /** * Get organization * + * @operationId getOrganization * @throws AuthorizationException */ public function show(Organization $organization): OrganizationResource @@ -26,6 +27,7 @@ class OrganizationController extends Controller /** * Update organization * + * @operationId updateOrganization * @throws AuthorizationException */ public function update(Organization $organization, OrganizationUpdateRequest $request): OrganizationResource diff --git a/app/Http/Resources/V1/Organization/OrganizationResource.php b/app/Http/Resources/V1/Organization/OrganizationResource.php index 071f563a..bce584fc 100644 --- a/app/Http/Resources/V1/Organization/OrganizationResource.php +++ b/app/Http/Resources/V1/Organization/OrganizationResource.php @@ -25,7 +25,7 @@ class OrganizationResource extends BaseResource 'id' => $this->resource->id, /** @var string $name Name */ 'name' => $this->resource->name, - /** @var string $color Personal organizations automatically created after registration */ + /** @var bool $color Personal organizations automatically created after registration */ 'is_personal' => $this->resource->personal_team, /** @var int|null $billable_rate Billable rate in cents per hour */ 'billable_rate' => $this->resource->billable_rate, diff --git a/app/Http/Resources/V1/Project/ProjectResource.php b/app/Http/Resources/V1/Project/ProjectResource.php index 97fa742e..9c7bad4f 100644 --- a/app/Http/Resources/V1/Project/ProjectResource.php +++ b/app/Http/Resources/V1/Project/ProjectResource.php @@ -31,6 +31,8 @@ class ProjectResource extends BaseResource 'client_id' => $this->resource->client_id, /** @var int|null $billable_rate Billable rate in cents per hour */ 'billable_rate' => $this->resource->billable_rate, + /** @var bool $is_billable Project time entries billable default */ + 'is_billable' => $this->resource->is_billable, ]; } } diff --git a/openapi.json.client.ts b/openapi.json.client.ts index b7666707..54116870 100644 --- a/openapi.json.client.ts +++ b/openapi.json.client.ts @@ -52,11 +52,11 @@ const OrganizationResource = z .object({ id: z.string(), name: z.string(), - is_personal: z.string(), + is_personal: z.boolean(), billable_rate: z.union([z.number(), z.null()]), }) .passthrough(); -const v1_organizations_update_Body = z +const updateOrganization_Body = z .object({ name: z.string(), billable_rate: z.union([z.number(), z.null()]).optional(), @@ -69,12 +69,14 @@ const ProjectResource = z color: z.string(), client_id: z.union([z.string(), z.null()]), billable_rate: z.union([z.number(), z.null()]), + is_billable: z.boolean(), }) .passthrough(); const createProject_Body = z .object({ name: z.string(), color: z.string(), + is_billable: z.boolean(), billable_rate: z.union([z.number(), z.null()]).optional(), client_id: z.union([z.string(), z.null()]).optional(), }) @@ -188,7 +190,7 @@ export const schemas = { updateMember_Body, MemberResource, OrganizationResource, - v1_organizations_update_Body, + updateOrganization_Body, ProjectResource, createProject_Body, ProjectMemberResource, @@ -210,7 +212,7 @@ const endpoints = makeApi([ { method: 'get', path: '/v1/organizations/:organization', - alias: 'v1.organizations.show', + alias: 'getOrganization', requestFormat: 'json', parameters: [ { @@ -236,13 +238,13 @@ const endpoints = makeApi([ { method: 'put', path: '/v1/organizations/:organization', - alias: 'v1.organizations.update', + alias: 'updateOrganization', requestFormat: 'json', parameters: [ { name: 'body', type: 'Body', - schema: v1_organizations_update_Body, + schema: updateOrganization_Body, }, { name: 'organization', diff --git a/playwright/fixtures.ts b/playwright/fixtures.ts index 7a46666f..80fadd92 100644 --- a/playwright/fixtures.ts +++ b/playwright/fixtures.ts @@ -10,7 +10,7 @@ export const test = baseTest.extend({ await page.getByLabel('Name').fill('John Doe'); await page .getByLabel('Email') - .fill(`john+${Math.round(Math.random() * 10000)}@doe.com`); + .fill(`john+${Math.round(Math.random() * 1000000)}@doe.com`); await page .getByLabel('Password', { exact: true }) .fill('amazingpassword123'); diff --git a/resources/js/Components/Common/BillableRateInput.vue b/resources/js/Components/Common/BillableRateInput.vue index dbf5371c..aec7e8c6 100644 --- a/resources/js/Components/Common/BillableRateInput.vue +++ b/resources/js/Components/Common/BillableRateInput.vue @@ -75,6 +75,7 @@ const inputValue = ref(formatValue(model.value)); ref="billableRateInput" v-model="inputValue" @blur="updateRate($event.target.value)" + @keydown.enter="updateRate($event.target.value)" type="text" :name="name" placeholder="Billable Rate" diff --git a/resources/js/Components/Common/Project/ProjectBillableSelect.vue b/resources/js/Components/Common/Project/ProjectBillableSelect.vue new file mode 100644 index 00000000..dde42f30 --- /dev/null +++ b/resources/js/Components/Common/Project/ProjectBillableSelect.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/resources/js/Components/Common/Project/ProjectCreateModal.vue b/resources/js/Components/Common/Project/ProjectCreateModal.vue index 08890c4c..2c61fd32 100644 --- a/resources/js/Components/Common/Project/ProjectCreateModal.vue +++ b/resources/js/Components/Common/Project/ProjectCreateModal.vue @@ -13,9 +13,9 @@ import Badge from '@/Components/Common/Badge.vue'; import { useClientsStore } from '@/utils/useClients'; import { storeToRefs } from 'pinia'; import ProjectColorSelector from '@/Components/Common/Project/ProjectColorSelector.vue'; -import BillableRateInput from '@/Components/Common/BillableRateInput.vue'; import { UserCircleIcon } from '@heroicons/vue/20/solid'; import InputLabel from '@/Components/InputLabel.vue'; +import ProjectEditBillableSection from '@/Components/Common/Project/ProjectEditBillableSection.vue'; const { createProject } = useProjectsStore(); const { clients } = storeToRefs(useClientsStore()); @@ -27,6 +27,7 @@ const project = ref({ color: getRandomColor(), client_id: null, billable_rate: null, + is_billable: false, }); async function submit() { @@ -37,6 +38,7 @@ async function submit() { color: getRandomColor(), client_id: null, billable_rate: null, + is_billable: false, }; } @@ -87,12 +89,6 @@ const currentClientName = computed(() => { autocomplete="projectName" /> -
- - -
@@ -112,10 +108,14 @@ const currentClientName = computed(() => {
+