restrict group and subgroup so they are always different

This commit is contained in:
Gregor Vostrak
2024-05-31 03:02:05 +02:00
parent 4943baa236
commit c9311780ed
3 changed files with 78 additions and 48 deletions

View File

@@ -1,50 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { FolderIcon } from '@heroicons/vue/16/solid';
import SelectDropdown from '@/Components/Common/SelectDropdown.vue'; import SelectDropdown from '@/Components/Common/SelectDropdown.vue';
import Badge from '@/Components/Common/Badge.vue'; import Badge from '@/Components/Common/Badge.vue';
import { computed } from 'vue'; import { type Component, computed } from 'vue';
import {
CheckCircleIcon,
UserCircleIcon,
UserGroupIcon,
} from '@heroicons/vue/20/solid';
import BillableIcon from '@/Components/Common/Icons/BillableIcon.vue';
const groupByOptions = [
{
label: 'Members',
value: 'user',
icon: UserGroupIcon,
},
{
label: 'Projects',
value: 'project',
icon: FolderIcon,
},
{
label: 'Tasks',
value: 'task',
icon: CheckCircleIcon,
},
{
label: 'Clients',
value: 'client',
icon: UserCircleIcon,
},
{
label: 'Billable',
value: 'billable',
icon: BillableIcon,
},
];
const model = defineModel<string | null>({ default: null }); const model = defineModel<string | null>({ default: null });
const props = defineProps<{
groupByOptions: { value: string; label: string; icon: Component }[];
}>();
const icon = computed(() => { const icon = computed(() => {
return groupByOptions.find((option) => option.value === model.value)?.icon; return props.groupByOptions.find((option) => option.value === model.value)
?.icon;
}); });
const title = computed(() => { const title = computed(() => {
return groupByOptions.find((option) => option.value === model.value)?.label; return props.groupByOptions.find((option) => option.value === model.value)
?.label;
}); });
</script> </script>

View File

@@ -14,7 +14,7 @@ import ReportingChart from '@/Components/Common/Reporting/ReportingChart.vue';
import BillableIcon from '@/Components/Common/Icons/BillableIcon.vue'; import BillableIcon from '@/Components/Common/Icons/BillableIcon.vue';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { formatHumanReadableDuration, getDayJsInstance } from '@/utils/time'; import { formatHumanReadableDuration, getDayJsInstance } from '@/utils/time';
import { useReportingStore } from '@/utils/useReporting'; import { type GroupingOption, useReportingStore } from '@/utils/useReporting';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import TagDropdown from '@/Components/Common/Tag/TagDropdown.vue'; import TagDropdown from '@/Components/Common/Tag/TagDropdown.vue';
import type { AggregatedTimeEntriesQueryParams } from '@/utils/api'; import type { AggregatedTimeEntriesQueryParams } from '@/utils/api';
@@ -42,11 +42,16 @@ 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';
const group = ref<GroupingOption>('project'); const group = ref<GroupingOption>('project');
const subGroup = ref<GroupingOption>('task'); const subGroup = ref<GroupingOption>('task');
const reportingStore = useReportingStore();
const { aggregatedGraphTimeEntries, aggregatedTableTimeEntries } =
storeToRefs(reportingStore);
const { groupByOptions } = reportingStore;
function getFilterAttributes() { function getFilterAttributes() {
let params: AggregatedTimeEntriesQueryParams = { let params: AggregatedTimeEntriesQueryParams = {
start: getDayJsInstance()(startDate.value).utc().format(), start: getDayJsInstance()(startDate.value).utc().format(),
@@ -90,6 +95,14 @@ function updateGraphReporting() {
function updateTableReporting() { function updateTableReporting() {
const params = getFilterAttributes(); const params = getFilterAttributes();
if (group.value === subGroup.value) {
const fallbackOption = groupByOptions.find(
(el) => el.value !== group.value
);
if (fallbackOption?.value) {
subGroup.value = fallbackOption.value;
}
}
if (getCurrentRole() === 'employee') { if (getCurrentRole() === 'employee') {
params.member_id = getCurrentMembershipId(); params.member_id = getCurrentMembershipId();
} }
@@ -103,11 +116,6 @@ function updateReporting() {
updateTableReporting(); updateTableReporting();
} }
const reportingStore = useReportingStore();
const { aggregatedGraphTimeEntries, aggregatedTableTimeEntries } =
storeToRefs(reportingStore);
function getOptimalGroupingOption(diff: number): 'day' | 'week' | 'month' { function getOptimalGroupingOption(diff: number): 'day' | 'week' | 'month' {
if (diff <= 31) { if (diff <= 31) {
return 'day'; return 'day';
@@ -248,10 +256,16 @@ onMounted(() => {
class="text-sm flex text-white items-center space-x-3 font-medium px-6 border-b border-card-background-separator pb-3"> class="text-sm flex text-white items-center space-x-3 font-medium px-6 border-b border-card-background-separator pb-3">
<span>Group by</span> <span>Group by</span>
<ReportingGroupBySelect <ReportingGroupBySelect
:group-by-options="groupByOptions"
@changed="updateTableReporting" @changed="updateTableReporting"
v-model="group"></ReportingGroupBySelect> v-model="group"></ReportingGroupBySelect>
<span>and</span> <span>and</span>
<ReportingGroupBySelect <ReportingGroupBySelect
:group-by-options="
groupByOptions.filter(
(el) => el.value !== group
)
"
@changed="updateTableReporting" @changed="updateTableReporting"
v-model="subGroup"></ReportingGroupBySelect> v-model="subGroup"></ReportingGroupBySelect>
</div> </div>

View File

@@ -1,6 +1,6 @@
import { defineStore, storeToRefs } from 'pinia'; import { defineStore, storeToRefs } from 'pinia';
import { api } from '../../../openapi.json.client'; import { api } from '../../../openapi.json.client';
import { computed, ref } from 'vue'; import { type Component, computed, ref } from 'vue';
import type { import type {
AggregatedTimeEntries, AggregatedTimeEntries,
AggregatedTimeEntriesQueryParams, AggregatedTimeEntriesQueryParams,
@@ -12,6 +12,20 @@ import { useProjectsStore } from '@/utils/useProjects';
import { useMembersStore } from '@/utils/useMembers'; import { useMembersStore } from '@/utils/useMembers';
import { useTasksStore } from '@/utils/useTasks'; import { useTasksStore } from '@/utils/useTasks';
import { useClientsStore } from '@/utils/useClients'; import { useClientsStore } from '@/utils/useClients';
import {
CheckCircleIcon,
UserCircleIcon,
UserGroupIcon,
} from '@heroicons/vue/20/solid';
import { FolderIcon } from '@heroicons/vue/16/solid';
import BillableIcon from '@/Components/Common/Icons/BillableIcon.vue';
export type GroupingOption =
| 'project'
| 'task'
| 'user'
| 'billable'
| 'client';
export const useReportingStore = defineStore('reporting', () => { export const useReportingStore = defineStore('reporting', () => {
const reportingGraphResponse = ref<ReportingResponse | null>(null); const reportingGraphResponse = ref<ReportingResponse | null>(null);
@@ -112,11 +126,44 @@ export const useReportingStore = defineStore('reporting', () => {
return key; return key;
} }
const groupByOptions: {
label: string;
value: GroupingOption;
icon: Component;
}[] = [
{
label: 'Members',
value: 'user',
icon: UserGroupIcon,
},
{
label: 'Projects',
value: 'project',
icon: FolderIcon,
},
{
label: 'Tasks',
value: 'task',
icon: CheckCircleIcon,
},
{
label: 'Clients',
value: 'client',
icon: UserCircleIcon,
},
{
label: 'Billable',
value: 'billable',
icon: BillableIcon,
},
];
return { return {
aggregatedGraphTimeEntries, aggregatedGraphTimeEntries,
fetchGraphReporting, fetchGraphReporting,
fetchTableReporting, fetchTableReporting,
aggregatedTableTimeEntries, aggregatedTableTimeEntries,
getNameForReportingRowEntry, getNameForReportingRowEntry,
groupByOptions,
}; };
}); });