design fixes, improve component encapsulation

This commit is contained in:
Gregor Vostrak
2025-11-06 14:20:12 +01:00
parent 8ba04eca0c
commit 0648437478
11 changed files with 26 additions and 19 deletions

View File

@@ -158,6 +158,7 @@ async function discardCurrentTimeEntry() {
}
const { tags } = storeToRefs(useTagsStore());
const { timeEntries } = storeToRefs(useTimeEntriesStore());
</script>
<template>
@@ -196,6 +197,7 @@ const { tags } = storeToRefs(useTagsStore());
:tags
:tasks
:projects
:time-entries
:create-tag
:is-active
:currency="getOrganizationCurrencyString()"

View File

@@ -26,6 +26,7 @@
"scripts": {
"dev": "vite",
"build": "vite build && vue-tsc --emitDeclarationOnly",
"watch": "vite build --watch",
"types": "vue-tsc ",
"preview": "vite preview"
},

View File

@@ -318,7 +318,9 @@ watch(showEditTimeEntryModal, (value) => {
:tags="tags as any"
:projects="projects"
:tasks="tasks"
:clients="clients" />
:clients="clients"
:currency="currency"
:can-create-project="canCreateProject" />
<FullCalendar ref="calendarRef" class="fullcalendar" :options="calendarOptions">
<template #eventContent="arg">
<FullCalendarEventContent

View File

@@ -102,7 +102,7 @@ function onSelectChange(checked: boolean) {
)
"
@update:checked="onSelectChange" />
<div class="flex items-center min-w-0">
<div class="flex items-center min-w-0 space-x-1 lg:space-x-2">
<GroupedItemsCountButton :expanded="expanded" @click="expanded = !expanded">
{{ timeEntry?.timeEntries?.length }}
</GroupedItemsCountButton>
@@ -125,7 +125,7 @@ function onSelectChange(checked: boolean) {
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
</div>
</div>
<div class="flex items-center font-medium lg:space-x-2">
<div class="flex items-center font-medium space-x-1 lg:space-x-2">
<TimeEntryRowTagDropdown
:create-tag
:tags="tags"
@@ -142,8 +142,8 @@ function onSelectChange(checked: boolean) {
twMerge(
'text-text-secondary px-1 py-1.5 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-medium focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-tertiary',
organization?.time_format === '12-hours'
? 'w-[170px]'
: 'w-[120px]'
? 'w-[150px]'
: 'w-[90px]'
)
"
@click="expanded = !expanded">
@@ -157,7 +157,7 @@ function onSelectChange(checked: boolean) {
</button>
</div>
<button
class="text-text-primary min-w-[90px] px-2.5 py-1.5 bg-transparent text-right hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-medium focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-tertiary"
class="text-text-primary !mr-2 min-w-[70px] px-1.5 py-1.5 bg-transparent text-right hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-medium focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-tertiary"
@click="expanded = !expanded">
{{
formatHumanReadableDuration(

View File

@@ -15,8 +15,6 @@ import type {
Client,
TimeEntry,
} from '@/packages/api/src';
import { getOrganizationCurrencyString } from '@/utils/money';
import { canCreateProjects } from '@/utils/permissions';
import TagDropdown from '@/packages/ui/src/Tag/TagDropdown.vue';
import { Badge } from '@/packages/ui/src';
import BillableIcon from '@/packages/ui/src/Icons/BillableIcon.vue';
@@ -44,6 +42,8 @@ const props = defineProps<{
projects: Project[];
tasks: Task[];
clients: Client[];
currency: string;
canCreateProject: boolean;
}>();
const description = ref<HTMLInputElement | null>(null);
@@ -163,8 +163,8 @@ type BillableOption = {
:clients
:create-project
:create-client
:can-create-project="canCreateProjects()"
:currency="getOrganizationCurrencyString()"
:can-create-project="canCreateProject"
:currency="currency"
size="xlarge"
class="bg-input-background"
:projects="projects"

View File

@@ -35,11 +35,11 @@ const organization = inject<ComputedRef<Organization>>('organization');
data-testid="time_entry_range_selector"
:class="
twMerge(
'text-text-secondary px-2 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:text-text-primary focus-visible:ring-ring focus-visible:bg-tertiary',
'text-text-secondary px-1 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:text-text-primary focus-visible:ring-ring focus-visible:bg-tertiary',
showDate
? 'text-xs py-1.5 font-semibold'
: 'text-sm py-1.5 font-medium',
organization?.time_format === '12-hours' ? 'w-[170px]' : 'w-[120px]',
organization?.time_format === '12-hours' ? 'w-[150px]' : 'w-[90px]',
open && 'border-card-border bg-card-background'
)
">

View File

@@ -186,7 +186,9 @@ async function handleDeleteTimeEntry() {
:tags="tags"
:projects="projects"
:tasks="tasks"
:clients="clients" />
:clients="clients"
:currency="currency"
:can-create-project="canCreateProject" />
</template>
<style scoped></style>

View File

@@ -77,7 +77,7 @@ function selectInput(event: Event) {
v-model="currentTime"
data-testid="time_entry_duration_input"
name="Duration"
class="text-text-primary w-[90px] px-2.5 py-1.5 bg-transparent text-right hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-medium focus-visible:bg-tertiary focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring"
class="text-text-primary w-[70px] !mr-2 px-1.5 py-1.5 bg-transparent text-right hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-medium focus-visible:bg-tertiary focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring"
@focus="selectInput"
@keydown.tab="open = false"
@blur="updateTimerAndStartLiveTimerUpdate"

View File

@@ -61,7 +61,7 @@ function selectUnselectAll(value: boolean) {
{{ formatDate(date, organization?.date_format) }}
</span>
</div>
<div class="text-text-secondary pr-[90px] lg:pr-[92px]">
<div class="text-text-secondary pr-[87px] lg:pr-[92px]">
<span class="font-medium">
{{
formatHumanReadableDuration(

View File

@@ -15,8 +15,6 @@ import type {
} from '@/packages/api/src';
import { computed, nextTick, ref, watch } from 'vue';
import type { Dayjs } from 'dayjs';
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
import { storeToRefs } from 'pinia';
import { useFocus } from '@vueuse/core';
import { autoUpdate, flip, limitShift, offset, shift, useFloating } from '@floating-ui/vue';
import TimeTrackerRecentlyTrackedEntry from '@/packages/ui/src/TimeTracker/TimeTrackerRecentlyTrackedEntry.vue';
@@ -34,6 +32,7 @@ const props = defineProps<{
tasks: Task[];
tags: Tag[];
clients: Client[];
timeEntries: TimeEntry[];
createTag: (name: string) => Promise<Tag | undefined>;
createProject: (project: CreateProjectBody) => Promise<Project | undefined>;
createClient: (client: CreateClientBody) => Promise<Client | undefined>;
@@ -131,10 +130,9 @@ function updateTimeEntryDescription() {
}
}
const { timeEntries } = storeToRefs(useTimeEntriesStore());
const filteredRecentlyTrackedTimeEntries = computed(() => {
// do not include running time entries
const finishedTimeEntries = timeEntries.value.filter((item) => item.end !== null);
const finishedTimeEntries = props.timeEntries.filter((item) => item.end !== null);
// filter out duplicates based on description, task, project, tags and billable
const nonDuplicateTimeEntries = finishedTimeEntries.filter((item, index, self) => {

View File

@@ -20,6 +20,7 @@ import TextInput from './Input/TextInput.vue';
import InputLabel from './Input/InputLabel.vue';
import TimeTrackerRunningInDifferentOrganizationOverlay from './TimeTracker/TimeTrackerRunningInDifferentOrganizationOverlay.vue';
import TimeTrackerControls from './TimeTracker/TimeTrackerControls.vue';
import TimeTrackerMoreOptionsDropdown from './TimeTracker/TimeTrackerMoreOptionsDropdown.vue';
import CardTitle from './CardTitle.vue';
import SelectDropdown from './Input/SelectDropdown.vue';
import Badge from './Badge.vue';
@@ -48,6 +49,7 @@ export {
InputLabel,
TimeTrackerRunningInDifferentOrganizationOverlay,
TimeTrackerControls,
TimeTrackerMoreOptionsDropdown,
CardTitle,
SelectDropdown,
Badge,