use prop function createTag instead of event to make sure it is handled by the parent

This commit is contained in:
Gregor Vostrak
2024-07-15 18:19:04 +02:00
parent 655723db49
commit 1e4f0afa67
14 changed files with 60 additions and 71 deletions

View File

@@ -304,7 +304,7 @@ test('test that deleting a time entry from the overview works', async ({
await goToTimeOverview(page); await goToTimeOverview(page);
const timeEntryRows = page.locator('[data-testid="time_entry_row"]'); const timeEntryRows = page.locator('[data-testid="time_entry_row"]');
await createEmptyTimeEntry(page); await createEmptyTimeEntry(page);
const timeEntryCount = await timeEntryRows.count(); await expect(timeEntryRows).toHaveCount(1);
const newTimeEntry = timeEntryRows.first(); const newTimeEntry = timeEntryRows.first();
const actionsDropdown = newTimeEntry const actionsDropdown = newTimeEntry
@@ -313,7 +313,7 @@ test('test that deleting a time entry from the overview works', async ({
await actionsDropdown.click(); await actionsDropdown.click();
const deleteButton = page.getByText('Delete'); const deleteButton = page.getByText('Delete');
await deleteButton.click(); await deleteButton.click();
await expect(timeEntryRows).toHaveCount(timeEntryCount - 1); await expect(timeEntryRows).toHaveCount(0);
}); });
test.skip('test that load more works when the end of page is reached', async ({ test.skip('test that load more works when the end of page is reached', async ({

View File

@@ -13,14 +13,15 @@ const tag = ref<CreateTagBody>({
name: '', name: '',
}); });
const emit = defineEmits<{ const props = defineProps<{
createTag: [name: string, callback: (tag: Tag) => void]; createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
async function submit() { async function submit() {
emit('createTag', tag.value.name, () => { const newTag = props.createTag(tag.value.name);
if (newTag !== undefined) {
show.value = false; show.value = false;
}); }
} }
const tagNameInput = ref<HTMLInputElement | null>(null); const tagNameInput = ref<HTMLInputElement | null>(null);

View File

@@ -8,6 +8,7 @@ import type { Tag } from '@/utils/api';
const props = defineProps<{ const props = defineProps<{
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
const model = defineModel<string[]>({ const model = defineModel<string[]>({
@@ -66,14 +67,13 @@ const filteredTags = computed(() => {
}); });
}); });
function createTag(name: string, callback: (tag: Tag) => void) { async function createAndAddTag(name: string) {
emit('createTag', name, (newTag: Tag) => { const newTag = await props.createTag(name);
if (newTag) { if (newTag) {
addOrRemoveTagFromSelection(newTag.id); addOrRemoveTagFromSelection(newTag.id);
} }
searchValue.value = ''; searchValue.value = '';
callback(newTag); return newTag;
});
} }
async function addTagIfNoneExists() { async function addTagIfNoneExists() {
@@ -109,7 +109,6 @@ function updateSearchValue(event: Event) {
const emit = defineEmits<{ const emit = defineEmits<{
changed: []; changed: [];
submit: []; submit: [];
createTag: [name: string, callback: (tag: Tag) => void];
}>(); }>();
function toggleTag(newValue: string) { function toggleTag(newValue: string) {
@@ -160,7 +159,7 @@ const showCreateTagModal = ref(false);
<template> <template>
<TagCreateModal <TagCreateModal
@createTag="createTag" :createTag="createAndAddTag"
v-model:show="showCreateTagModal"></TagCreateModal> v-model:show="showCreateTagModal"></TagCreateModal>
<Dropdown <Dropdown
@submit="emit('submit')" @submit="emit('submit')"

View File

@@ -9,13 +9,18 @@ import TagTableRow from '@/Components/Common/Tag/TagTableRow.vue';
import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue'; import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
import TagTableHeading from '@/Components/Common/Tag/TagTableHeading.vue'; import TagTableHeading from '@/Components/Common/Tag/TagTableHeading.vue';
import { canCreateTags } from '@/utils/permissions'; import { canCreateTags } from '@/utils/permissions';
import type { Tag } from '@/utils/api';
defineProps<{
createTag: (name: string) => Promise<Tag | undefined>;
}>();
const { tags } = storeToRefs(useTagsStore()); const { tags } = storeToRefs(useTagsStore());
const createTag = ref(false); const showCreateTagModal = ref(false);
</script> </script>
<template> <template>
<TagCreateModal v-model:show="createTag"></TagCreateModal> <TagCreateModal
:createTag
v-model:show="showCreateTagModal"></TagCreateModal>
<div class="flow-root"> <div class="flow-root">
<div class="inline-block min-w-full align-middle"> <div class="inline-block min-w-full align-middle">
<div <div
@@ -34,7 +39,7 @@ const createTag = ref(false);
</p> </p>
<SecondaryButton <SecondaryButton
v-if="canCreateTags()" v-if="canCreateTags()"
@click="createTag = true" @click="showCreateTagModal = true"
:icon="PlusIcon" :icon="PlusIcon"
>Create your First Tag</SecondaryButton >Create your First Tag</SecondaryButton
> >

View File

@@ -21,13 +21,13 @@ const props = defineProps<{
projects: Project[]; projects: Project[];
tasks: Task[]; tasks: Task[];
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
onStartStopClick: [timeEntry: TimeEntry]; onStartStopClick: [timeEntry: TimeEntry];
updateTimeEntries: [timeEntries: TimeEntry[]]; updateTimeEntries: [timeEntries: TimeEntry[]];
deleteTimeEntries: [timeEntries: TimeEntry[]]; deleteTimeEntries: [timeEntries: TimeEntry[]];
createTag: [name: string, callback: (tag: Tag) => void];
}>(); }>();
function updateTimeEntryDescription(description: string) { function updateTimeEntryDescription(description: string) {
@@ -95,7 +95,7 @@ const expanded = ref(false);
</div> </div>
<div class="flex items-center font-medium lg:space-x-2"> <div class="flex items-center font-medium lg:space-x-2">
<TimeEntryRowTagDropdown <TimeEntryRowTagDropdown
@createTag="(...args) => emit('createTag', ...args)" :createTag
:tags="tags" :tags="tags"
@changed="updateTimeEntryTags" @changed="updateTimeEntryTags"
:modelValue="timeEntry.tags"></TimeEntryRowTagDropdown> :modelValue="timeEntry.tags"></TimeEntryRowTagDropdown>
@@ -142,7 +142,7 @@ const expanded = ref(false);
@updateTimeEntry="(arg) => emit('updateTimeEntries', [arg])" @updateTimeEntry="(arg) => emit('updateTimeEntries', [arg])"
@onStartStopClick="emit('onStartStopClick', subEntry)" @onStartStopClick="emit('onStartStopClick', subEntry)"
@deleteTimeEntry="emit('deleteTimeEntries', [subEntry])" @deleteTimeEntry="emit('deleteTimeEntries', [subEntry])"
@createTag="(...args) => emit('createTag', ...args)" :createTag
:key="subEntry.id" :key="subEntry.id"
v-for="subEntry in timeEntry.timeEntries" v-for="subEntry in timeEntry.timeEntries"
:time-entry="subEntry"></TimeEntryRow> :time-entry="subEntry"></TimeEntryRow>

View File

@@ -17,7 +17,6 @@ import { storeToRefs } from 'pinia';
import { useTasksStore } from '@/utils/useTasks'; import { useTasksStore } from '@/utils/useTasks';
import { useProjectsStore } from '@/utils/useProjects'; import { useProjectsStore } from '@/utils/useProjects';
import { useTagsStore } from '@/utils/useTags'; import { useTagsStore } from '@/utils/useTags';
import type { Tag } from '@/utils/api';
const projectStore = useProjectsStore(); const projectStore = useProjectsStore();
const { projects } = storeToRefs(projectStore); const { projects } = storeToRefs(projectStore);
const taskStore = useTasksStore(); const taskStore = useTasksStore();
@@ -75,11 +74,8 @@ async function submit() {
show.value = false; show.value = false;
} }
const { tags } = storeToRefs(useTagsStore()); const { tags } = storeToRefs(useTagsStore());
async function createTag(tag: string, callback: (tag: Tag) => void) { async function createTag(tag: string) {
const newTag = await useTagsStore().createTag(tag); return await useTagsStore().createTag(tag);
if (newTag !== undefined) {
callback(newTag);
}
} }
</script> </script>
@@ -118,7 +114,7 @@ async function createTag(tag: string, callback: (tag: Tag) => void) {
<div class="flex items-center space-x-2 px-4"> <div class="flex items-center space-x-2 px-4">
<TimeTrackerTagDropdown <TimeTrackerTagDropdown
:tags="tags" :tags="tags"
@createTag="createTag" :createTag="createTag"
v-model="timeEntry.tags"></TimeTrackerTagDropdown> v-model="timeEntry.tags"></TimeTrackerTagDropdown>
<BillableToggleButton <BillableToggleButton
v-model="timeEntry.billable"></BillableToggleButton> v-model="timeEntry.billable"></BillableToggleButton>

View File

@@ -19,6 +19,7 @@ const props = defineProps<{
projects: Project[]; projects: Project[];
tasks: Task[]; tasks: Task[];
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -26,7 +27,6 @@ const emit = defineEmits<{
updateTimeEntries: [entries: TimeEntry[]]; updateTimeEntries: [entries: TimeEntry[]];
deleteTimeEntries: [entries: TimeEntry[]]; deleteTimeEntries: [entries: TimeEntry[]];
createTimeEntry: [entry: Omit<CreateTimeEntryBody, 'member_id'>]; createTimeEntry: [entry: Omit<CreateTimeEntryBody, 'member_id'>];
createTag: [name: string, callback: (tag: Tag) => void];
}>(); }>();
const groupedTimeEntries = computed(() => { const groupedTimeEntries = computed(() => {
@@ -118,14 +118,14 @@ function startTimeEntryFromExisting(entry: TimeEntry) {
@onStartStopClick="startTimeEntryFromExisting(entry)" @onStartStopClick="startTimeEntryFromExisting(entry)"
@updateTimeEntries="(arg) => emit('updateTimeEntries', arg)" @updateTimeEntries="(arg) => emit('updateTimeEntries', arg)"
@deleteTimeEntries="(arg) => emit('deleteTimeEntries', arg)" @deleteTimeEntries="(arg) => emit('deleteTimeEntries', arg)"
@createTag="(...args) => emit('createTag', ...args)" :createTag
v-if="'timeEntries' in entry && entry.timeEntries.length > 1" v-if="'timeEntries' in entry && entry.timeEntries.length > 1"
:time-entry="entry"></TimeEntryAggregateRow> :time-entry="entry"></TimeEntryAggregateRow>
<TimeEntryRow <TimeEntryRow
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
:tags="tags" :tags="tags"
@createTag="(...args) => emit('createTag', ...args)" :createTag
@updateTimeEntry="(arg) => emit('updateTimeEntry', arg)" @updateTimeEntry="(arg) => emit('updateTimeEntry', arg)"
@onStartStopClick="startTimeEntryFromExisting(entry)" @onStartStopClick="startTimeEntryFromExisting(entry)"
@deleteTimeEntry="() => emit('deleteTimeEntries', [entry])" @deleteTimeEntry="() => emit('deleteTimeEntries', [entry])"

View File

@@ -16,13 +16,13 @@ const props = defineProps<{
projects: Project[]; projects: Project[];
tasks: Task[]; tasks: Task[];
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
onStartStopClick: []; onStartStopClick: [];
deleteTimeEntry: []; deleteTimeEntry: [];
updateTimeEntry: [timeEntry: TimeEntry]; updateTimeEntry: [timeEntry: TimeEntry];
createTag: [name: string, callback: (tag: Tag) => void];
}>(); }>();
function updateTimeEntryDescription(description: string) { function updateTimeEntryDescription(description: string) {
@@ -81,7 +81,7 @@ function updateProjectAndTask(projectId: string, taskId: string) {
<div class="flex items-center font-medium lg:space-x-2"> <div class="flex items-center font-medium lg:space-x-2">
<TimeEntryRowTagDropdown <TimeEntryRowTagDropdown
@changed="updateTimeEntryTags" @changed="updateTimeEntryTags"
@createTag="(...args) => emit('createTag', ...args)" :createTag
:tags="tags" :tags="tags"
:modelValue="timeEntry.tags"></TimeEntryRowTagDropdown> :modelValue="timeEntry.tags"></TimeEntryRowTagDropdown>
<BillableToggleButton <BillableToggleButton

View File

@@ -6,10 +6,10 @@ import type { Tag } from '@/utils/api';
const props = defineProps<{ const props = defineProps<{
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
createTag: [name: string, callback: (tag: Tag) => void];
changed: [model: string[]]; changed: [model: string[]];
}>(); }>();
@@ -24,7 +24,7 @@ const timeEntryTags = computed<Tag[]>(() => {
<template> <template>
<TagDropdown <TagDropdown
:tags="tags" :tags="tags"
@createTag="(...args) => emit('createTag', ...args)" :createTag
@changed="emit('changed', model)" @changed="emit('changed', model)"
v-model="model"> v-model="model">
<template #trigger> <template #trigger>

View File

@@ -7,7 +7,6 @@ import type { Tag } from '@/utils/api';
const emit = defineEmits<{ const emit = defineEmits<{
changed: []; changed: [];
createTag: [name: string, callback: (tag: Tag) => void];
}>(); }>();
const model = defineModel({ const model = defineModel({
@@ -22,12 +21,13 @@ const iconColorClasses = computed(() => {
}); });
defineProps<{ defineProps<{
tags: Tag[]; tags: Tag[];
createTag: (name: string) => Promise<Tag | undefined>;
}>(); }>();
</script> </script>
<template> <template>
<TagDropdown <TagDropdown
@createTag="(...args) => $emit('createTag', ...args)" :createTag
@changed="emit('changed')" @changed="emit('changed')"
v-model="model" v-model="model"
:tags="tags"> :tags="tags">

View File

@@ -21,7 +21,6 @@ import TimeTrackerRangeSelector from '@/Components/Common/TimeTracker/TimeTracke
import { useProjectsStore } from '@/utils/useProjects'; import { useProjectsStore } from '@/utils/useProjects';
import { useTasksStore } from '@/utils/useTasks'; import { useTasksStore } from '@/utils/useTasks';
import { useTagsStore } from '@/utils/useTags'; import { useTagsStore } from '@/utils/useTags';
import type { Tag } from '@/utils/api';
const page = usePage<{ const page = usePage<{
auth: { auth: {
@@ -105,11 +104,8 @@ function switchToTimeEntryOrganization() {
switchOrganization(currentTimeEntry.value.organization_id); switchOrganization(currentTimeEntry.value.organization_id);
} }
} }
async function createTag(tag: string, callback: (tag: Tag) => void) { async function createTag(tag: string) {
const newTag = await useTagsStore().createTag(tag); return await useTagsStore().createTag(tag);
if (newTag !== undefined) {
callback(newTag);
}
} }
const { tags } = storeToRefs(useTagsStore()); const { tags } = storeToRefs(useTagsStore());
@@ -160,7 +156,7 @@ const { tags } = storeToRefs(useTagsStore());
<div class="flex items-center space-x-2 px-4"> <div class="flex items-center space-x-2 px-4">
<TimeTrackerTagDropdown <TimeTrackerTagDropdown
@changed="updateTimeEntry" @changed="updateTimeEntry"
@createTag="createTag" :createTag
:tags="tags" :tags="tags"
v-model=" v-model="
currentTimeEntry.tags currentTimeEntry.tags

View File

@@ -21,7 +21,7 @@ import {
import { type GroupingOption, useReportingStore } from '@/utils/useReporting'; import { type GroupingOption, useReportingStore } from '@/utils/useReporting';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import TagDropdown from '@/Components/Common/Tag/TagDropdown.vue'; import TagDropdown from '@/Components/Common/Tag/TagDropdown.vue';
import type { AggregatedTimeEntriesQueryParams, Tag } from '@/utils/api'; import type { AggregatedTimeEntriesQueryParams } from '@/utils/api';
import ReportingFilterBadge from '@/Components/Common/Reporting/ReportingFilterBadge.vue'; import ReportingFilterBadge from '@/Components/Common/Reporting/ReportingFilterBadge.vue';
import ProjectMultiselectDropdown from '@/Components/Common/Project/ProjectMultiselectDropdown.vue'; import ProjectMultiselectDropdown from '@/Components/Common/Project/ProjectMultiselectDropdown.vue';
import MemberMultiselectDropdown from '@/Components/Common/Member/MemberMultiselectDropdown.vue'; import MemberMultiselectDropdown from '@/Components/Common/Member/MemberMultiselectDropdown.vue';
@@ -139,11 +139,8 @@ onMounted(() => {
}); });
const { tags } = storeToRefs(useTagsStore()); const { tags } = storeToRefs(useTagsStore());
async function createTag(tag: string, callback: (tag: Tag) => void) { async function createTag(tag: string) {
const newTag = await useTagsStore().createTag(tag); return await useTagsStore().createTag(tag);
if (newTag !== undefined) {
callback(newTag);
}
} }
</script> </script>
@@ -205,7 +202,7 @@ async function createTag(tag: string, callback: (tag: Tag) => void) {
</ClientMultiselectDropdown> </ClientMultiselectDropdown>
<TagDropdown <TagDropdown
@submit="updateReporting" @submit="updateReporting"
@createTag="createTag" :createTag
v-model="selectedTags" v-model="selectedTags"
:tags="tags"> :tags="tags">
<template v-slot:trigger> <template v-slot:trigger>

View File

@@ -9,13 +9,11 @@ import TagCreateModal from '@/Components/Common/Tag/TagCreateModal.vue';
import PageTitle from '@/Components/Common/PageTitle.vue'; import PageTitle from '@/Components/Common/PageTitle.vue';
import { canCreateTags } from '@/utils/permissions'; import { canCreateTags } from '@/utils/permissions';
import { useTagsStore } from '@/utils/useTags'; import { useTagsStore } from '@/utils/useTags';
import type { Tag } from '@/utils/api';
const showCreateTagModal = ref(false); const showCreateTagModal = ref(false);
async function createTag(tag: string, callback: (tag: Tag) => void) {
const newTag = await useTagsStore().createTag(tag); async function createTag(tag: string) {
if (newTag !== undefined) { return await useTagsStore().createTag(tag);
callback(newTag);
}
} }
</script> </script>
@@ -24,18 +22,18 @@ async function createTag(tag: string, callback: (tag: Tag) => void) {
<MainContainer <MainContainer
class="py-5 border-b border-default-background-separator flex justify-between items-center"> class="py-5 border-b border-default-background-separator flex justify-between items-center">
<div class="flex items-center space-x-6"> <div class="flex items-center space-x-6">
<PageTitle :icon="TagIcon" title="Tags"> </PageTitle> <PageTitle :icon="TagIcon" title="Tags"></PageTitle>
</div> </div>
<SecondaryButton <SecondaryButton
v-if="canCreateTags()" v-if="canCreateTags()"
:icon="PlusIcon" :icon="PlusIcon"
@click="showCreateTagModal = true" @click="showCreateTagModal = true"
>Create Tag</SecondaryButton >Create Tag
> </SecondaryButton>
<TagCreateModal <TagCreateModal
@createTag="createTag" :createTag="createTag"
v-model:show="showCreateTagModal"></TagCreateModal> v-model:show="showCreateTagModal"></TagCreateModal>
</MainContainer> </MainContainer>
<TagTable></TagTable> <TagTable :createTag="createTag"></TagTable>
</AppLayout> </AppLayout>
</template> </template>

View File

@@ -5,7 +5,7 @@ import { onMounted, ref, watch } from 'vue';
import MainContainer from '@/Pages/MainContainer.vue'; import MainContainer from '@/Pages/MainContainer.vue';
import { useTimeEntriesStore } from '@/utils/useTimeEntries'; import { useTimeEntriesStore } from '@/utils/useTimeEntries';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import type { CreateTimeEntryBody, Tag, TimeEntry } from '@/utils/api'; import type { CreateTimeEntryBody, TimeEntry } from '@/utils/api';
import { useElementVisibility } from '@vueuse/core'; import { useElementVisibility } from '@vueuse/core';
import { ClockIcon } from '@heroicons/vue/20/solid'; import { ClockIcon } from '@heroicons/vue/20/solid';
import SecondaryButton from '@/Components/SecondaryButton.vue'; import SecondaryButton from '@/Components/SecondaryButton.vue';
@@ -77,11 +77,8 @@ const { projects } = storeToRefs(projectStore);
const taskStore = useTasksStore(); const taskStore = useTasksStore();
const { tasks } = storeToRefs(taskStore); const { tasks } = storeToRefs(taskStore);
async function createTag(name: string, callback: (tag: Tag) => void) { async function createTag(name: string) {
const newTag = await useTagsStore().createTag(name); return await useTagsStore().createTag(name);
if (newTag !== undefined) {
callback(newTag);
}
} }
</script> </script>
@@ -111,7 +108,7 @@ async function createTag(name: string, callback: (tag: Tag) => void) {
@updateTimeEntries="updateTimeEntries" @updateTimeEntries="updateTimeEntries"
@deleteTimeEntries="deleteTimeEntries" @deleteTimeEntries="deleteTimeEntries"
@createTimeEntry="startTimeEntry" @createTimeEntry="startTimeEntry"
@createTag="createTag" :createTag
:projects="projects" :projects="projects"
:tasks="tasks" :tasks="tasks"
:timeEntries="timeEntries" :timeEntries="timeEntries"