mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-18 21:22:15 +01:00
add client_ids to reporting filters
This commit is contained in:
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace App\Http\Requests\V1\TimeEntry;
|
namespace App\Http\Requests\V1\TimeEntry;
|
||||||
|
|
||||||
use App\Enums\TimeEntryAggregationType;
|
use App\Enums\TimeEntryAggregationType;
|
||||||
|
use App\Models\Client;
|
||||||
use App\Models\Member;
|
use App\Models\Member;
|
||||||
use App\Models\Organization;
|
use App\Models\Organization;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
@@ -86,6 +87,19 @@ class TimeEntryAggregateRequest extends FormRequest
|
|||||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
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
|
// Filter by tag IDs, tag IDs are AND combined
|
||||||
'tag_ids' => [
|
'tag_ids' => [
|
||||||
'array',
|
'array',
|
||||||
|
|||||||
@@ -165,15 +165,16 @@ const v1_time_entries_update_multiple_Body = z
|
|||||||
.passthrough();
|
.passthrough();
|
||||||
const updateTimeEntry_Body = z
|
const updateTimeEntry_Body = z
|
||||||
.object({
|
.object({
|
||||||
member_id: z.string().uuid().optional(),
|
member_id: z.string().uuid(),
|
||||||
project_id: z.union([z.string(), z.null()]).optional(),
|
project_id: z.union([z.string(), z.null()]),
|
||||||
task_id: z.union([z.string(), z.null()]).optional(),
|
task_id: z.union([z.string(), z.null()]),
|
||||||
start: z.string(),
|
start: z.string(),
|
||||||
end: z.union([z.string(), z.null()]).optional(),
|
end: z.union([z.string(), z.null()]),
|
||||||
billable: z.boolean().optional(),
|
billable: z.boolean(),
|
||||||
description: z.union([z.string(), z.null()]).optional(),
|
description: z.union([z.string(), z.null()]),
|
||||||
tags: z.union([z.array(z.string()), z.null()]).optional(),
|
tags: z.union([z.array(z.string()), z.null()]),
|
||||||
})
|
})
|
||||||
|
.partial()
|
||||||
.passthrough();
|
.passthrough();
|
||||||
|
|
||||||
export const schemas = {
|
export const schemas = {
|
||||||
@@ -2114,6 +2115,11 @@ If the group parameters are all set to `null` or are all missing, the
|
|||||||
type: 'Query',
|
type: 'Query',
|
||||||
schema: z.array(z.string()).min(1).optional(),
|
schema: z.array(z.string()).min(1).optional(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'client_ids',
|
||||||
|
type: 'Query',
|
||||||
|
schema: z.array(z.string()).min(1).optional(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'tag_ids',
|
name: 'tag_ids',
|
||||||
type: 'Query',
|
type: 'Query',
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -28,6 +28,7 @@ import ReportingRow from '@/Components/Common/Reporting/ReportingRow.vue';
|
|||||||
import { formatCents } from '@/utils/money';
|
import { formatCents } from '@/utils/money';
|
||||||
import ReportingPieChart from '@/Components/Common/Reporting/ReportingPieChart.vue';
|
import ReportingPieChart from '@/Components/Common/Reporting/ReportingPieChart.vue';
|
||||||
import { getCurrentMembershipId, getCurrentRole } from '@/utils/useUser';
|
import { getCurrentMembershipId, getCurrentRole } from '@/utils/useUser';
|
||||||
|
import ClientMultiselectDropdown from '@/Components/Common/Client/ClientMultiselectDropdown.vue';
|
||||||
|
|
||||||
const startDate = ref<string | null>(
|
const startDate = ref<string | null>(
|
||||||
getDayJsInstance()().subtract(14, 'd').format('YYYY-MM-DD')
|
getDayJsInstance()().subtract(14, 'd').format('YYYY-MM-DD')
|
||||||
@@ -37,6 +38,8 @@ const selectedTags = ref<string[]>([]);
|
|||||||
const selectedProjects = ref<string[]>([]);
|
const selectedProjects = ref<string[]>([]);
|
||||||
const selectedMembers = ref<string[]>([]);
|
const selectedMembers = ref<string[]>([]);
|
||||||
const selectedTasks = ref<string[]>([]);
|
const selectedTasks = ref<string[]>([]);
|
||||||
|
const selectedClients = ref<string[]>([]);
|
||||||
|
|
||||||
const billable = ref<'true' | 'false' | null>(null);
|
const billable = ref<'true' | 'false' | null>(null);
|
||||||
|
|
||||||
type GroupingOption = 'project' | 'task' | 'user' | 'billable' | 'client';
|
type GroupingOption = 'project' | 'task' | 'user' | 'billable' | 'client';
|
||||||
@@ -49,36 +52,25 @@ function getFilterAttributes() {
|
|||||||
start: getDayJsInstance()(startDate.value).utc().format(),
|
start: getDayJsInstance()(startDate.value).utc().format(),
|
||||||
end: getDayJsInstance()(endDate.value).endOf('day').utc().format(),
|
end: getDayJsInstance()(endDate.value).endOf('day').utc().format(),
|
||||||
};
|
};
|
||||||
if (selectedMembers.value.length > 0) {
|
params = {
|
||||||
params = {
|
...params,
|
||||||
...params,
|
member_ids:
|
||||||
member_ids: selectedMembers.value,
|
selectedMembers.value.length > 0
|
||||||
};
|
? selectedMembers.value
|
||||||
}
|
: undefined,
|
||||||
if (selectedProjects.value.length > 0) {
|
project_ids:
|
||||||
params = {
|
selectedProjects.value.length > 0
|
||||||
...params,
|
? selectedProjects.value
|
||||||
project_ids: selectedProjects.value,
|
: undefined,
|
||||||
};
|
task_ids:
|
||||||
}
|
selectedTasks.value.length > 0 ? selectedTasks.value : undefined,
|
||||||
if (selectedTasks.value.length > 0) {
|
client_ids:
|
||||||
params = {
|
selectedClients.value.length > 0
|
||||||
...params,
|
? selectedClients.value
|
||||||
task_ids: selectedTasks.value,
|
: undefined,
|
||||||
};
|
tag_ids: selectedTags.value.length > 0 ? selectedTags.value : undefined,
|
||||||
}
|
billable: billable.value !== null ? billable.value : undefined,
|
||||||
if (selectedTags.value.length > 0) {
|
};
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
tag_ids: selectedTags.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (billable.value !== null) {
|
|
||||||
params = {
|
|
||||||
...params,
|
|
||||||
billable: billable.value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +171,15 @@ onMounted(() => {
|
|||||||
:icon="CheckCircleIcon"></ReportingFilterBadge>
|
:icon="CheckCircleIcon"></ReportingFilterBadge>
|
||||||
</template>
|
</template>
|
||||||
</TaskMultiselectDropdown>
|
</TaskMultiselectDropdown>
|
||||||
|
<ClientMultiselectDropdown
|
||||||
|
@submit="updateReporting"
|
||||||
|
v-model="selectedClients">
|
||||||
|
<template v-slot:trigger>
|
||||||
|
<ReportingFilterBadge
|
||||||
|
title="Clients"
|
||||||
|
:icon="FolderIcon"></ReportingFilterBadge>
|
||||||
|
</template>
|
||||||
|
</ClientMultiselectDropdown>
|
||||||
<TagDropdown
|
<TagDropdown
|
||||||
@submit="updateReporting"
|
@submit="updateReporting"
|
||||||
v-model="selectedTags">
|
v-model="selectedTags">
|
||||||
|
|||||||
Reference in New Issue
Block a user