add client_ids to reporting filters

This commit is contained in:
Gregor Vostrak
2024-05-31 02:04:45 +02:00
parent 4e439010d1
commit 4c977b5bf8
4 changed files with 87 additions and 37 deletions

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Http\Requests\V1\TimeEntry;
use App\Enums\TimeEntryAggregationType;
use App\Models\Client;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Project;
@@ -86,6 +87,19 @@ class TimeEntryAggregateRequest extends FormRequest
return $builder->whereBelongsTo($this->organization, 'organization');
}),
],
// Filter by client IDs, client IDs are OR combined
'client_ids' => [
'array',
'min:1',
],
'client_ids.*' => [
'string',
'uuid',
new ExistsEloquent(Client::class, null, function (Builder $builder): Builder {
/** @var Builder<Client> $builder */
return $builder->whereBelongsTo($this->organization, 'organization');
}),
],
// Filter by tag IDs, tag IDs are AND combined
'tag_ids' => [
'array',

View File

@@ -165,15 +165,16 @@ const v1_time_entries_update_multiple_Body = z
.passthrough();
const updateTimeEntry_Body = z
.object({
member_id: z.string().uuid().optional(),
project_id: z.union([z.string(), z.null()]).optional(),
task_id: z.union([z.string(), z.null()]).optional(),
member_id: z.string().uuid(),
project_id: z.union([z.string(), z.null()]),
task_id: z.union([z.string(), z.null()]),
start: z.string(),
end: z.union([z.string(), z.null()]).optional(),
billable: z.boolean().optional(),
description: z.union([z.string(), z.null()]).optional(),
tags: z.union([z.array(z.string()), z.null()]).optional(),
end: z.union([z.string(), z.null()]),
billable: z.boolean(),
description: z.union([z.string(), z.null()]),
tags: z.union([z.array(z.string()), z.null()]),
})
.partial()
.passthrough();
export const schemas = {
@@ -2114,6 +2115,11 @@ If the group parameters are all set to &#x60;null&#x60; or are all missing, the
type: 'Query',
schema: z.array(z.string()).min(1).optional(),
},
{
name: 'client_ids',
type: 'Query',
schema: z.array(z.string()).min(1).optional(),
},
{
name: 'tag_ids',
type: 'Query',

View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
import MultiselectDropdown from '@/Components/Common/MultiselectDropdown.vue';
import { storeToRefs } from 'pinia';
import type { Client } from '@/utils/api';
import { useClientsStore } from '@/utils/useClients';
const clientsStore = useClientsStore();
const { clients } = storeToRefs(clientsStore);
function getKeyFromItem(item: Client) {
return item.id;
}
function getNameForItem(item: Client) {
return item.name;
}
</script>
<template>
<MultiselectDropdown
searchPlaceholder="Search for a Client..."
:items="clients"
:get-key-from-item="getKeyFromItem"
:get-name-for-item="getNameForItem">
<template #trigger>
<slot name="trigger"></slot>
</template>
</MultiselectDropdown>
</template>

View File

@@ -28,6 +28,7 @@ import ReportingRow from '@/Components/Common/Reporting/ReportingRow.vue';
import { formatCents } from '@/utils/money';
import ReportingPieChart from '@/Components/Common/Reporting/ReportingPieChart.vue';
import { getCurrentMembershipId, getCurrentRole } from '@/utils/useUser';
import ClientMultiselectDropdown from '@/Components/Common/Client/ClientMultiselectDropdown.vue';
const startDate = ref<string | null>(
getDayJsInstance()().subtract(14, 'd').format('YYYY-MM-DD')
@@ -37,6 +38,8 @@ const selectedTags = ref<string[]>([]);
const selectedProjects = ref<string[]>([]);
const selectedMembers = ref<string[]>([]);
const selectedTasks = ref<string[]>([]);
const selectedClients = ref<string[]>([]);
const billable = ref<'true' | 'false' | null>(null);
type GroupingOption = 'project' | 'task' | 'user' | 'billable' | 'client';
@@ -49,36 +52,25 @@ function getFilterAttributes() {
start: getDayJsInstance()(startDate.value).utc().format(),
end: getDayJsInstance()(endDate.value).endOf('day').utc().format(),
};
if (selectedMembers.value.length > 0) {
params = {
...params,
member_ids: selectedMembers.value,
};
}
if (selectedProjects.value.length > 0) {
params = {
...params,
project_ids: selectedProjects.value,
};
}
if (selectedTasks.value.length > 0) {
params = {
...params,
task_ids: selectedTasks.value,
};
}
if (selectedTags.value.length > 0) {
params = {
...params,
tag_ids: selectedTags.value,
};
}
if (billable.value !== null) {
params = {
...params,
billable: billable.value,
};
}
params = {
...params,
member_ids:
selectedMembers.value.length > 0
? selectedMembers.value
: undefined,
project_ids:
selectedProjects.value.length > 0
? selectedProjects.value
: undefined,
task_ids:
selectedTasks.value.length > 0 ? selectedTasks.value : undefined,
client_ids:
selectedClients.value.length > 0
? selectedClients.value
: undefined,
tag_ids: selectedTags.value.length > 0 ? selectedTags.value : undefined,
billable: billable.value !== null ? billable.value : undefined,
};
return params;
}
@@ -179,6 +171,15 @@ onMounted(() => {
:icon="CheckCircleIcon"></ReportingFilterBadge>
</template>
</TaskMultiselectDropdown>
<ClientMultiselectDropdown
@submit="updateReporting"
v-model="selectedClients">
<template v-slot:trigger>
<ReportingFilterBadge
title="Clients"
:icon="FolderIcon"></ReportingFilterBadge>
</template>
</ClientMultiselectDropdown>
<TagDropdown
@submit="updateReporting"
v-model="selectedTags">