mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-17 04:32:15 +01:00
refactor to common MoreOptionsDropdown component for shared ui
This commit is contained in:
@@ -307,9 +307,11 @@ test('test that deleting a time entry from the overview works', async ({
|
|||||||
const timeEntryCount = await timeEntryRows.count();
|
const timeEntryCount = await timeEntryRows.count();
|
||||||
|
|
||||||
const newTimeEntry = timeEntryRows.first();
|
const newTimeEntry = timeEntryRows.first();
|
||||||
const actionsDropdown = newTimeEntry.getByTestId('time_entry_actions');
|
const actionsDropdown = newTimeEntry
|
||||||
|
.getByRole('button', { name: 'Actions for the time entry' })
|
||||||
|
.first();
|
||||||
await actionsDropdown.click();
|
await actionsDropdown.click();
|
||||||
const deleteButton = page.getByTestId('time_entry_delete');
|
const deleteButton = page.getByText('Delete');
|
||||||
await deleteButton.click();
|
await deleteButton.click();
|
||||||
await expect(timeEntryRows).toHaveCount(timeEntryCount - 1);
|
await expect(timeEntryRows).toHaveCount(timeEntryCount - 1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { PencilSquareIcon, TrashIcon } from '@heroicons/vue/20/solid';
|
import { PencilSquareIcon, TrashIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Client } from '@/utils/api';
|
import type { Client } from '@/utils/api';
|
||||||
import { canDeleteClients, canUpdateClients } from '@/utils/permissions';
|
import { canDeleteClients, canUpdateClients } from '@/utils/permissions';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -14,47 +14,29 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown>
|
<MoreOptionsDropdown :label="'Actions for Client ' + props.client.name">
|
||||||
<template #trigger>
|
<div class="min-w-[150px]">
|
||||||
<svg
|
<button
|
||||||
data-testid="client_actions"
|
v-if="canUpdateClients()"
|
||||||
:aria-label="'Actions for Client ' + props.client.name"
|
@click="emit('edit')"
|
||||||
class="h-10 w-10 p-2 rounded-full hover:bg-card-background opacity-20 group-hover:opacity-100 transition"
|
:aria-label="'Edit Client ' + props.client.name"
|
||||||
viewBox="0 0 24 24"
|
data-testid="client_edit"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
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">
|
||||||
<path
|
<PencilSquareIcon
|
||||||
fill="none"
|
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||||
stroke="currentColor"
|
<span>Edit</span>
|
||||||
stroke-linecap="round"
|
</button>
|
||||||
stroke-linejoin="round"
|
<button
|
||||||
stroke-width="1.5"
|
v-if="canDeleteClients()"
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
@click="emit('delete')"
|
||||||
</svg>
|
:aria-label="'Delete Client ' + props.client.name"
|
||||||
</template>
|
data-testid="client_delete"
|
||||||
<template #content>
|
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">
|
||||||
<div class="min-w-[150px]">
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
<button
|
<span>Delete</span>
|
||||||
v-if="canUpdateClients()"
|
</button>
|
||||||
@click="emit('edit')"
|
</div>
|
||||||
:aria-label="'Edit Client ' + props.client.name"
|
</MoreOptionsDropdown>
|
||||||
data-testid="client_edit"
|
|
||||||
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">
|
|
||||||
<PencilSquareIcon
|
|
||||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="canDeleteClients()"
|
|
||||||
@click="emit('delete')"
|
|
||||||
:aria-label="'Delete Client ' + props.client.name"
|
|
||||||
data-testid="client_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { TrashIcon, ArrowPathIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon, ArrowPathIcon } from '@heroicons/vue/20/solid';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
resend: [];
|
resend: [];
|
||||||
@@ -8,39 +8,22 @@ const emit = defineEmits<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown align="bottom-end">
|
<MoreOptionsDropdown label="Actions for the invitation">
|
||||||
<template #trigger>
|
<button
|
||||||
<svg
|
@click="emit('resend')"
|
||||||
data-testid="invitation_actions"
|
data-testid="invitation_delete"
|
||||||
class="h-10 w-10 p-2 rounded-full hover:bg-card-background opacity-20 group-hover:opacity-100 transition"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<ArrowPathIcon class="w-5 text-icon-active"></ArrowPathIcon>
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<span>Resend Invitation</span>
|
||||||
<path
|
</button>
|
||||||
fill="none"
|
<button
|
||||||
stroke="currentColor"
|
@click="emit('delete')"
|
||||||
stroke-linecap="round"
|
data-testid="invitation_delete"
|
||||||
stroke-linejoin="round"
|
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">
|
||||||
stroke-width="1.5"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
<span>Delete</span>
|
||||||
</svg>
|
</button>
|
||||||
</template>
|
</MoreOptionsDropdown>
|
||||||
<template #content>
|
|
||||||
<button
|
|
||||||
@click="emit('resend')"
|
|
||||||
data-testid="invitation_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">
|
|
||||||
<ArrowPathIcon class="w-5 text-icon-active"></ArrowPathIcon>
|
|
||||||
<span>Resend Invitation</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="emit('delete')"
|
|
||||||
data-testid="invitation_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Member } from '@/utils/api';
|
import type { Member } from '@/utils/api';
|
||||||
import { canDeleteMembers, canUpdateMembers } from '@/utils/permissions';
|
import { canDeleteMembers, canUpdateMembers } from '@/utils/permissions';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -14,50 +14,30 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown
|
<MoreOptionsDropdown
|
||||||
v-if="canUpdateMembers() || canDeleteMembers()"
|
v-if="canUpdateMembers() || canDeleteMembers()"
|
||||||
align="bottom-end">
|
:label="'Actions for Member ' + props.member.name">
|
||||||
<template #trigger>
|
<div class="min-w-[150px]">
|
||||||
<button
|
<button
|
||||||
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity"
|
v-if="canUpdateMembers()"
|
||||||
:aria-label="'Actions for Member ' + props.member.name">
|
@click="emit('edit')"
|
||||||
<svg
|
:aria-label="'Edit Member ' + props.member.name"
|
||||||
class="h-10 w-10 p-2 rounded-full"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<PencilSquareIcon
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||||
<path
|
<span>Edit</span>
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
<button
|
||||||
<template #content>
|
v-if="canDeleteMembers()"
|
||||||
<div class="min-w-[150px]">
|
@click="emit('delete')"
|
||||||
<button
|
:aria-label="'Delete Member ' + props.member.name"
|
||||||
v-if="canUpdateMembers()"
|
data-testid="member_delete"
|
||||||
@click="emit('edit')"
|
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">
|
||||||
:aria-label="'Edit Member ' + props.member.name"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
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">
|
<span>Delete</span>
|
||||||
<PencilSquareIcon
|
</button>
|
||||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
</div>
|
||||||
<span>Edit</span>
|
</MoreOptionsDropdown>
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="canDeleteMembers()"
|
|
||||||
@click="emit('delete')"
|
|
||||||
:aria-label="'Delete Member ' + props.member.name"
|
|
||||||
data-testid="member_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import {
|
import {
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
PencilSquareIcon,
|
PencilSquareIcon,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
} from '@heroicons/vue/20/solid';
|
} from '@heroicons/vue/20/solid';
|
||||||
import type { Project } from '@/utils/api';
|
import type { Project } from '@/utils/api';
|
||||||
import { canDeleteProjects, canUpdateProjects } from '@/utils/permissions';
|
import { canDeleteProjects, canUpdateProjects } from '@/utils/permissions';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
edit: [];
|
edit: [];
|
||||||
@@ -18,61 +18,37 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown>
|
<MoreOptionsDropdown :label="'Actions for Project ' + props.project.name">
|
||||||
<template #trigger>
|
<div class="min-w-[150px]">
|
||||||
<button
|
<button
|
||||||
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity"
|
@click.prevent="emit('edit')"
|
||||||
data-testid="project_actions"
|
v-if="canUpdateProjects()"
|
||||||
:aria-label="'Actions for Project ' + props.project.name">
|
:aria-label="'Edit Project ' + props.project.name"
|
||||||
<svg
|
data-testid="project_edit"
|
||||||
class="h-10 w-10 p-2 rounded-full"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<PencilSquareIcon
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||||
<path
|
<span>Edit</span>
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
<button
|
||||||
<template #content>
|
@click.prevent="emit('archive')"
|
||||||
<div class="min-w-[150px]">
|
v-if="canUpdateProjects()"
|
||||||
<button
|
:aria-label="'Archive Project ' + props.project.name"
|
||||||
@click.prevent="emit('edit')"
|
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">
|
||||||
v-if="canUpdateProjects()"
|
<ArchiveBoxIcon class="w-5 text-icon-active"></ArchiveBoxIcon>
|
||||||
:aria-label="'Edit Project ' + props.project.name"
|
<span>{{ project.is_archived ? 'Unarchive' : 'Archive' }}</span>
|
||||||
data-testid="project_edit"
|
</button>
|
||||||
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">
|
<button
|
||||||
<PencilSquareIcon
|
@click.prevent="emit('delete')"
|
||||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
:aria-label="'Delete Project ' + props.project.name"
|
||||||
<span>Edit</span>
|
data-testid="project_delete"
|
||||||
</button>
|
v-if="canDeleteProjects()"
|
||||||
<button
|
class="border-b border-card-background-separator 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.prevent="emit('archive')"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
v-if="canUpdateProjects()"
|
<span>Delete</span>
|
||||||
:aria-label="'Archive Project ' + props.project.name"
|
</button>
|
||||||
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">
|
</div>
|
||||||
<ArchiveBoxIcon
|
</MoreOptionsDropdown>
|
||||||
class="w-5 text-icon-active"></ArchiveBoxIcon>
|
|
||||||
<span>{{
|
|
||||||
project.is_archived ? 'Unarchive' : 'Archive'
|
|
||||||
}}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click.prevent="emit('delete')"
|
|
||||||
:aria-label="'Delete Project ' + props.project.name"
|
|
||||||
data-testid="project_delete"
|
|
||||||
v-if="canDeleteProjects()"
|
|
||||||
class="border-b border-card-background-separator 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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon, PencilSquareIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { ProjectMember } from '@/utils/api';
|
import type { ProjectMember } from '@/utils/api';
|
||||||
import { useMembersStore } from '@/utils/useMembers';
|
import { useMembersStore } from '@/utils/useMembers';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -24,46 +24,24 @@ const currentMember = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown>
|
<MoreOptionsDropdown
|
||||||
<template #trigger>
|
:label="'Actions for Project Member ' + currentMember?.name">
|
||||||
<button
|
<button
|
||||||
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity"
|
@click.prevent="emit('edit')"
|
||||||
:aria-label="
|
:aria-label="'Edit Project Member ' + currentMember?.name"
|
||||||
'Actions for Project Member ' + currentMember?.name
|
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">
|
||||||
">
|
<PencilSquareIcon class="w-5 text-icon-active"></PencilSquareIcon>
|
||||||
<svg
|
<span>Edit</span>
|
||||||
class="h-10 w-10 p-2 rounded-full"
|
</button>
|
||||||
viewBox="0 0 24 24"
|
<button
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
@click.prevent="emit('delete')"
|
||||||
<path
|
:aria-label="'Delete Project Member ' + currentMember?.name"
|
||||||
fill="none"
|
data-testid="project_delete"
|
||||||
stroke="currentColor"
|
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">
|
||||||
stroke-linecap="round"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
stroke-linejoin="round"
|
<span>Remove from Team</span>
|
||||||
stroke-width="1.5"
|
</button>
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
</MoreOptionsDropdown>
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<button
|
|
||||||
@click.prevent="emit('edit')"
|
|
||||||
:aria-label="'Edit Project Member ' + currentMember?.name"
|
|
||||||
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">
|
|
||||||
<PencilSquareIcon
|
|
||||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
|
||||||
<span>Edit</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click.prevent="emit('delete')"
|
|
||||||
:aria-label="'Delete Project Member ' + currentMember?.name"
|
|
||||||
data-testid="project_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Remove from Team</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { TrashIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon } from '@heroicons/vue/20/solid';
|
||||||
import type { Tag } from '@/utils/api';
|
import type { Tag } from '@/utils/api';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
@@ -12,34 +12,16 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown>
|
<MoreOptionsDropdown :label="'Actions for Tag ' + props.tag.name">
|
||||||
<template #trigger>
|
<button
|
||||||
<svg
|
@click="emit('delete')"
|
||||||
data-testid="tag_actions"
|
:aria-label="'Delete Tag ' + props.tag.name"
|
||||||
:aria-label="'Actions for Tag ' + props.tag.name"
|
data-testid="tag_delete"
|
||||||
class="h-10 w-10 p-2 rounded-full hover:bg-card-background opacity-20 group-hover:opacity-100 transition"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<span>Delete</span>
|
||||||
<path
|
</button>
|
||||||
fill="none"
|
</MoreOptionsDropdown>
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<button
|
|
||||||
@click="emit('delete')"
|
|
||||||
:aria-label="'Delete Tag ' + props.tag.name"
|
|
||||||
data-testid="tag_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import {
|
import {
|
||||||
TrashIcon,
|
TrashIcon,
|
||||||
PencilSquareIcon,
|
PencilSquareIcon,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
} from '@heroicons/vue/20/solid';
|
} from '@heroicons/vue/20/solid';
|
||||||
import type { Task } from '@/utils/api';
|
import type { Task } from '@/utils/api';
|
||||||
import { canDeleteTasks, canUpdateTasks } from '@/utils/permissions';
|
import { canDeleteTasks, canUpdateTasks } from '@/utils/permissions';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
edit: [];
|
edit: [];
|
||||||
@@ -18,60 +18,38 @@ const props = defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown align="bottom-end">
|
<MoreOptionsDropdown :label="'Actions for Task ' + props.task.name">
|
||||||
<template #trigger>
|
<div class="min-w-[150px]">
|
||||||
<button
|
<button
|
||||||
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity"
|
@click="emit('edit')"
|
||||||
data-testid="task_actions"
|
v-if="canUpdateTasks()"
|
||||||
:aria-label="'Actions for Task ' + props.task.name">
|
:aria-label="'Edit Task ' + props.task.name"
|
||||||
<svg
|
data-testid="task_edit"
|
||||||
class="h-10 w-10 p-2 rounded-full"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<PencilSquareIcon
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
class="w-5 text-icon-active"></PencilSquareIcon>
|
||||||
<path
|
<span>Edit</span>
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
<button
|
||||||
<template #content>
|
@click="emit('done')"
|
||||||
<div class="min-w-[150px]">
|
v-if="canUpdateTasks()"
|
||||||
<button
|
:aria-label="'Mark Task ' + props.task.name + ' as done'"
|
||||||
@click="emit('edit')"
|
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">
|
||||||
v-if="canUpdateTasks()"
|
<CheckCircleIcon class="w-5 text-icon-active"></CheckCircleIcon>
|
||||||
:aria-label="'Edit Task ' + props.task.name"
|
<span v-if="props.task.is_done">Mark as active</span>
|
||||||
data-testid="task_edit"
|
<span v-else>Mark as done</span>
|
||||||
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">
|
</button>
|
||||||
<PencilSquareIcon
|
<button
|
||||||
class="w-5 text-icon-active"></PencilSquareIcon>
|
@click="emit('delete')"
|
||||||
<span>Edit</span>
|
:aria-label="'Delete Task ' + props.task.name"
|
||||||
</button>
|
v-if="canDeleteTasks()"
|
||||||
<button
|
data-testid="task_delete"
|
||||||
@click="emit('done')"
|
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">
|
||||||
v-if="canUpdateTasks()"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
:aria-label="'Mark Task ' + props.task.name + ' as done'"
|
<span>Delete</span>
|
||||||
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">
|
</button>
|
||||||
<CheckCircleIcon
|
</div>
|
||||||
class="w-5 text-icon-active"></CheckCircleIcon>
|
</MoreOptionsDropdown>
|
||||||
<span v-if="props.task.is_done">Mark as active</span>
|
|
||||||
<span v-else>Mark as done</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="emit('delete')"
|
|
||||||
:aria-label="'Delete Task ' + props.task.name"
|
|
||||||
v-if="canDeleteTasks()"
|
|
||||||
data-testid="task_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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
@@ -1,38 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Dropdown from '@/Components/Dropdown.vue';
|
|
||||||
import { TrashIcon } from '@heroicons/vue/20/solid';
|
import { TrashIcon } from '@heroicons/vue/20/solid';
|
||||||
|
import MoreOptionsDropdown from '@/Components/MoreOptionsDropdown.vue';
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
delete: [];
|
delete: [];
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dropdown>
|
<MoreOptionsDropdown label="Actions for the time entry">
|
||||||
<template #trigger>
|
<button
|
||||||
<svg
|
@click="emit('delete')"
|
||||||
data-testid="time_entry_actions"
|
data-testid="time_entry_delete"
|
||||||
class="h-10 w-10 p-2 rounded-full hover:bg-card-background opacity-20 group-hover:opacity-100 transition"
|
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">
|
||||||
viewBox="0 0 24 24"
|
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<span>Delete</span>
|
||||||
<path
|
</button>
|
||||||
fill="none"
|
</MoreOptionsDropdown>
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="1.5"
|
|
||||||
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
<template #content>
|
|
||||||
<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">
|
|
||||||
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
|
|
||||||
<span>Delete</span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Dropdown>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
35
resources/js/Components/MoreOptionsDropdown.vue
Normal file
35
resources/js/Components/MoreOptionsDropdown.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Dropdown from '@/Components/Dropdown.vue';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
label: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Dropdown align="bottom-end">
|
||||||
|
<template #trigger>
|
||||||
|
<button
|
||||||
|
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-1 focus-visible:ring-input-border-active focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity"
|
||||||
|
:aria-label="label">
|
||||||
|
<svg
|
||||||
|
class="h-10 w-10 p-2 rounded-full"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M12 5.92A.96.96 0 1 0 12 4a.96.96 0 0 0 0 1.92m0 7.04a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92M12 20a.96.96 0 1 0 0-1.92a.96.96 0 0 0 0 1.92" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
</Dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
Reference in New Issue
Block a user