add billable rate restriction for free organizations

This commit is contained in:
Gregor Vostrak
2026-10-13 14:59:12 +02:00
committed by Constantin Graf
parent deb0c26e32
commit 5a9c8a8bf7
55 changed files with 1207 additions and 103 deletions

View File

@@ -1,10 +1,11 @@
<script setup lang="ts">
import { useBreaksEnabled } from '@/packages/ui/src/utils/useBreaksEnabled';
import { CheckCircleIcon, TagIcon, UserGroupIcon } from '@heroicons/vue/20/solid';
import { CheckCircleIcon, LockClosedIcon, TagIcon, UserGroupIcon } from '@heroicons/vue/20/solid';
import { FolderIcon } from '@heroicons/vue/16/solid';
import { Check, Coffee } from '@lucide/vue';
import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot, type AcceptableValue } from 'reka-ui';
import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue';
import { useBillableRatesLock } from '@/packages/ui/src/utils/useBillableRatesLock';
import ReportingRoundingControls from '@/Components/Common/Reporting/ReportingRoundingControls.vue';
import TaskMultiselectDropdown from '@/Components/Common/Task/TaskMultiselectDropdown.vue';
import ClientMultiselectDropdown from '@/Components/Common/Client/ClientMultiselectDropdown.vue';
@@ -12,6 +13,7 @@ import MemberMultiselectDropdown from '@/Components/Common/Member/MemberMultisel
import ReportingFilterBadge from '@/Components/Common/Reporting/ReportingFilterBadge.vue';
import ProjectMultiselectDropdown from '@/Components/Common/Project/ProjectMultiselectDropdown.vue';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/packages/ui/src';
import { Button } from '@/packages/ui/src/Buttons';
import MainContainer from '@/packages/ui/src/MainContainer.vue';
import DateRangePicker from '@/packages/ui/src/Input/DateRangePicker.vue';
import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
@@ -35,6 +37,8 @@ const roundingMinutes = defineModel<number>('roundingMinutes', { required: true
const startDate = defineModel<string>('startDate', { required: true });
const endDate = defineModel<string>('endDate', { required: true });
const { locked: billableRatesLocked, requestUpgrade } = useBillableRatesLock();
const emit = defineEmits<{
submit: [];
}>();
@@ -141,7 +145,17 @@ async function createTag(name: string) {
</template>
</TagDropdown>
<Select v-model="billable" @update:model-value="emit('submit')">
<Button
v-if="billableRatesLocked"
variant="outline"
size="sm"
data-testid="billable_filter_locked"
@click="requestUpgrade">
<BillableIcon class="h-4 text-text-quaternary" />
<span class="text-text-secondary">Billable</span>
<LockClosedIcon class="!w-3 text-text-tertiary" />
</Button>
<Select v-else v-model="billable" @update:model-value="emit('submit')">
<SelectTrigger
size="sm"
variant="outline"

View File

@@ -1,4 +1,6 @@
<script setup lang="ts">
import UpgradeLockedValue from '@/packages/ui/src/UpgradeLockedValue.vue';
import { useBillableRatesLock } from '@/packages/ui/src/utils/useBillableRatesLock';
import {
ChartBarIcon,
ArrowDownTrayIcon,
@@ -295,6 +297,8 @@ const tableData = computed(() => {
};
});
});
const { locked: billableRatesLocked } = useBillableRatesLock();
</script>
<template>
@@ -450,17 +454,20 @@ const tableData = computed(() => {
<div
v-if="showBillableRate"
class="justify-end pr-6 flex items-center font-medium">
{{
aggregatedTableTimeEntries.cost
? formatCents(
aggregatedTableTimeEntries.cost,
getOrganizationCurrencyString(),
organization?.currency_format,
organization?.currency_symbol,
organization?.number_format
)
: '--'
}}
<UpgradeLockedValue v-if="billableRatesLocked" />
<template v-else>
{{
aggregatedTableTimeEntries.cost
? formatCents(
aggregatedTableTimeEntries.cost,
getOrganizationCurrencyString(),
organization?.currency_format,
organization?.currency_symbol,
organization?.number_format
)
: '--'
}}
</template>
</div>
</div>
</template>

View File

@@ -2,6 +2,8 @@
import { formatReportingDuration } from '@/packages/ui/src/utils/time';
import { formatCents } from '@/packages/ui/src/utils/money';
import GroupedItemsCountButton from '@/packages/ui/src/GroupedItemsCountButton.vue';
import UpgradeLockedValue from '@/packages/ui/src/UpgradeLockedValue.vue';
import { useBillableRatesLock } from '@/packages/ui/src/utils/useBillableRatesLock';
import { ref, inject, type ComputedRef } from 'vue';
import { twMerge } from 'tailwind-merge';
import type { Organization } from '@/packages/api/src';
@@ -27,6 +29,8 @@ const props = defineProps<{
const expanded = ref(false);
const organization = inject<ComputedRef<Organization>>('organization');
const { locked: billableRatesLocked } = useBillableRatesLock();
</script>
<template>
@@ -53,17 +57,20 @@ const organization = inject<ComputedRef<Organization>>('organization');
}}
</div>
<div v-if="showCost" class="justify-end pr-6 flex items-center">
{{
entry.cost
? formatCents(
entry.cost,
props.currency,
organization?.currency_format,
organization?.currency_symbol,
organization?.number_format
)
: '--'
}}
<UpgradeLockedValue v-if="billableRatesLocked" />
<template v-else>
{{
entry.cost
? formatCents(
entry.cost,
props.currency,
organization?.currency_format,
organization?.currency_symbol,
organization?.number_format
)
: '--'
}}
</template>
</div>
</div>
<div