add e2e tests for employee restrictions

This commit is contained in:
Gregor Vostrak
2026-02-10 14:41:04 +01:00
parent d06b0633d3
commit 6c319fafbc
21 changed files with 1211 additions and 37 deletions

View File

@@ -67,6 +67,12 @@ const { groupByOptions, getNameForReportingRowEntry, emptyPlaceholder } = report
const organization = inject<ComputedRef<Organization>>('organization');
const showBillableRate = computed(() => {
return !!(
getCurrentRole() !== 'employee' || organization?.value?.employees_can_see_billable_rates
);
});
// Ensure sub-group falls back when it collides with group
watch(
group,
@@ -280,12 +286,14 @@ const tableData = computed(() => {
groupByOptions.filter((el) => el.value !== group)
"></ReportingGroupBySelect>
</div>
<div class="grid items-center" style="grid-template-columns: 1fr 100px 150px">
<div
class="grid items-center"
:style="`grid-template-columns: 1fr 100px ${showBillableRate ? '150px' : ''}`">
<div
class="contents [&>*]:border-card-background-separator [&>*]:border-b [&>*]:bg-secondary [&>*]:pb-1.5 [&>*]:pt-1 text-text-tertiary text-sm">
<div class="pl-6">Name</div>
<div class="text-right">Duration</div>
<div class="text-right pr-6">Cost</div>
<div v-if="showBillableRate" class="text-right pr-6">Cost</div>
</div>
<template
v-if="
@@ -297,6 +305,7 @@ const tableData = computed(() => {
:key="entry.description ?? 'none'"
:currency="getOrganizationCurrencyString()"
:type="aggregatedTableTimeEntries.grouped_type"
:show-cost="showBillableRate"
:entry="entry"></ReportingRow>
<div class="contents [&>*]:transition text-text-tertiary [&>*]:h-[50px]">
<div class="flex items-center pl-6 font-medium">
@@ -311,7 +320,9 @@ const tableData = computed(() => {
)
}}
</div>
<div class="justify-end pr-6 flex items-center font-medium">
<div
v-if="showBillableRate"
class="justify-end pr-6 flex items-center font-medium">
{{
aggregatedTableTimeEntries.cost
? formatCents(
@@ -328,7 +339,8 @@ const tableData = computed(() => {
</template>
<div
v-else
class="chart flex flex-col items-center justify-center py-12 col-span-3">
class="chart flex flex-col items-center justify-center py-12"
:class="showBillableRate ? 'col-span-3' : 'col-span-2'">
<p class="text-lg text-text-primary font-medium">No time entries found</p>
<p>Try to change the filters and time range</p>
</div>

View File

@@ -20,6 +20,7 @@ const props = defineProps<{
entry: AggregatedGroupedData;
indent?: boolean;
currency: string;
showCost?: boolean;
}>();
const expanded = ref(false);
@@ -50,7 +51,7 @@ const organization = inject<ComputedRef<Organization>>('organization');
)
}}
</div>
<div class="justify-end pr-6 flex items-center">
<div v-if="showCost" class="justify-end pr-6 flex items-center">
{{
entry.cost
? formatCents(
@@ -66,12 +67,14 @@ const organization = inject<ComputedRef<Organization>>('organization');
</div>
<div
v-if="expanded && entry.grouped_data"
class="col-span-3 grid bg-tertiary"
style="grid-template-columns: 1fr 150px 150px">
:class="showCost ? 'col-span-3' : 'col-span-2'"
class="grid bg-tertiary"
:style="`grid-template-columns: 1fr 150px ${showCost ? '150px' : ''}`">
<ReportingRow
v-for="subEntry in entry.grouped_data"
:key="subEntry.description ?? 'none'"
:currency="props.currency"
:show-cost="showCost"
indent
:entry="subEntry"></ReportingRow>
</div>

View File

@@ -1,31 +1,58 @@
<script setup lang="ts">
import { router } from '@inertiajs/vue3';
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
import { canViewReport } from '@/utils/permissions';
import { computed } from 'vue';
defineProps<{
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
const props = defineProps<{
active: 'reporting' | 'detailed' | 'shared';
}>();
const showSharedReports = computed(() => canViewReport());
const tabs = computed(() => {
const items = [
{ value: 'reporting', label: 'Overview', href: route('reporting') },
{ value: 'detailed', label: 'Detailed', href: route('reporting.detailed') },
];
if (showSharedReports.value) {
items.push({
value: 'shared',
label: 'Shared',
href: route('reporting.shared'),
});
}
return items;
});
function hrefForTab(value: string) {
return tabs.value.find((tab) => tab.value === value)?.href;
}
function onTabChange(value: string | number) {
const href = hrefForTab(String(value));
if (href) {
router.visit(href);
}
}
function onTabHover(value: string) {
const href = hrefForTab(value);
if (href) {
router.prefetch(href, {}, { cacheFor: '1m' });
}
}
</script>
<template>
<TabBar :model-value="active">
<TabBarItem value="reporting" @click="router.visit(route('reporting'))"
>Overview</TabBarItem
>
<TabBarItem value="detailed" @click="router.visit(route('reporting.detailed'))"
>Detailed</TabBarItem
>
<TabBar :default-value="props.active" @update:model-value="onTabChange">
<TabBarItem
v-if="showSharedReports"
value="shared"
@click="router.visit(route('reporting.shared'))"
>Shared</TabBarItem
>
v-for="tab in tabs"
:key="tab.value"
:value="tab.value"
@mouseenter="onTabHover(tab.value)">
{{ tab.label }}
</TabBarItem>
</TabBar>
</template>
<style scoped></style>