use container queries for time entry table

This commit is contained in:
Gregor Vostrak
2025-11-13 12:24:28 +01:00
parent 8a1253e101
commit 9c9aeeab0f
8 changed files with 105 additions and 103 deletions

View File

@@ -93,7 +93,7 @@ function onSelectChange(checked: boolean) {
class="border-b border-default-background-separator bg-row-background min-w-0 transition" class="border-b border-default-background-separator bg-row-background min-w-0 transition"
data-testid="time_entry_row"> data-testid="time_entry_row">
<MainContainer class="min-w-0"> <MainContainer class="min-w-0">
<div class="sm:flex py-2 items-center min-w-0 justify-between group"> <div class="@sm:flex py-2 items-center min-w-0 justify-between group">
<div class="flex space-x-3 items-center min-w-0"> <div class="flex space-x-3 items-center min-w-0">
<Checkbox <Checkbox
:checked=" :checked="
@@ -102,7 +102,7 @@ function onSelectChange(checked: boolean) {
) )
" "
@update:checked="onSelectChange" /> @update:checked="onSelectChange" />
<div class="flex items-center min-w-0 space-x-1 lg:space-x-2"> <div class="flex items-center min-w-0 space-x-1 @lg:space-x-2">
<GroupedItemsCountButton :expanded="expanded" @click="expanded = !expanded"> <GroupedItemsCountButton :expanded="expanded" @click="expanded = !expanded">
{{ timeEntry?.timeEntries?.length }} {{ timeEntry?.timeEntries?.length }}
</GroupedItemsCountButton> </GroupedItemsCountButton>
@@ -125,7 +125,7 @@ function onSelectChange(checked: boolean) {
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown> @changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
</div> </div>
</div> </div>
<div class="flex items-center font-medium space-x-1 lg:space-x-2"> <div class="flex items-center font-medium space-x-1 @lg:space-x-2">
<TimeEntryRowTagDropdown <TimeEntryRowTagDropdown
:create-tag :create-tag
:tags="tags" :tags="tags"

View File

@@ -137,79 +137,81 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
</script> </script>
<template> <template>
<div v-for="(value, key) in groupedTimeEntries" :key="key"> <div class="@container">
<TimeEntryRowHeading <div v-for="(value, key) in groupedTimeEntries" :key="key">
:date="key" <TimeEntryRowHeading
:duration="sumDuration(value)" :date="key"
:checked=" :duration="sumDuration(value)"
value.every((timeEntry: TimeEntry) => selectedTimeEntries.includes(timeEntry)) :checked="
" value.every((timeEntry: TimeEntry) => selectedTimeEntries.includes(timeEntry))
@select-all="selectAllTimeEntries(value)"
@unselect-all="unselectAllTimeEntries(value)"></TimeEntryRowHeading>
<template v-for="entry in value" :key="entry.id">
<TimeEntryAggregateRow
v-if="'timeEntries' in entry && entry.timeEntries.length > 1"
:create-project
:can-create-project
:enable-estimated-time
:selected-time-entries="selectedTimeEntries"
:create-client
:projects="projects"
:tasks="tasks"
:tags="tags"
:clients
:on-start-stop-click="startTimeEntryFromExisting"
:duplicate-time-entry="createTimeEntry"
:update-time-entries
:update-time-entry
:delete-time-entries
:create-tag
:currency="currency"
:time-entry="entry"
@selected="
(timeEntries: TimeEntry[]) => {
selectedTimeEntries = [...selectedTimeEntries, ...timeEntries];
}
" "
@unselected=" @select-all="selectAllTimeEntries(value)"
(timeEntriesToUnselect: TimeEntry[]) => { @unselect-all="unselectAllTimeEntries(value)"></TimeEntryRowHeading>
<template v-for="entry in value" :key="entry.id">
<TimeEntryAggregateRow
v-if="'timeEntries' in entry && entry.timeEntries.length > 1"
:create-project
:can-create-project
:enable-estimated-time
:selected-time-entries="selectedTimeEntries"
:create-client
:projects="projects"
:tasks="tasks"
:tags="tags"
:clients
:on-start-stop-click="startTimeEntryFromExisting"
:duplicate-time-entry="createTimeEntry"
:update-time-entries
:update-time-entry
:delete-time-entries
:create-tag
:currency="currency"
:time-entry="entry"
@selected="
(timeEntries: TimeEntry[]) => {
selectedTimeEntries = [...selectedTimeEntries, ...timeEntries];
}
"
@unselected="
(timeEntriesToUnselect: TimeEntry[]) => {
selectedTimeEntries = selectedTimeEntries.filter(
(item: TimeEntry) =>
!timeEntriesToUnselect.find(
(filterEntry: TimeEntry) => filterEntry.id === item.id
)
);
}
"></TimeEntryAggregateRow>
<TimeEntryRow
v-else
:create-client
:enable-estimated-time
:can-create-project
:create-project
:projects="projects"
:selected="
!!selectedTimeEntries.find(
(filterEntry: TimeEntry) => filterEntry.id === entry.id
)
"
:tasks="tasks"
:tags="tags"
:clients
:create-tag
:update-time-entry
:on-start-stop-click="() => startTimeEntryFromExisting(entry)"
:delete-time-entry="() => deleteTimeEntries([entry])"
:duplicate-time-entry="() => createTimeEntry(entry)"
:currency="currency"
:time-entry="entry.timeEntries[0]"
@selected="selectedTimeEntries.push(entry)"
@unselected="
selectedTimeEntries = selectedTimeEntries.filter( selectedTimeEntries = selectedTimeEntries.filter(
(item: TimeEntry) => (item: TimeEntry) => item.id !== entry.id
!timeEntriesToUnselect.find( )
(filterEntry: TimeEntry) => filterEntry.id === item.id "></TimeEntryRow>
) </template>
); </div>
}
"></TimeEntryAggregateRow>
<TimeEntryRow
v-else
:create-client
:enable-estimated-time
:can-create-project
:create-project
:projects="projects"
:selected="
!!selectedTimeEntries.find(
(filterEntry: TimeEntry) => filterEntry.id === entry.id
)
"
:tasks="tasks"
:tags="tags"
:clients
:create-tag
:update-time-entry
:on-start-stop-click="() => startTimeEntryFromExisting(entry)"
:delete-time-entry="() => deleteTimeEntries([entry])"
:duplicate-time-entry="() => createTimeEntry(entry)"
:currency="currency"
:time-entry="entry.timeEntries[0]"
@selected="selectedTimeEntries.push(entry)"
@unselected="
selectedTimeEntries = selectedTimeEntries.filter(
(item: TimeEntry) => item.id !== entry.id
)
"></TimeEntryRow>
</template>
</div> </div>
</template> </template>

View File

@@ -112,7 +112,7 @@ async function handleDeleteTimeEntry() {
class="border-b border-default-background-separator transition min-w-0 bg-row-background" class="border-b border-default-background-separator transition min-w-0 bg-row-background"
data-testid="time_entry_row"> data-testid="time_entry_row">
<MainContainer class="min-w-0"> <MainContainer class="min-w-0">
<div class="sm:flex py-2 min-w-0 items-center justify-between group"> <div class="@sm:flex py-2 min-w-0 items-center justify-between group">
<div class="flex items-center min-w-0"> <div class="flex items-center min-w-0">
<Checkbox :checked="selected" @update:checked="onSelectChange" /> <Checkbox :checked="selected" @update:checked="onSelectChange" />
<div v-if="indent === true" class="w-10 h-7"></div> <div v-if="indent === true" class="w-10 h-7"></div>
@@ -134,7 +134,7 @@ async function handleDeleteTimeEntry() {
:task="timeEntry.task_id" :task="timeEntry.task_id"
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown> @changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
</div> </div>
<div class="flex items-center font-medium space-x-1 lg:space-x-2"> <div class="flex items-center font-medium space-x-1 @lg:space-x-2">
<div v-if="showMember && members" class="text-sm px-2"> <div v-if="showMember && members" class="text-sm px-2">
{{ memberName }} {{ memberName }}
</div> </div>

View File

@@ -32,13 +32,13 @@ function selectUnselectAll(value: boolean) {
<template> <template>
<div <div
class="bg-row-heading-background border-t border-b border-row-heading-border py-1 text-xs sm:text-sm"> class="bg-row-heading-background border-t border-b border-row-heading-border py-1 text-xs @sm:text-sm">
<MainContainer> <MainContainer>
<div class="flex group justify-between items-center"> <div class="flex group justify-between items-center">
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<div class="w-5"> <div class="w-5">
<svg <svg
class="w-3 sm:w-4 text-icon-default group-hover:hidden block" class="w-3 @sm:w-4 text-icon-default group-hover:hidden block"
viewBox="0 0 24 24" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"> xmlns="http://www.w3.org/2000/svg">
<g fill="none"> <g fill="none">
@@ -61,7 +61,7 @@ function selectUnselectAll(value: boolean) {
{{ formatDate(date, organization?.date_format) }} {{ formatDate(date, organization?.date_format) }}
</span> </span>
</div> </div>
<div class="text-text-secondary pr-[87px] lg:pr-[92px]"> <div class="text-text-secondary pr-[87px] @lg:pr-[92px]">
<span class="font-medium"> <span class="font-medium">
{{ {{
formatHumanReadableDuration( formatHumanReadableDuration(

View File

@@ -1,15 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TooltipRootEmits, TooltipRootProps } from "reka-ui" import type { TooltipRootEmits, TooltipRootProps } from 'reka-ui';
import { TooltipRoot, useForwardPropsEmits } from "reka-ui" import { TooltipRoot, useForwardPropsEmits } from 'reka-ui';
const props = defineProps<TooltipRootProps>() const props = defineProps<TooltipRootProps>();
const emits = defineEmits<TooltipRootEmits>() const emits = defineEmits<TooltipRootEmits>();
const forwarded = useForwardPropsEmits(props, emits) const forwarded = useForwardPropsEmits(props, emits);
</script> </script>
<template> <template>
<TooltipRoot v-bind="forwarded"> <TooltipRoot v-bind="forwarded">
<slot /> <slot />
</TooltipRoot> </TooltipRoot>
</template> </template>

View File

@@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TooltipProviderProps } from "reka-ui" import type { TooltipProviderProps } from 'reka-ui';
import { TooltipProvider } from "reka-ui" import { TooltipProvider } from 'reka-ui';
const props = defineProps<TooltipProviderProps>() const props = defineProps<TooltipProviderProps>();
</script> </script>
<template> <template>
<TooltipProvider v-bind="props"> <TooltipProvider v-bind="props">
<slot /> <slot />
</TooltipProvider> </TooltipProvider>
</template> </template>

View File

@@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import type { TooltipTriggerProps } from "reka-ui" import type { TooltipTriggerProps } from 'reka-ui';
import { TooltipTrigger } from "reka-ui" import { TooltipTrigger } from 'reka-ui';
const props = defineProps<TooltipTriggerProps>() const props = defineProps<TooltipTriggerProps>();
</script> </script>
<template> <template>
<TooltipTrigger v-bind="props"> <TooltipTrigger v-bind="props">
<slot /> <slot />
</TooltipTrigger> </TooltipTrigger>
</template> </template>

View File

@@ -1,4 +1,4 @@
export { default as Tooltip } from "./Tooltip.vue" export { default as Tooltip } from './Tooltip.vue';
export { default as TooltipContent } from "./TooltipContent.vue" export { default as TooltipContent } from './TooltipContent.vue';
export { default as TooltipProvider } from "./TooltipProvider.vue" export { default as TooltipProvider } from './TooltipProvider.vue';
export { default as TooltipTrigger } from "./TooltipTrigger.vue" export { default as TooltipTrigger } from './TooltipTrigger.vue';