work-hub-server/apps/web/app/pages/projects/index.vue

118 lines
3.7 KiB
Vue

<script setup lang="ts">
import type { TableColumn } from "@nuxt/ui";
import type { Project } from "@workspace/schema";
definePageMeta({
title: "Projects",
});
const { projects, loading, error, fetchProjects, deleteProject } = useProjectApi();
const deleting = ref<string | null>(null);
const confirmDelete = ref(false);
onMounted(() => fetchProjects());
function statusColor(status: string) {
if (status === "active") return "success" as const;
if (status === "archived") return "neutral" as const;
if (status === "completed") return "info" as const;
return "neutral" as const;
}
async function confirmAndDelete(id: string) {
deleting.value = id;
await deleteProject(id);
deleting.value = null;
confirmDelete.value = false;
}
const columns: TableColumn<Project>[] = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "status", header: "Status" },
{
accessorKey: "description",
header: "Description",
cell: ({ row }) => row.getValue("description") ?? "—",
},
{ accessorKey: "createdAt", header: "Created" },
{ id: "actions", header: "" },
];
</script>
<template>
<UPage>
<UPageHeader
title="Projects"
description="Manage your projects"
:links="[{ label: 'New project', icon: 'i-lucide-plus', to: '/projects/new', color: 'primary' }]" />
<UPageBody>
<UAlert v-if="error" color="error" variant="soft" icon="i-lucide-alert-circle" :title="error" class="mb-4" />
<UCard>
<UTable :columns="columns" :data="projects" :loading="loading" class="flex-1">
<template #status-cell="{ row }">
<UBadge
:label="row.getValue('status')"
:color="statusColor(row.getValue('status') as string)"
variant="subtle"
class="capitalize" />
</template>
<template #createdAt-cell="{ row }">
<span class="text-muted">{{ new Date(row.getValue("createdAt") as string).toLocaleDateString() }}</span>
</template>
<template #actions-cell="{ row }">
<div class="flex justify-end gap-1">
<UButton
icon="i-lucide-pencil"
color="neutral"
variant="ghost"
size="sm"
:to="`/projects/${row.original.id}/edit`"
aria-label="Edit" />
<UButton
icon="i-lucide-trash-2"
color="error"
variant="ghost"
size="sm"
:loading="deleting === row.original.id"
aria-label="Delete"
@click="
confirmDelete = true;
deleting = row.original.id;
" />
</div>
</template>
<template #empty>
<div class="py-12 text-center text-muted">
<UIcon name="i-lucide-folder-open" class="size-8 mx-auto mb-2" />
<p>No projects yet.</p>
</div>
</template>
</UTable>
</UCard>
<UModal v-model:open="confirmDelete">
<template #header>
<h3 class="text-lg font-semibold">Delete project</h3>
</template>
<template #body>
<p class="text-sm text-muted">Are you sure you want to delete this project? This action cannot be undone.</p>
</template>
<template #footer>
<div class="flex gap-3 justify-end">
<UButton label="Cancel" color="neutral" variant="outline" @click="confirmDelete = false" />
<UButton label="Delete" color="error" @click="deleting && confirmAndDelete(deleting)" />
</div>
</template>
</UModal>
</UPageBody>
</UPage>
</template>