update dependencies, update eslint config, update optional ts props types

This commit is contained in:
Gregor Vostrak
2025-01-29 17:58:03 +01:00
parent 00e2518196
commit 18ab1f714b
128 changed files with 2926 additions and 3127 deletions

View File

@@ -97,7 +97,6 @@ function onSelectChange(event: Event) {
class="sm:flex py-2 items-center min-w-0 justify-between group">
<div class="flex space-x-3 items-center min-w-0">
<Checkbox
@update:checked="onSelectChange"
:checked="
timeEntry.timeEntries.every(
(aggregateTimeEntry: TimeEntry) =>
@@ -105,7 +104,8 @@ function onSelectChange(event: Event) {
aggregateTimeEntry
)
)
" />
"
@update:checked="onSelectChange" />
<div class="flex items-center min-w-0">
<GroupedItemsCountButton
:expanded="expanded"
@@ -114,36 +114,36 @@ function onSelectChange(event: Event) {
</GroupedItemsCountButton>
<TimeEntryDescriptionInput
class="min-w-0 mr-4"
@changed="updateTimeEntryDescription"
:modelValue="
:model-value="
timeEntry.description
"></TimeEntryDescriptionInput>
"
@changed="updateTimeEntryDescription"></TimeEntryDescriptionInput>
<TimeTrackerProjectTaskDropdown
:clients
:createProject
:createClient
:canCreateProject
:create-project
:create-client
:can-create-project
:projects="projects"
:tasks="tasks"
:showBadgeBorder="false"
@changed="updateProjectAndTask"
:show-badge-border="false"
:project="timeEntry.project_id"
:enableEstimatedTime
:enable-estimated-time
:currency="currency"
class="border border-border-primary"
:task="
timeEntry.task_id
"></TimeTrackerProjectTaskDropdown>
"
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
</div>
</div>
<div class="flex items-center font-medium lg:space-x-2">
<TimeEntryRowTagDropdown
:createTag
:create-tag
:tags="tags"
@changed="updateTimeEntryTags"
:modelValue="timeEntry.tags"></TimeEntryRowTagDropdown>
:model-value="timeEntry.tags"
@changed="updateTimeEntryTags"></TimeEntryRowTagDropdown>
<BillableToggleButton
:modelValue="timeEntry.billable"
:model-value="timeEntry.billable"
class="opacity-50 focus-visible:opacity-100 group-hover:opacity-100"
size="small"
@changed="
@@ -151,23 +151,23 @@ function onSelectChange(event: Event) {
"></BillableToggleButton>
<div class="flex-1">
<button
@click="expanded = !expanded"
class="hidden lg:block text-muted w-[110px] 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">
class="hidden lg:block text-muted w-[110px] 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"
@click="expanded = !expanded">
{{ formatStartEnd(timeEntry.start, timeEntry.end) }}
</button>
</div>
<button
@click="expanded = !expanded"
class="text-white min-w-[90px] px-2 py-1.5 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-semibold focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-tertiary">
class="text-white min-w-[90px] px-2 py-1.5 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-semibold focus-visible:outline-none focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:bg-tertiary"
@click="expanded = !expanded">
{{
formatHumanReadableDuration(timeEntry.duration ?? 0)
}}
</button>
<TimeTrackerStartStop
@changed="onStartStopClick(timeEntry)"
:active="!!(timeEntry.start && !timeEntry.end)"
class="opacity-20 hidden sm:flex group-hover:opacity-100 focus-visible:opacity-100"></TimeTrackerStartStop>
class="opacity-20 hidden sm:flex group-hover:opacity-100 focus-visible:opacity-100"
@changed="onStartStopClick(timeEntry)"></TimeTrackerStartStop>
<TimeEntryMoreOptionsDropdown
@delete="
deleteTimeEntries(timeEntry?.timeEntries ?? [])
@@ -179,9 +179,11 @@ function onSelectChange(event: Event) {
v-if="expanded"
class="w-full border-t border-default-background-separator bg-black/15">
<TimeEntryRow
v-for="subEntry in timeEntry.timeEntries"
:key="subEntry.id"
:projects="projects"
:enableEstimatedTime
:canCreateProject
:enable-estimated-time
:can-create-project
:tasks="tasks"
:selected="
!!selectedTimeEntries.find(
@@ -189,23 +191,21 @@ function onSelectChange(event: Event) {
filterEntry.id === subEntry.id
)
"
@selected="emit('selected', [subEntry])"
@unselected="emit('unselected', [subEntry])"
:createClient
:create-client
:clients
:createProject
:create-project
:tags="tags"
indent
:updateTimeEntry="
:update-time-entry="
(timeEntry: TimeEntry) => updateTimeEntry(timeEntry)
"
:onStartStopClick="() => onStartStopClick(subEntry)"
:deleteTimeEntry="() => deleteTimeEntries([subEntry])"
:on-start-stop-click="() => onStartStopClick(subEntry)"
:delete-time-entry="() => deleteTimeEntries([subEntry])"
:currency="currency"
:createTag
:key="subEntry.id"
v-for="subEntry in timeEntry.timeEntries"
:time-entry="subEntry"></TimeEntryRow>
:create-tag
:time-entry="subEntry"
@selected="emit('selected', [subEntry])"
@unselected="emit('unselected', [subEntry])"></TimeEntryRow>
</div>
</div>
</template>

View File

@@ -135,11 +135,11 @@ type BillableOption = {
<TextInput
id="description"
ref="description"
placeholder="What did you work on?"
v-model="timeEntry.description"
@keydown.enter="submit"
placeholder="What did you work on?"
type="text"
class="mt-1 block w-full" />
class="mt-1 block w-full"
@keydown.enter="submit" />
</div>
</div>
<div
@@ -147,28 +147,28 @@ type BillableOption = {
<div class="flex w-full items-center space-x-2 justify-between">
<div class="flex-1 min-w-0">
<TimeTrackerProjectTaskDropdown
v-model:project="timeEntry.project_id"
v-model:task="
timeEntry.task_id
"
:clients
:createProject
:createClient
:canCreateProject="canCreateProjects()"
:create-project
:create-client
:can-create-project="canCreateProjects()"
:currency="getOrganizationCurrencyString()"
size="xlarge"
class="bg-input-background"
:projects="projects"
:tasks="tasks"
:enableEstimatedTime="enableEstimatedTime"
v-model:project="timeEntry.project_id"
v-model:task="
timeEntry.task_id
"></TimeTrackerProjectTaskDropdown>
:enable-estimated-time="enableEstimatedTime"></TimeTrackerProjectTaskDropdown>
</div>
<div class="flex items-center space-x-2">
<div class="flex-col">
<TagDropdown
:createTag
v-model="timeEntry.tags"
:create-tag
:tags="tags">
<template v-slot:trigger>
<template #trigger>
<Badge
class="bg-input-background"
tag="button"
@@ -206,7 +206,7 @@ type BillableOption = {
value: 'false',
},
]">
<template v-slot:trigger>
<template #trigger>
<Badge
class="bg-input-background"
tag="button"
@@ -246,22 +246,22 @@ type BillableOption = {
<InputLabel>Start</InputLabel>
<div class="flex flex-col items-center space-y-2 mt-1">
<TimePicker
size="large"
v-model="localStart"></TimePicker>
v-model="localStart"
size="large"></TimePicker>
<DatePicker
class="text-xs text-text-tertiary max-w-28 px-1.5 py-1.5"
v-model="localStart"></DatePicker>
v-model="localStart"
class="text-xs text-text-tertiary max-w-28 px-1.5 py-1.5"></DatePicker>
</div>
</div>
<div class="">
<InputLabel>End</InputLabel>
<div class="flex flex-col items-center space-y-2 mt-1">
<TimePicker
size="large"
v-model="localEnd"></TimePicker>
v-model="localEnd"
size="large"></TimePicker>
<DatePicker
class="text-xs text-text-tertiary max-w-28 px-1.5 py-1.5"
v-model="localEnd"></DatePicker>
v-model="localEnd"
class="text-xs text-text-tertiary max-w-28 px-1.5 py-1.5"></DatePicker>
</div>
</div>
</div>

View File

@@ -44,11 +44,11 @@ const displaysPlaceholder = computed(() => {
<input
data-testid="time_entry_description"
:value="liveDataValue"
placeholder="Add a description"
class="absolute px-0 h-full min-w-0 pl-3 pr-1 left-0 top-0 w-full text-sm text-white font-medium bg-transparent focus-visible:ring-0 rounded-lg border-0"
@blur="onChange"
@input="onInput"
@keydown.enter="onChange"
placeholder="Add a description"
class="absolute px-0 h-full min-w-0 pl-3 pr-1 left-0 top-0 w-full text-sm text-white font-medium bg-transparent focus-visible:ring-0 rounded-lg border-0" />
@keydown.enter="onChange" />
</div>
</div>
</template>

View File

@@ -152,19 +152,32 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
<TimeEntryRowHeading
:date="key"
:duration="sumDuration(value)"
@select-all="selectAllTimeEntries(value)"
@unselect-all="unselectAllTimeEntries(value)"
:checked="
value.every((timeEntry: TimeEntry) =>
selectedTimeEntries.includes(timeEntry)
)
"></TimeEntryRowHeading>
"
@select-all="selectAllTimeEntries(value)"
@unselect-all="unselectAllTimeEntries(value)"></TimeEntryRowHeading>
<template v-for="entry in value" :key="entry.id">
<TimeEntryAggregateRow
:createProject
:canCreateProject
:enableEstimatedTime
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"
:update-time-entries
:update-time-entry
:delete-time-entries
:create-tag
:currency="currency"
:time-entry="entry"
@selected="
(timeEntries: TimeEntry[]) => {
selectedTimeEntries = [
@@ -183,47 +196,34 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
)
);
}
"
:createClient
:projects="projects"
:tasks="tasks"
:tags="tags"
:clients
:onStartStopClick="startTimeEntryFromExisting"
:updateTimeEntries
:updateTimeEntry
:deleteTimeEntries
:createTag
:currency="currency"
v-if="'timeEntries' in entry && entry.timeEntries.length > 1"
:time-entry="entry"></TimeEntryAggregateRow>
"></TimeEntryAggregateRow>
<TimeEntryRow
:createClient
:enableEstimatedTime
:canCreateProject
:createProject
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])"
:currency="currency"
:time-entry="entry.timeEntries[0]"
@selected="selectedTimeEntries.push(entry)"
@unselected="
selectedTimeEntries = selectedTimeEntries.filter(
(item: TimeEntry) => item.id !== entry.id
)
"
:tasks="tasks"
:tags="tags"
:clients
:createTag
:updateTimeEntry
:onStartStopClick="() => startTimeEntryFromExisting(entry)"
:deleteTimeEntry="() => deleteTimeEntries([entry])"
:currency="currency"
v-else
:time-entry="entry.timeEntries[0]"></TimeEntryRow>
"></TimeEntryRow>
</template>
</div>
</template>

View File

@@ -47,20 +47,20 @@ const showMassUpdateModal = ref(false);
<template>
<TimeEntryMassUpdateModal
v-model:show="showMassUpdateModal"
:projects
:tasks
:tags
:clients
:createTag
:createProject
:createClient
:updateTimeEntries
:enableEstimatedTime
:canCreateProject
:create-tag
:create-project
:create-client
:update-time-entries
:enable-estimated-time
:can-create-project
:currency
:time-entries="selectedTimeEntries"
@submit="emit('submit')"
v-model:show="showMassUpdateModal"></TimeEntryMassUpdateModal>
@submit="emit('submit')"></TimeEntryMassUpdateModal>
<MainContainer
:class="
twMerge(
@@ -69,35 +69,35 @@ const showMassUpdateModal = ref(false);
)
">
<Checkbox
:checked="allSelected"
id="selectAll"
:checked="allSelected"
@update:checked="
allSelected ? emit('unselectAll') : emit('selectAll')
">
</Checkbox>
<InputLabel
v-if="selectedTimeEntries.length > 0"
for="selectAll"
class="select-none text-text-secondary"
v-if="selectedTimeEntries.length > 0">
class="select-none text-text-secondary">
{{ selectedTimeEntries.length }} selected
</InputLabel>
<InputLabel
v-else
for="selectAll"
class="text-text-secondary select-none"
v-else
>Select All</InputLabel
>
<button
v-if="selectedTimeEntries.length"
class="text-text-tertiary flex space-x-1 items-center hover:text-text-secondary transition focus-visible:ring-2 outline-0 focus-visible:text-text-primary focus-visible:ring-ring rounded h-full px-2"
@click="showMassUpdateModal = true"
v-if="selectedTimeEntries.length">
@click="showMassUpdateModal = true">
<PencilSquareIcon class="w-4"></PencilSquareIcon>
<span> Edit </span>
</button>
<button
v-if="selectedTimeEntries.length"
class="text-red-400 h-full px-2 space-x-1 items-center flex hover:text-red-500 transition focus-visible:ring-2 outline-0 focus-visible:text-red-500 focus-visible:ring-ring rounded"
@click="deleteSelected"
v-if="selectedTimeEntries.length">
@click="deleteSelected">
<TrashIcon class="w-3.5"></TrashIcon>
<span> Delete </span>
</button>

View File

@@ -118,7 +118,7 @@ async function submit() {
billable.value = undefined;
saving.value = false;
removeAllTags.value = false;
} catch (e) {
} catch {
saving.value = false;
}
}
@@ -147,34 +147,34 @@ type SelectOption = { label: string; value: string };
id="description"
ref="descriptionInput"
v-model="description"
@keydown.enter="submit"
type="text"
class="mt-1 block w-full" />
class="mt-1 block w-full"
@keydown.enter="submit" />
</div>
<div class="space-y-2">
<InputLabel for="project" value="Project" />
<TimeTrackerProjectTaskDropdown
v-model:project="projectId"
v-model:task="taskId"
:clients
:createProject
:createClient
:create-project
:create-client
:currency="currency"
:canCreateProject
:can-create-project
class="mt-1"
empty-placeholder="Select project..."
allow-reset
size="xlarge"
:enableEstimatedTime
:enable-estimated-time
:projects="projects"
:tasks="tasks"
v-model:project="projectId"
v-model:task="taskId"></TimeTrackerProjectTaskDropdown>
:tasks="tasks"></TimeTrackerProjectTaskDropdown>
</div>
<div class="space-y-2">
<InputLabel for="project" value="Tag" />
<div class="flex space-x-5">
<TagDropdown
:createTag
v-model="selectedTags"
:create-tag
:tags="tags">
<template #trigger>
<Badge
@@ -190,8 +190,8 @@ type SelectOption = { label: string; value: string };
</TagDropdown>
<div class="flex items-center space-x-2">
<Checkbox
v-model:checked="removeAllTags"
id="no_tags"></Checkbox>
id="no_tags"
v-model:checked="removeAllTags"></Checkbox>
<InputLabel for="no_tags" value="Remove all tags" />
</div>
</div>
@@ -221,7 +221,7 @@ type SelectOption = { label: string; value: string };
value: 'non-billable',
},
]">
<template v-slot:trigger>
<template #trigger>
<Badge tag="button" size="xlarge">
<span v-if="billable === undefined">
Set billable status

View File

@@ -10,9 +10,9 @@ const emit = defineEmits<{
<template>
<MoreOptionsDropdown label="Actions for the time entry">
<button
@click="emit('delete')"
data-testid="time_entry_delete"
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out">
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-white hover:bg-card-background-active focus:outline-none focus:bg-card-background-active transition duration-150 ease-in-out"
@click="emit('delete')">
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
<span>Delete</span>
</button>

View File

@@ -26,13 +26,13 @@ const triggerElement = ref<HTMLButtonElement | null>(null);
<div class="relative">
<Dropdown
v-model="open"
@submit="open = false"
align="bottom"
:close-on-content-click="false">
:close-on-content-click="false"
@submit="open = false">
<template #trigger>
<button
data-testid="time_entry_range_selector"
ref="triggerElement"
data-testid="time_entry_range_selector"
:class="
twMerge(
'text-muted w-[110px] 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',
@@ -50,14 +50,14 @@ const triggerElement = ref<HTMLButtonElement | null>(null);
</template>
<template #content>
<TimeRangeSelector
focus
:start="start"
:end="end"
@changed="
(newStart: string, newEnd: string) =>
emit('changed', newStart, newEnd)
"
focus
@close="open = false"
:start="start"
:end="end">
@close="open = false">
</TimeRangeSelector>
</template>
</Dropdown>

View File

@@ -100,44 +100,44 @@ function onSelectChange(event: Event) {
class="sm:flex py-2 min-w-0 items-center justify-between group">
<div class="flex items-center min-w-0">
<Checkbox
@update:checked="onSelectChange"
:checked="selected" />
<div class="w-10 h-7" v-if="indent === true"></div>
:checked="selected"
@update:checked="onSelectChange" />
<div v-if="indent === true" class="w-10 h-7"></div>
<TimeEntryDescriptionInput
@changed="updateTimeEntryDescription"
class="min-w-0 mr-4"
:modelValue="
:model-value="
timeEntry.description
"></TimeEntryDescriptionInput>
"
@changed="updateTimeEntryDescription"></TimeEntryDescriptionInput>
<TimeTrackerProjectTaskDropdown
:createProject
:createClient
:canCreateProject
:create-project
:create-client
:can-create-project
:clients
:projects="projects"
:tasks="tasks"
:showBadgeBorder="false"
@changed="updateProjectAndTask"
:show-badge-border="false"
:project="timeEntry.project_id"
:currency="currency"
class="border border-border-primary"
:enableEstimatedTime
:enable-estimated-time
:task="
timeEntry.task_id
"></TimeTrackerProjectTaskDropdown>
"
@changed="updateProjectAndTask"></TimeTrackerProjectTaskDropdown>
</div>
<div
class="flex items-center font-medium space-x-1 lg:space-x-2">
<div class="text-sm px-2" v-if="showMember && members">
<div v-if="showMember && members" class="text-sm px-2">
{{ memberName }}
</div>
<TimeEntryRowTagDropdown
@changed="updateTimeEntryTags"
:createTag
:create-tag
:tags="tags"
:modelValue="timeEntry.tags"></TimeEntryRowTagDropdown>
:model-value="timeEntry.tags"
@changed="updateTimeEntryTags"></TimeEntryRowTagDropdown>
<BillableToggleButton
:modelValue="timeEntry.billable"
:model-value="timeEntry.billable"
class="opacity-50 group-hover:opacity-100 focus-visible:opacity-100"
size="small"
@changed="
@@ -148,7 +148,7 @@ function onSelectChange(event: Event) {
class="hidden lg:block"
:start="timeEntry.start"
:end="timeEntry.end"
:showDate
:show-date
@changed="
updateStartEndTime
"></TimeEntryRangeSelector>
@@ -160,9 +160,9 @@ function onSelectChange(event: Event) {
updateStartEndTime
"></TimeEntryRowDurationInput>
<TimeTrackerStartStop
@changed="onStartStopClick"
:active="!!(timeEntry.start && !timeEntry.end)"
class="opacity-20 hidden sm:flex focus-visible:opacity-100 group-hover:opacity-100"></TimeTrackerStartStop>
class="opacity-20 hidden sm:flex focus-visible:opacity-100 group-hover:opacity-100"
@changed="onStartStopClick"></TimeTrackerStartStop>
<TimeEntryMoreOptionsDropdown
@delete="
deleteTimeEntry

View File

@@ -62,13 +62,13 @@ function selectInput(event: Event) {
<template>
<input
v-model="currentTime"
data-testid="time_entry_duration_input"
class="text-white w-[90px] px-2 py-1.5 bg-transparent text-center hover:bg-card-background rounded-lg border border-transparent hover:border-card-border text-sm font-semibold focus-visible:bg-tertiary focus-visible:border-transparent focus-visible:ring-2 focus-visible:ring-ring"
@focus="selectInput"
@keydown.tab="open = false"
@blur="updateTimerAndStartLiveTimerUpdate"
@keydown.enter="updateTimerAndStartLiveTimerUpdate"
v-model="currentTime" />
@keydown.enter="updateTimerAndStartLiveTimerUpdate" />
</template>
<style scoped></style>

View File

@@ -23,11 +23,11 @@ const timeEntryTags = computed<Tag[]>(() => {
</script>
<template>
<TagDropdown
v-model="model"
:tags="tags"
align="bottom-end"
:createTag
@changed="emit('changed', model)"
v-model="model">
:create-tag
@changed="emit('changed', model)">
<template #trigger>
<button
data-testid="time_entry_tag_dropdown"