refactor to shadcn components, dynamically load extension frontend

add jetstream permissions, add dynamic inertia module loading, add shadcn components, change modals and dropdowns to shadcn dismissable layer,
This commit is contained in:
Gregor Vostrak
2025-04-13 23:06:58 +02:00
parent 4605aa75ff
commit 132b6cbe8f
204 changed files with 5458 additions and 1275 deletions

View File

@@ -109,7 +109,7 @@ function updateValue(project: Project) {
</script>
<template>
<Dropdown v-model="open" align="bottom-start" width="60">
<Dropdown v-model="open" align="start" width="60">
<template #trigger>
<ProjectBadge
ref="projectDropdownTrigger"

View File

@@ -6,7 +6,13 @@ import {
} from '@heroicons/vue/20/solid';
import type { Project } from '@/packages/api/src';
import { canDeleteProjects, canUpdateProjects } from '@/utils/permissions';
import MoreOptionsDropdown from '@/packages/ui/src/MoreOptionsDropdown.vue';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/Components/ui/dropdown-menu';
const emit = defineEmits<{
delete: [];
edit: [];
@@ -18,37 +24,54 @@ const props = defineProps<{
</script>
<template>
<MoreOptionsDropdown :label="'Actions for Project ' + props.project.name">
<div class="min-w-[150px]">
<DropdownMenu>
<DropdownMenuTrigger as-child>
<button
class="focus-visible:outline-none focus-visible:bg-card-background rounded-full focus-visible:ring-2 focus-visible:ring-ring focus-visible:opacity-100 hover:bg-card-background group-hover:opacity-100 opacity-20 transition-opacity text-text-secondary"
:aria-label="'Actions for Project ' + props.project.name">
<svg
class="h-8 w-8 p-1 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>
</DropdownMenuTrigger>
<DropdownMenuContent class="min-w-[150px]" align="end">
<DropdownMenuItem
v-if="canUpdateProjects()"
:aria-label="'Edit Project ' + props.project.name"
data-testid="project_edit"
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary 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 cursor-pointer"
@click.prevent="emit('edit')">
<PencilSquareIcon
class="w-5 text-icon-active"></PencilSquareIcon>
<PencilSquareIcon class="w-5 text-icon-active" />
<span>Edit</span>
</button>
<button
</DropdownMenuItem>
<DropdownMenuItem
v-if="canUpdateProjects()"
:aria-label="'Archive Project ' + props.project.name"
class="flex items-center space-x-3 w-full px-3 py-2.5 text-start text-sm font-medium leading-5 text-text-primary 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 cursor-pointer"
@click.prevent="emit('archive')">
<ArchiveBoxIcon class="w-5 text-icon-active"></ArchiveBoxIcon>
<ArchiveBoxIcon class="w-5 text-icon-active" />
<span>{{ project.is_archived ? 'Unarchive' : 'Archive' }}</span>
</button>
<button
</DropdownMenuItem>
<DropdownMenuItem
v-if="canDeleteProjects()"
:aria-label="'Delete Project ' + props.project.name"
data-testid="project_delete"
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-text-primary 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 cursor-pointer text-destructive focus:text-destructive"
@click.prevent="emit('delete')">
<TrashIcon class="w-5 text-icon-active"></TrashIcon>
<TrashIcon class="w-5" />
<span>Delete</span>
</button>
</div>
</MoreOptionsDropdown>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</template>
<style scoped></style>

View File

@@ -79,9 +79,9 @@ const showEditProjectModal = ref(false);
<span class="overflow-ellipsis overflow-hidden">
{{ project.name }}
</span>
<span class="text-muted"> {{ projectTasksCount }} Tasks </span>
<span class="text-text-secondary"> {{ projectTasksCount }} Tasks </span>
</div>
<div class="whitespace-nowrap min-w-0 px-3 py-4 text-sm text-muted">
<div class="whitespace-nowrap min-w-0 px-3 py-4 text-sm text-text-secondary">
<div
v-if="project.client_id"
class="overflow-ellipsis overflow-hidden">
@@ -89,14 +89,14 @@ const showEditProjectModal = ref(false);
</div>
<div v-else>No client</div>
</div>
<div class="whitespace-nowrap px-3 py-4 text-sm text-muted">
<div class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary">
<div v-if="project.spent_time">
{{ formatHumanReadableDuration(project.spent_time) }}
</div>
<div v-else>--</div>
</div>
<div
class="whitespace-nowrap px-3 flex items-center text-sm text-muted">
class="whitespace-nowrap px-3 flex items-center text-sm text-text-secondary">
<UpgradeBadge
v-if="!isAllowedToPerformPremiumAction()"></UpgradeBadge>
<EstimatedTimeProgress
@@ -107,11 +107,11 @@ const showEditProjectModal = ref(false);
</div>
<div
v-if="showBillableRate"
class="whitespace-nowrap px-3 py-4 text-sm text-muted">
class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary">
{{ billableRateInfo }}
</div>
<div
class="whitespace-nowrap px-3 py-4 text-sm text-muted flex space-x-1 items-center font-medium">
class="whitespace-nowrap px-3 py-4 text-sm text-text-secondary flex space-x-1 items-center font-medium">
<CheckCircleIcon class="w-5"></CheckCircleIcon>
<span>Active</span>
</div>