style(tags): align tags page with list layout
This commit is contained in:
@@ -8,6 +8,7 @@ import { useWorkspace } from "../context/WorkspaceContext";
|
|||||||
import { useTranslation } from "../hooks/useTranslation";
|
import { useTranslation } from "../hooks/useTranslation";
|
||||||
import { TAGS_CREATE, TAGS_EDIT, canDeleteWorkspaceResource, canWorkspace } from "../lib/permissions";
|
import { TAGS_CREATE, TAGS_EDIT, canDeleteWorkspaceResource, canWorkspace } from "../lib/permissions";
|
||||||
import FilterBar from "../components/FilterBar";
|
import FilterBar from "../components/FilterBar";
|
||||||
|
import { ListPageSkeleton } from "../components/ListPageSkeleton";
|
||||||
import { Modal } from "../components/Modal";
|
import { Modal } from "../components/Modal";
|
||||||
import { Pagination } from "../components/Pagination";
|
import { Pagination } from "../components/Pagination";
|
||||||
import { Button } from "../components/ui/button";
|
import { Button } from "../components/ui/button";
|
||||||
@@ -140,39 +141,50 @@ export default function Tags() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!activeWorkspace) {
|
if (!activeWorkspace) {
|
||||||
return <div className="p-6 text-center text-slate-500">{t.tags?.selectWorkspace || t.clients.selectWorkspace}</div>;
|
return (
|
||||||
|
<div className="mx-auto max-w-7xl p-4 md:p-6">
|
||||||
|
<div className="rounded-3xl border border-slate-200 bg-white p-6 text-slate-600 shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:text-slate-300">
|
||||||
|
{t.tags?.selectWorkspace || t.clients.selectWorkspace}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col p-6 min-h-[calc(100vh-73px)] bg-slate-50 dark:bg-slate-900">
|
<div className="mx-auto flex min-h-full max-w-7xl flex-col p-4 md:p-6">
|
||||||
<div className="flex justify-between items-center mb-8 gap-4">
|
<div className="flex flex-1 flex-col gap-5">
|
||||||
<div>
|
<div className="rounded-3xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900 sm:p-6">
|
||||||
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">{t.tags?.title || "Tags"}</h1>
|
<div className="flex items-start justify-between gap-4">
|
||||||
<p className="text-slate-500 dark:text-slate-400 mt-1">
|
<div>
|
||||||
{t.tags?.description?.(activeWorkspace.name) || `Manage tags for ${activeWorkspace.name}`}
|
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">{t.tags?.title || "Tags"}</h1>
|
||||||
</p>
|
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
||||||
|
{t.tags?.description?.(activeWorkspace.name) || `Manage tags for ${activeWorkspace.name}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{canCreateTag && (
|
||||||
|
<Button onClick={openCreateModal} size="icon" className="shrink-0 shadow-sm" title={t.tags?.create || "Create Tag"}>
|
||||||
|
<Plus className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{canCreateTag && (
|
|
||||||
<Button onClick={openCreateModal} size="icon" className="shadow-sm shrink-0" title={t.tags?.create || "Create Tag"}>
|
|
||||||
<Plus className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FilterBar
|
<div className="rounded-3xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900 sm:p-5">
|
||||||
searchQuery={searchQuery}
|
<FilterBar
|
||||||
setSearchQuery={setSearchQuery}
|
searchQuery={searchQuery}
|
||||||
ordering={ordering}
|
setSearchQuery={setSearchQuery}
|
||||||
setOrdering={setOrdering}
|
ordering={ordering}
|
||||||
orderingOptions={orderingOptions}
|
setOrdering={setOrdering}
|
||||||
searchPlaceholder={t.tags?.searchPlaceholder || "Search tags..."}
|
orderingOptions={orderingOptions}
|
||||||
/>
|
searchPlaceholder={t.tags?.searchPlaceholder || "Search tags..."}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="p-12 flex justify-center text-slate-500">{t.loading || "Loading..."}</div>
|
<ListPageSkeleton variant="dense-grid" />
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col flex-1">
|
<div className="flex flex-1 flex-col gap-6">
|
||||||
<div className="mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4">
|
||||||
{tags.map((tag) => {
|
{tags.map((tag) => {
|
||||||
const canDeleteTag = canDeleteWorkspaceResource({
|
const canDeleteTag = canDeleteWorkspaceResource({
|
||||||
workspaceRole,
|
workspaceRole,
|
||||||
@@ -196,7 +208,13 @@ export default function Tags() {
|
|||||||
{(canEditTag || canDeleteTag) && (
|
{(canEditTag || canDeleteTag) && (
|
||||||
<div className="flex shrink-0 items-center gap-1">
|
<div className="flex shrink-0 items-center gap-1">
|
||||||
{canEditTag && (
|
{canEditTag && (
|
||||||
<Button variant="ghost" size="icon" onClick={() => openEditModal(tag)} title={t.actions?.edit || "Edit"}>
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => openEditModal(tag)}
|
||||||
|
className="h-8 w-8 text-slate-400 hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-900/20 dark:hover:text-blue-400"
|
||||||
|
title={t.actions?.edit || "Edit"}
|
||||||
|
>
|
||||||
<Edit2 className="w-4 h-4" />
|
<Edit2 className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -205,9 +223,10 @@ export default function Tags() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => setDeleteModal({ isOpen: true, tag })}
|
onClick={() => setDeleteModal({ isOpen: true, tag })}
|
||||||
|
className="h-8 w-8 text-slate-400 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400"
|
||||||
title={t.actions?.delete || "Delete"}
|
title={t.actions?.delete || "Delete"}
|
||||||
>
|
>
|
||||||
<Trash2 className="w-4 h-4 text-red-500" />
|
<Trash2 className="w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -219,9 +238,9 @@ export default function Tags() {
|
|||||||
})}
|
})}
|
||||||
|
|
||||||
{tags.length === 0 && (
|
{tags.length === 0 && (
|
||||||
<div className="py-16 flex flex-col items-center justify-center border-2 border-dashed border-slate-200 dark:border-slate-800 rounded-2xl text-slate-500 dark:text-slate-400">
|
<div className="col-span-full flex flex-1 flex-col items-center justify-center rounded-3xl border-2 border-dashed border-slate-200 bg-white py-16 text-slate-500 shadow-sm dark:border-slate-800 dark:bg-slate-900 dark:text-slate-400">
|
||||||
<TagIcon className="w-10 h-10 mb-3" />
|
<TagIcon className="w-10 h-10 mb-3" />
|
||||||
<p>{t.tags?.emptyState || "No tags found"}</p>
|
<p className="font-medium">{t.tags?.emptyState || "No tags found"}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -235,6 +254,7 @@ export default function Tags() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
|
|||||||
Reference in New Issue
Block a user