Move tabs and TabBar into UI package

This commit is contained in:
Gregor Vostrak
2026-03-23 17:25:26 +01:00
parent 5c6d84dc38
commit c19a0f9acc
35 changed files with 103 additions and 25 deletions

View File

@@ -1,4 +1,5 @@
{ {
"name": "solidtime",
"private": true, "private": true,
"type": "module", "type": "module",
"workspaces": [ "workspaces": [

View File

@@ -9,6 +9,8 @@ import { useTagsStore } from '@/utils/useTags';
import { useTimeEntriesMutations } from '@/utils/useTimeEntriesMutations'; import { useTimeEntriesMutations } from '@/utils/useTimeEntriesMutations';
import { getOrganizationCurrencyString } from '@/utils/money'; import { getOrganizationCurrencyString } from '@/utils/money';
import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { canCreateProjects } from '@/utils/permissions'; import { canCreateProjects } from '@/utils/permissions';
import type { import type {
CreateClientBody, CreateClientBody,
@@ -37,6 +39,8 @@ import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
import DialogModal from '@/packages/ui/src/DialogModal.vue'; import DialogModal from '@/packages/ui/src/DialogModal.vue';
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue'; import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const { const {
isOpen, isOpen,
searchTerm, searchTerm,
@@ -162,6 +166,7 @@ const firstProjectId = computed(() => projects.value[0]?.id ?? '');
:create-client="createClient" :create-client="createClient"
:clients="activeClients" :clients="activeClients"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
:enable-estimated-time="isAllowedToPerformPremiumAction()" /> :enable-estimated-time="isAllowedToPerformPremiumAction()" />
<!-- Client Create Modal --> <!-- Client Create Modal -->
@@ -192,7 +197,8 @@ const firstProjectId = computed(() => projects.value[0]?.id ?? '');
:clients="activeClients" :clients="activeClients"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:can-create-project="canCreateProjects()" /> :can-create-project="canCreateProjects()"
:organization-billable-rate="organization?.billable_rate ?? null" />
<!-- Project Selector Dialog for Active Timer --> <!-- Project Selector Dialog for Active Timer -->
<DialogModal :show="showProjectSelector" closeable @close="showProjectSelector = false"> <DialogModal :show="showProjectSelector" closeable @close="showProjectSelector = false">
@@ -210,6 +216,7 @@ const firstProjectId = computed(() => projects.value[0]?.id ?? '');
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:organization-billable-rate="organization?.billable_rate ?? null"
class="w-full" /> class="w-full" />
</template> </template>
<template #footer> <template #footer>
@@ -234,6 +241,7 @@ const firstProjectId = computed(() => projects.value[0]?.id ?? '');
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:organization-billable-rate="organization?.billable_rate ?? null"
class="w-full" /> class="w-full" />
</template> </template>
<template #footer> <template #footer>

View File

@@ -20,7 +20,10 @@ import { useClientsQuery } from '@/utils/useClientsQuery';
import { getOrganizationCurrencyString } from '@/utils/money'; import { getOrganizationCurrencyString } from '@/utils/money';
import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { canCreateProjects } from '@/utils/permissions'; import { canCreateProjects } from '@/utils/permissions';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const searchValue = ref(''); const searchValue = ref('');
const searchInput = ref<HTMLElement | null>(null); const searchInput = ref<HTMLElement | null>(null);
const model = defineModel<string | null>({ const model = defineModel<string | null>({
@@ -156,6 +159,7 @@ function updateValue(project: Project) {
:create-client="handleCreateClient" :create-client="handleCreateClient"
:clients="activeClients" :clients="activeClients"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
:enable-estimated-time="isAllowedToPerformPremiumAction()" /> :enable-estimated-time="isAllowedToPerformPremiumAction()" />
</template> </template>

View File

@@ -20,9 +20,12 @@ import ProjectBillableRateModal from '@/packages/ui/src/Project/ProjectBillableR
import { getOrganizationCurrencyString } from '@/utils/money'; import { getOrganizationCurrencyString } from '@/utils/money';
import ProjectEditBillableSection from '@/packages/ui/src/Project/ProjectEditBillableSection.vue'; import ProjectEditBillableSection from '@/packages/ui/src/Project/ProjectEditBillableSection.vue';
import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
const { updateProject } = useProjectsStore(); const { updateProject } = useProjectsStore();
const { clients } = useClientsQuery(); const { clients } = useClientsQuery();
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const show = defineModel('show', { default: false }); const show = defineModel('show', { default: false });
const saving = ref(false); const saving = ref(false);
const showBillableRateModal = ref(false); const showBillableRateModal = ref(false);
@@ -117,6 +120,7 @@ async function submitBillableRate() {
v-model:is-billable="project.is_billable" v-model:is-billable="project.is_billable"
v-model:billable-rate="project.billable_rate" v-model:billable-rate="project.billable_rate"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
@submit="submit"></ProjectEditBillableSection> @submit="submit"></ProjectEditBillableSection>
<EstimatedTimeSection <EstimatedTimeSection
v-if="isAllowedToPerformPremiumAction()" v-if="isAllowedToPerformPremiumAction()"

View File

@@ -22,6 +22,8 @@ import { useClientsStore } from '@/utils/useClients';
import { useClientsQuery } from '@/utils/useClientsQuery'; import { useClientsQuery } from '@/utils/useClientsQuery';
import { getOrganizationCurrencyString } from '@/utils/money'; import { getOrganizationCurrencyString } from '@/utils/money';
import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { import {
useVueTable, useVueTable,
getCoreRowModel, getCoreRowModel,
@@ -29,6 +31,8 @@ import {
type SortingState, type SortingState,
} from '@tanstack/vue-table'; } from '@tanstack/vue-table';
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const props = defineProps<{ const props = defineProps<{
projects: Project[]; projects: Project[];
showBillableRate: boolean; showBillableRate: boolean;
@@ -155,6 +159,7 @@ const gridTemplate = computed(() => {
:create-project :create-project
:create-client :create-client
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
:clients="clients" :clients="clients"
:enable-estimated-time="isAllowedToPerformPremiumAction()"></ProjectCreateModal> :enable-estimated-time="isAllowedToPerformPremiumAction()"></ProjectCreateModal>
<div class="flow-root max-w-[100vw] overflow-x-auto"> <div class="flow-root max-w-[100vw] overflow-x-auto">

View File

@@ -2,8 +2,7 @@
import { router } from '@inertiajs/vue3'; import { router } from '@inertiajs/vue3';
import { canViewReport } from '@/utils/permissions'; import { canViewReport } from '@/utils/permissions';
import { computed } from 'vue'; import { computed } from 'vue';
import TabBar from '@/Components/Common/TabBar/TabBar.vue'; import { TabBar, TabBarItem } from '@/packages/ui/src';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
const props = defineProps<{ const props = defineProps<{
active: 'reporting' | 'detailed' | 'shared'; active: 'reporting' | 'detailed' | 'shared';

View File

@@ -11,6 +11,7 @@ import duration from 'dayjs/plugin/duration';
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry'; import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { getCurrentOrganizationId } from '@/utils/useUser'; import { getCurrentOrganizationId } from '@/utils/useUser';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { switchOrganization } from '@/utils/useOrganization'; import { switchOrganization } from '@/utils/useOrganization';
import { useProjectsQuery } from '@/utils/useProjectsQuery'; import { useProjectsQuery } from '@/utils/useProjectsQuery';
import { useTasksQuery } from '@/utils/useTasksQuery'; import { useTasksQuery } from '@/utils/useTasksQuery';
@@ -47,6 +48,8 @@ dayjs.extend(duration);
dayjs.extend(utc); dayjs.extend(utc);
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const currentTimeEntryStore = useCurrentTimeEntryStore(); const currentTimeEntryStore = useCurrentTimeEntryStore();
const { currentTimeEntry, isActive, now } = storeToRefs(currentTimeEntryStore); const { currentTimeEntry, isActive, now } = storeToRefs(currentTimeEntryStore);
const { startLiveTimer, stopLiveTimer, setActiveState } = currentTimeEntryStore; const { startLiveTimer, stopLiveTimer, setActiveState } = currentTimeEntryStore;
@@ -152,6 +155,7 @@ const { tags } = useTagsQuery();
:create-time-entry="createTimeEntry" :create-time-entry="createTimeEntry"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:organization-billable-rate="organization?.billable_rate ?? null"
:projects :projects
:tasks :tasks
:tags :tags
@@ -173,6 +177,7 @@ const { tags } = useTagsQuery();
:create-project :create-project
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:organization-billable-rate="organization?.billable_rate ?? null"
:create-client :create-client
:clients :clients
:tags :tags

View File

@@ -23,7 +23,10 @@ import { useClientsStore } from '@/utils/useClients';
import { getOrganizationCurrencyString } from '@/utils/money'; import { getOrganizationCurrencyString } from '@/utils/money';
import { canCreateProjects } from '@/utils/permissions'; import { canCreateProjects } from '@/utils/permissions';
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry'; import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const calendarStart = ref<Date | undefined>(undefined); const calendarStart = ref<Date | undefined>(undefined);
const calendarEnd = ref<Date | undefined>(undefined); const calendarEnd = ref<Date | undefined>(undefined);
@@ -124,6 +127,7 @@ function onRefresh() {
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:organization-billable-rate="organization?.billable_rate ?? null"
:create-time-entry="createTimeEntry" :create-time-entry="createTimeEntry"
:update-time-entry="updateTimeEntry" :update-time-entry="updateTimeEntry"
:delete-time-entry="deleteTimeEntry" :delete-time-entry="deleteTimeEntry"

View File

@@ -10,8 +10,7 @@ import ClientTable from '@/Components/Common/Client/ClientTable.vue';
import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue'; import ClientCreateModal from '@/Components/Common/Client/ClientCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue'; import PageTitle from '@/Components/Common/PageTitle.vue';
import { canCreateClients } from '@/utils/permissions'; import { canCreateClients } from '@/utils/permissions';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue'; import { TabBar, TabBarItem } from '@/packages/ui/src';
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import { useStorage } from '@vueuse/core'; import { useStorage } from '@vueuse/core';
import type { SortColumn, SortDirection } from '@/Components/Common/Client/ClientTable.vue'; import type { SortColumn, SortDirection } from '@/Components/Common/Client/ClientTable.vue';

View File

@@ -4,8 +4,7 @@ import AppLayout from '@/Layouts/AppLayout.vue';
import { PlusIcon } from '@heroicons/vue/16/solid'; import { PlusIcon } from '@heroicons/vue/16/solid';
import { UserGroupIcon } from '@heroicons/vue/20/solid'; import { UserGroupIcon } from '@heroicons/vue/20/solid';
import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue'; import SecondaryButton from '@/packages/ui/src/Buttons/SecondaryButton.vue';
import TabBar from '@/Components/Common/TabBar/TabBar.vue'; import { TabBar, TabBarItem } from '@/packages/ui/src';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue';
import { ref } from 'vue'; import { ref } from 'vue';
import MemberTable from '@/Components/Common/Member/MemberTable.vue'; import MemberTable from '@/Components/Common/Member/MemberTable.vue';
import MemberInviteModal from '@/Components/Common/Member/MemberInviteModal.vue'; import MemberInviteModal from '@/Components/Common/Member/MemberInviteModal.vue';

View File

@@ -21,8 +21,7 @@ import ProjectMemberTable from '@/Components/Common/ProjectMember/ProjectMemberT
import ProjectMemberCreateModal from '@/Components/Common/ProjectMember/ProjectMemberCreateModal.vue'; import ProjectMemberCreateModal from '@/Components/Common/ProjectMember/ProjectMemberCreateModal.vue';
import { useProjectMembersQuery } from '@/utils/useProjectMembersQuery'; import { useProjectMembersQuery } from '@/utils/useProjectMembersQuery';
import { canCreateProjects, canCreateTasks, canViewProjectMembers } from '@/utils/permissions'; import { canCreateProjects, canCreateTasks, canViewProjectMembers } from '@/utils/permissions';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue'; import { TabBar, TabBarItem } from '@/packages/ui/src';
import TabBar from '@/Components/Common/TabBar/TabBar.vue';
import { useTasksQuery } from '@/utils/useTasksQuery'; import { useTasksQuery } from '@/utils/useTasksQuery';
import ProjectEditModal from '@/Components/Common/Project/ProjectEditModal.vue'; import ProjectEditModal from '@/Components/Common/Project/ProjectEditModal.vue';
import { Badge } from '@/packages/ui/src'; import { Badge } from '@/packages/ui/src';

View File

@@ -131,6 +131,7 @@ const showBillableRate = computed(() => {
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:create-client :create-client
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
:clients="clients" :clients="clients"
@submit="createProject"></ProjectCreateModal> @submit="createProject"></ProjectCreateModal>
</MainContainer> </MainContainer>

View File

@@ -66,6 +66,7 @@ import ReportingExportModal from '@/Components/Common/Reporting/ReportingExportM
import ReportingFilterBar from '@/Components/Common/Reporting/ReportingFilterBar.vue'; import ReportingFilterBar from '@/Components/Common/Reporting/ReportingFilterBar.vue';
import { useTimeEntriesReportQuery } from '@/utils/useTimeEntriesReportQuery'; import { useTimeEntriesReportQuery } from '@/utils/useTimeEntriesReportQuery';
import { useTimeEntriesMutations } from '@/utils/useTimeEntriesMutations'; import { useTimeEntriesMutations } from '@/utils/useTimeEntriesMutations';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
// TimeEntryRoundingType is now defined in ReportingRoundingControls component // TimeEntryRoundingType is now defined in ReportingRoundingControls component
type TimeEntryRoundingType = 'up' | 'down' | 'nearest'; type TimeEntryRoundingType = 'up' | 'down' | 'nearest';
@@ -89,6 +90,7 @@ const roundingType = ref<TimeEntryRoundingType>('nearest');
const roundingMinutes = ref<number>(15); const roundingMinutes = ref<number>(15);
const { members } = useMembersQuery(); const { members } = useMembersQuery();
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const pageLimit = 15; const pageLimit = 15;
// Watch rounding enabled state to trigger updates // Watch rounding enabled state to trigger updates
@@ -353,6 +355,7 @@ async function downloadExport(format: ExportFormat) {
:tags="tags" :tags="tags"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:clients="clients" :clients="clients"
:organization-billable-rate="organization?.billable_rate ?? null"
class="border-b border-default-background-separator" class="border-b border-default-background-separator"
:update-time-entries=" :update-time-entries="
(args) => (args) =>
@@ -384,6 +387,7 @@ async function downloadExport(format: ExportFormat) {
:on-start-stop-click="() => startTimeEntryFromExisting(entry)" :on-start-stop-click="() => startTimeEntryFromExisting(entry)"
:delete-time-entry="() => deleteTimeEntries([entry])" :delete-time-entry="() => deleteTimeEntries([entry])"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:organization-billable-rate="organization?.billable_rate ?? null"
:duplicate-time-entry="() => createTimeEntry(entry)" :duplicate-time-entry="() => createTimeEntry(entry)"
:members="members" :members="members"
show-date show-date

View File

@@ -26,6 +26,8 @@ import TimeEntryMassActionRow from '@/packages/ui/src/TimeEntry/TimeEntryMassAct
import type { UpdateMultipleTimeEntriesChangeset } from '@/packages/api/src'; import type { UpdateMultipleTimeEntriesChangeset } from '@/packages/api/src';
import { isAllowedToPerformPremiumAction } from '@/utils/billing'; import { isAllowedToPerformPremiumAction } from '@/utils/billing';
import { canCreateProjects } from '@/utils/permissions'; import { canCreateProjects } from '@/utils/permissions';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
import { useTagsStore } from '@/utils/useTags'; import { useTagsStore } from '@/utils/useTags';
import { useProjectsStore } from '@/utils/useProjects'; import { useProjectsStore } from '@/utils/useProjects';
import { useClientsStore } from '@/utils/useClients'; import { useClientsStore } from '@/utils/useClients';
@@ -87,6 +89,8 @@ async function createClient(body: CreateClientBody): Promise<Client | undefined>
return await useClientsStore().createClient(body); return await useClientsStore().createClient(body);
} }
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
const selectedTimeEntries = ref([] as TimeEntry[]); const selectedTimeEntries = ref([] as TimeEntry[]);
async function clearSelectionAndState() { async function clearSelectionAndState() {
@@ -115,6 +119,7 @@ function deleteSelected() {
:tags="tags" :tags="tags"
:currency="getOrganizationCurrencyString()" :currency="getOrganizationCurrencyString()"
:clients="clients" :clients="clients"
:organization-billable-rate="organization?.billable_rate ?? null"
class="border-t border-default-background-separator hidden sm:block" class="border-t border-default-background-separator hidden sm:block"
:update-time-entries=" :update-time-entries="
(args) => (args) =>
@@ -134,6 +139,7 @@ function deleteSelected() {
:create-project :create-project
:enable-estimated-time="isAllowedToPerformPremiumAction()" :enable-estimated-time="isAllowedToPerformPremiumAction()"
:can-create-project="canCreateProjects()" :can-create-project="canCreateProjects()"
:organization-billable-rate="organization?.billable_rate ?? null"
:clients :clients
:create-client :create-client
:update-time-entry :update-time-entry

View File

@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { Button } from '..'; import { Button } from '..';
import { ChevronLeft, ChevronRight } from 'lucide-vue-next'; import { ChevronLeft, ChevronRight } from 'lucide-vue-next';
import { Tabs, TabsList } from '@/Components/ui/tabs'; import { Tabs, TabsList } from '../tabs';
import TabBarItem from '@/Components/Common/TabBar/TabBarItem.vue'; import TabBarItem from '../TabBar/TabBarItem.vue';
import CalendarSettingsPopover from './CalendarSettingsPopover.vue'; import CalendarSettingsPopover from './CalendarSettingsPopover.vue';
import type { CalendarSettings } from './calendarSettings'; import type { CalendarSettings } from './calendarSettings';

View File

@@ -73,6 +73,7 @@ const props = defineProps<{
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
currency: string; currency: string;
canCreateProject: boolean; canCreateProject: boolean;
organizationBillableRate: number | null;
createTimeEntry: ( createTimeEntry: (
entry: Omit<TimeEntry, 'id' | 'organization_id' | 'user_id'> entry: Omit<TimeEntry, 'id' | 'organization_id' | 'user_id'>
@@ -417,6 +418,7 @@ function getEventDurationSeconds(dayEvent: DayEvent, dayStr: string): number {
:create-tag="createTag" :create-tag="createTag"
:currency="currency" :currency="currency"
:can-create-project="canCreateProject" :can-create-project="canCreateProject"
:organization-billable-rate="organizationBillableRate"
:tags="tags as any" :tags="tags as any"
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
@@ -438,7 +440,8 @@ function getEventDurationSeconds(dayEvent: DayEvent, dayStr: string): number {
:tasks="tasks" :tasks="tasks"
:clients="clients" :clients="clients"
:currency="currency" :currency="currency"
:can-create-project="canCreateProject" /> :can-create-project="canCreateProject"
:organization-billable-rate="organizationBillableRate" />
<template v-if="!loading"> <template v-if="!loading">
<CalendarToolbar <CalendarToolbar

View File

@@ -26,6 +26,7 @@ const props = defineProps<{
createClient: (client: CreateClientBody) => Promise<Client | undefined>; createClient: (client: CreateClientBody) => Promise<Client | undefined>;
currency: string; currency: string;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
organizationBillableRate: number | null;
}>(); }>();
const activeClients = computed(() => { const activeClients = computed(() => {
@@ -113,7 +114,10 @@ const currentClientName = computed(() => {
<ProjectEditBillableSection <ProjectEditBillableSection
v-model:is-billable="project.is_billable" v-model:is-billable="project.is_billable"
v-model:billable-rate="project.billable_rate" v-model:billable-rate="project.billable_rate"
:currency="currency"></ProjectEditBillableSection> :currency="currency"
:organization-billable-rate="
organizationBillableRate
"></ProjectEditBillableSection>
<EstimatedTimeSection <EstimatedTimeSection
v-if="enableEstimatedTime" v-if="enableEstimatedTime"
v-model="project.estimated_time" v-model="project.estimated_time"

View File

@@ -10,15 +10,12 @@ import {
} from '@/packages/ui/src/tooltip'; } from '@/packages/ui/src/tooltip';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue'; import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue';
import { useOrganizationQuery } from '@/utils/useOrganizationQuery';
import { getCurrentOrganizationId } from '@/utils/useUser';
defineProps<{ const props = defineProps<{
currency: string; currency: string;
organizationBillableRate: number | null;
}>(); }>();
const { organization } = useOrganizationQuery(getCurrentOrganizationId()!);
type RateType = 'default-rate' | 'custom-rate'; type RateType = 'default-rate' | 'custom-rate';
const billableDefault = ref<'billable' | 'non-billable'>('non-billable'); const billableDefault = ref<'billable' | 'non-billable'>('non-billable');
@@ -49,7 +46,7 @@ watch(rateType, () => {
billableDefault.value = 'billable'; billableDefault.value = 'billable';
isBillable.value = true; isBillable.value = true;
if (!billableRate.value) { if (!billableRate.value) {
billableRate.value = organization.value?.billable_rate ?? null; billableRate.value = props.organizationBillableRate ?? null;
} }
} }
}); });
@@ -57,7 +54,7 @@ watch(rateType, () => {
const displayedRate = computed({ const displayedRate = computed({
get() { get() {
if (rateType.value === 'default-rate') { if (rateType.value === 'default-rate') {
return organization.value?.billable_rate ?? null; return props.organizationBillableRate ?? null;
} }
return billableRate.value; return billableRate.value;
}, },

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { Tabs, TabsList } from '@/Components/ui/tabs'; import { Tabs, TabsList } from '../tabs';
defineProps<{ defineProps<{
defaultValue?: string; defaultValue?: string;

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { TabsTrigger } from '@/Components/ui/tabs'; import { TabsTrigger } from '../tabs';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
import type { Component } from 'vue'; import type { Component } from 'vue';

View File

@@ -46,6 +46,7 @@ const props = defineProps<{
updateTimeEntry: (timeEntry: TimeEntry) => void; updateTimeEntry: (timeEntry: TimeEntry) => void;
deleteTimeEntries: (timeEntries: TimeEntry[]) => void; deleteTimeEntries: (timeEntries: TimeEntry[]) => void;
currency: string; currency: string;
organizationBillableRate: number | null;
selectedTimeEntries: TimeEntry[]; selectedTimeEntries: TimeEntry[];
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
canCreateProject: boolean; canCreateProject: boolean;
@@ -136,6 +137,7 @@ function onSelectChange(checked: boolean) {
:project="timeEntry.project_id" :project="timeEntry.project_id"
:enable-estimated-time :enable-estimated-time
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:task="timeEntry.task_id" :task="timeEntry.task_id"
@changed=" @changed="
updateProjectAndTask updateProjectAndTask
@@ -240,6 +242,7 @@ function onSelectChange(checked: boolean) {
:project="timeEntry.project_id" :project="timeEntry.project_id"
:enable-estimated-time :enable-estimated-time
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:task="timeEntry.task_id" :task="timeEntry.task_id"
@changed=" @changed="
updateProjectAndTask updateProjectAndTask
@@ -291,6 +294,7 @@ function onSelectChange(checked: boolean) {
:create-client :create-client
:clients :clients
:create-project :create-project
:organization-billable-rate="organizationBillableRate"
:tags="tags" :tags="tags"
indent indent
:update-time-entry="(timeEntry: TimeEntry) => updateTimeEntry(timeEntry)" :update-time-entry="(timeEntry: TimeEntry) => updateTimeEntry(timeEntry)"

View File

@@ -42,6 +42,7 @@ const props = defineProps<{
start?: string; start?: string;
end?: string; end?: string;
currency: string; currency: string;
organizationBillableRate: number | null;
canCreateProject: boolean; canCreateProject: boolean;
}>(); }>();
@@ -165,6 +166,7 @@ const billableProxy = computed({
:create-client :create-client
:can-create-project :can-create-project
:currency :currency
:organization-billable-rate="organizationBillableRate"
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
:enable-estimated-time="enableEstimatedTime" /> :enable-estimated-time="enableEstimatedTime" />

View File

@@ -43,6 +43,7 @@ const props = defineProps<{
tasks: Task[]; tasks: Task[];
clients: Client[]; clients: Client[];
currency: string; currency: string;
organizationBillableRate: number | null;
canCreateProject: boolean; canCreateProject: boolean;
}>(); }>();
@@ -172,6 +173,7 @@ const billableProxy = computed({
:create-client :create-client
:can-create-project="canCreateProject" :can-create-project="canCreateProject"
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
:enable-estimated-time="enableEstimatedTime" /> :enable-estimated-time="enableEstimatedTime" />

View File

@@ -34,6 +34,7 @@ const props = defineProps<{
createProject: (project: CreateProjectBody) => Promise<Project | undefined>; createProject: (project: CreateProjectBody) => Promise<Project | undefined>;
createClient: (client: CreateClientBody) => Promise<Client | undefined>; createClient: (client: CreateClientBody) => Promise<Client | undefined>;
currency: string; currency: string;
organizationBillableRate: number | null;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
canCreateProject: boolean; canCreateProject: boolean;
}>(); }>();
@@ -163,6 +164,7 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
:delete-time-entries :delete-time-entries
:create-tag :create-tag
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:time-entry="entry" :time-entry="entry"
@selected=" @selected="
(timeEntries: TimeEntry[]) => { (timeEntries: TimeEntry[]) => {
@@ -195,6 +197,7 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
:tags="tags" :tags="tags"
:clients :clients
:create-tag :create-tag
:organization-billable-rate="organizationBillableRate"
:update-time-entry :update-time-entry
:on-start-stop-click="() => startTimeEntryFromExisting(entry)" :on-start-stop-click="() => startTimeEntryFromExisting(entry)"
:delete-time-entry="() => deleteTimeEntries([entry])" :delete-time-entry="() => deleteTimeEntries([entry])"

View File

@@ -33,6 +33,7 @@ const props = defineProps<{
currency: string; currency: string;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
canCreateProject: boolean; canCreateProject: boolean;
organizationBillableRate: number | null;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -58,6 +59,7 @@ const showMassUpdateModal = ref(false);
:enable-estimated-time :enable-estimated-time
:can-create-project :can-create-project
:currency :currency
:organization-billable-rate="organizationBillableRate"
:time-entries="selectedTimeEntries" :time-entries="selectedTimeEntries"
@submit="emit('submit')"></TimeEntryMassUpdateModal> @submit="emit('submit')"></TimeEntryMassUpdateModal>
<MainContainer <MainContainer

View File

@@ -35,6 +35,7 @@ const props = defineProps<{
createTag: (name: string) => Promise<Tag | undefined>; createTag: (name: string) => Promise<Tag | undefined>;
updateTimeEntries: (changeset: UpdateMultipleTimeEntriesChangeset) => Promise<void>; updateTimeEntries: (changeset: UpdateMultipleTimeEntriesChangeset) => Promise<void>;
currency: string; currency: string;
organizationBillableRate: number | null;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
canCreateProject: boolean; canCreateProject: boolean;
}>(); }>();
@@ -162,6 +163,7 @@ watch(removeAllTags, () => {
:create-project :create-project
:create-client :create-client
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:can-create-project :can-create-project
empty-placeholder="Select project..." empty-placeholder="Select project..."
allow-reset allow-reset

View File

@@ -46,6 +46,7 @@ const props = defineProps<{
duplicateTimeEntry?: () => void; duplicateTimeEntry?: () => void;
updateTimeEntry: (timeEntry: TimeEntry) => void; updateTimeEntry: (timeEntry: TimeEntry) => void;
currency: string; currency: string;
organizationBillableRate: number | null;
showMember?: boolean; showMember?: boolean;
showDate?: boolean; showDate?: boolean;
selected?: boolean; selected?: boolean;
@@ -142,6 +143,7 @@ async function handleDeleteTimeEntry() {
:tasks="tasks" :tasks="tasks"
:project="timeEntry.project_id" :project="timeEntry.project_id"
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:enable-estimated-time :enable-estimated-time
:task="timeEntry.task_id" :task="timeEntry.task_id"
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown> @changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
@@ -209,6 +211,7 @@ async function handleDeleteTimeEntry() {
:tasks="tasks" :tasks="tasks"
:project="timeEntry.project_id" :project="timeEntry.project_id"
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:enable-estimated-time :enable-estimated-time
:task="timeEntry.task_id" :task="timeEntry.task_id"
@changed=" @changed="
@@ -277,6 +280,7 @@ async function handleDeleteTimeEntry() {
:tasks="tasks" :tasks="tasks"
:clients="clients" :clients="clients"
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:can-create-project="canCreateProject" /> :can-create-project="canCreateProject" />
</template> </template>

View File

@@ -38,6 +38,7 @@ const props = defineProps<{
createClient: (client: CreateClientBody) => Promise<Client | undefined>; createClient: (client: CreateClientBody) => Promise<Client | undefined>;
isActive: boolean; isActive: boolean;
currency: string; currency: string;
organizationBillableRate: number | null;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
canCreateProject: boolean; canCreateProject: boolean;
}>(); }>();
@@ -262,6 +263,7 @@ useSelectEvents(
:clients :clients
:create-project :create-project
:currency="currency" :currency="currency"
:organization-billable-rate="organizationBillableRate"
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
:enable-estimated-time="enableEstimatedTime" :enable-estimated-time="enableEstimatedTime"

View File

@@ -58,6 +58,7 @@ const props = withDefaults(
emptyPlaceholder?: string; emptyPlaceholder?: string;
allowReset?: boolean; allowReset?: boolean;
enableEstimatedTime: boolean; enableEstimatedTime: boolean;
organizationBillableRate: number | null;
canCreateProject: boolean; canCreateProject: boolean;
class?: string; class?: string;
variant?: 'input' | 'ghost' | 'outline'; variant?: 'input' | 'ghost' | 'outline';
@@ -660,6 +661,7 @@ const showCreateProject = ref(false);
v-model:show="showCreateProject" v-model:show="showCreateProject"
:create-client :create-client
:enable-estimated-time="enableEstimatedTime" :enable-estimated-time="enableEstimatedTime"
:organization-billable-rate="organizationBillableRate"
:currency="currency" :currency="currency"
:clients="clients" :clients="clients"
:create-project></ProjectCreateModal> :create-project></ProjectCreateModal>

View File

@@ -145,6 +145,9 @@ import {
SelectValue, SelectValue,
} from './select/index'; } from './select/index';
import { Separator } from './separator/index'; import { Separator } from './separator/index';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './tabs/index';
import TabBar from './TabBar/TabBar.vue';
import TabBarItem from './TabBar/TabBarItem.vue';
import TimezoneMismatchModal from './TimezoneMismatchModal.vue'; import TimezoneMismatchModal from './TimezoneMismatchModal.vue';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip/index'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from './tooltip/index';
@@ -257,6 +260,12 @@ export {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
Separator, Separator,
TabBar,
TabBarItem,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
TextInput, TextInput,
time, time,
TimeEntryCalendar, TimeEntryCalendar,

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils'; import { cn } from '../utils/cn';
import { TabsContent, type TabsContentProps } from 'reka-ui'; import { TabsContent, type TabsContentProps } from 'reka-ui';
import { computed, type HTMLAttributes } from 'vue'; import { computed, type HTMLAttributes } from 'vue';

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils'; import { cn } from '../utils/cn';
import { TabsList, type TabsListProps } from 'reka-ui'; import { TabsList, type TabsListProps } from 'reka-ui';
import { computed, type HTMLAttributes } from 'vue'; import { computed, type HTMLAttributes } from 'vue';

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { cn } from '@/lib/utils'; import { cn } from '../utils/cn';
import { TabsTrigger, type TabsTriggerProps, useForwardProps } from 'reka-ui'; import { TabsTrigger, type TabsTriggerProps, useForwardProps } from 'reka-ui';
import { computed, type HTMLAttributes, type Component } from 'vue'; import { computed, type HTMLAttributes, type Component } from 'vue';

View File

@@ -0,0 +1,4 @@
export { default as Tabs } from './Tabs.vue';
export { default as TabsContent } from './TabsContent.vue';
export { default as TabsList } from './TabsList.vue';
export { default as TabsTrigger } from './TabsTrigger.vue';