refactor(lists): align client and project page controls

This commit is contained in:
2026-04-27 20:52:18 +03:30
parent 8ecf317700
commit 1e5f0b6b5e
3 changed files with 211 additions and 217 deletions

View File

@@ -1,13 +1,13 @@
import { useEffect, useState } from "react"
import { Plus, Building2, Loader2, Pencil, Trash2 } from "lucide-react"
import { useWorkspace } from "../context/WorkspaceContext"
import { useTranslation } from "../hooks/useTranslation"
import {
CLIENTS_CREATE,
CLIENTS_DELETE,
CLIENTS_EDIT,
canWorkspace,
} from "../lib/permissions"
import { useTranslation } from "../hooks/useTranslation"
import {
CLIENTS_CREATE,
CLIENTS_DELETE,
CLIENTS_EDIT,
canWorkspace,
} from "../lib/permissions"
import { type Client } from "../types/client"
import { getClients } from "../api/clients"
import CreateClientModal from "../components/CreateClientModal"
@@ -38,12 +38,12 @@ export default function Clients() {
const [editClient, setEditClient] = useState<Client | null>(null)
const [deleteClient, setDeleteClient] = useState<Client | null>(null)
const { t, lang } = useTranslation()
const isFa = lang === "fa"
const workspaceRole = activeWorkspace?.my_role
const canCreateClient = canWorkspace(workspaceRole, CLIENTS_CREATE)
const canEditClient = canWorkspace(workspaceRole, CLIENTS_EDIT)
const canDeleteClient = canWorkspace(workspaceRole, CLIENTS_DELETE)
const { t, lang } = useTranslation()
const isFa = lang === "fa"
const workspaceRole = activeWorkspace?.my_role
const canCreateClient = canWorkspace(workspaceRole, CLIENTS_CREATE)
const canEditClient = canWorkspace(workspaceRole, CLIENTS_EDIT)
const canDeleteClient = canWorkspace(workspaceRole, CLIENTS_DELETE)
const orderingOptions = [
{ value: "-created_at", label: t.ordering?.createdAtDesc || "Newest First" },
@@ -116,24 +116,24 @@ export default function Clients() {
return (
<div className="flex flex-col p-6 min-h-[calc(100vh-73px)] bg-slate-50 dark:bg-slate-900">
<div className="flex justify-between items-center mb-8 gap-4">
<div>
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">{t.clients.title}</h1>
<p className="text-slate-500 dark:text-slate-400 text-sm mt-1">
{t.clients.description(activeWorkspace.name)}
</p>
</div>
{canCreateClient && (
<Button
onClick={() => setIsCreateModalOpen(true)}
size="icon"
className="shadow-sm shrink-0"
title={t.clients.addClient}
>
<Plus className="w-4 h-4" />
</Button>
)}
<div className="flex justify-between items-center mb-8 gap-4">
<div>
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">{t.clients.title}</h1>
<p className="text-slate-500 dark:text-slate-400 text-sm mt-1">
{t.clients.description(activeWorkspace.name)}
</p>
</div>
{canCreateClient && (
<Button
onClick={() => setIsCreateModalOpen(true)}
size="icon"
className="shadow-sm shrink-0"
title={t.clients.addClient}
>
<Plus className="w-5 h-5" />
</Button>
)}
</div>
<FilterBar
@@ -170,35 +170,32 @@ export default function Clients() {
{client.notes}
</p>
)}
<div className="text-[11px] text-slate-400 mt-3 font-medium">
{t.clients.addedOn}: {formatDate(client.created_at)}
</div>
</div>
{(canEditClient || canDeleteClient) && (
<div className="flex items-center gap-1 shrink-0">
{canEditClient && (
<Button
variant="ghost"
size="icon"
onClick={() => setEditClient(client)}
className="h-8 w-8 text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:text-blue-400 dark:hover:bg-blue-900/20"
>
<Pencil className="w-4 h-4" />
</Button>
)}
{canDeleteClient && (
<Button
variant="ghost"
size="icon"
onClick={() => setDeleteClient(client)}
className="h-8 w-8 text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:text-red-400 dark:hover:bg-red-900/20"
>
<Trash2 className="w-4 h-4" />
</Button>
)}
</div>
)}
{(canEditClient || canDeleteClient) && (
<div className="flex items-center gap-1 shrink-0">
{canEditClient && (
<Button
variant="ghost"
size="icon"
onClick={() => setEditClient(client)}
className="h-8 w-8 text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:text-blue-400 dark:hover:bg-blue-900/20"
>
<Pencil className="w-4 h-4" />
</Button>
)}
{canDeleteClient && (
<Button
variant="ghost"
size="icon"
onClick={() => setDeleteClient(client)}
className="h-8 w-8 text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:text-red-400 dark:hover:bg-red-900/20"
>
<Trash2 className="w-4 h-4" />
</Button>
)}
</div>
)}
</li>
))}
</ul>
@@ -216,32 +213,32 @@ export default function Clients() {
/>
)}
{canCreateClient && (
<CreateClientModal
isOpen={isCreateModalOpen}
onClose={() => setIsCreateModalOpen(false)}
onSuccess={fetchClientsList}
workspaceId={activeWorkspace.id}
/>
)}
{canEditClient && (
<EditClientModal
isOpen={!!editClient}
onClose={() => setEditClient(null)}
onSuccess={fetchClientsList}
client={editClient}
/>
)}
{canDeleteClient && (
<DeleteClientModal
isOpen={!!deleteClient}
onClose={() => setDeleteClient(null)}
onSuccess={fetchClientsList}
client={deleteClient}
/>
)}
{canCreateClient && (
<CreateClientModal
isOpen={isCreateModalOpen}
onClose={() => setIsCreateModalOpen(false)}
onSuccess={fetchClientsList}
workspaceId={activeWorkspace.id}
/>
)}
{canEditClient && (
<EditClientModal
isOpen={!!editClient}
onClose={() => setEditClient(null)}
onSuccess={fetchClientsList}
client={editClient}
/>
)}
{canDeleteClient && (
<DeleteClientModal
isOpen={!!deleteClient}
onClose={() => setDeleteClient(null)}
onSuccess={fetchClientsList}
client={deleteClient}
/>
)}
</div>
)
}