feat(permissions): gate workspace resources by role
This commit is contained in:
@@ -1,7 +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 { 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"
|
||||
@@ -32,8 +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 { 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" },
|
||||
@@ -114,10 +124,12 @@ export default function Clients() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button onClick={() => setIsCreateModalOpen(true)} className="flex items-center gap-2">
|
||||
<Plus className="w-4 h-4" />
|
||||
{t.clients.addClient}
|
||||
</Button>
|
||||
{canCreateClient && (
|
||||
<Button onClick={() => setIsCreateModalOpen(true)} className="flex items-center gap-2">
|
||||
<Plus className="w-4 h-4" />
|
||||
{t.clients.addClient}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<FilterBar
|
||||
@@ -159,24 +171,30 @@ export default function Clients() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<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>
|
||||
<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>
|
||||
@@ -194,26 +212,32 @@ export default function Clients() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<CreateClientModal
|
||||
isOpen={isCreateModalOpen}
|
||||
onClose={() => setIsCreateModalOpen(false)}
|
||||
onSuccess={fetchClientsList}
|
||||
workspaceId={activeWorkspace.id}
|
||||
/>
|
||||
|
||||
<EditClientModal
|
||||
isOpen={!!editClient}
|
||||
onClose={() => setEditClient(null)}
|
||||
onSuccess={fetchClientsList}
|
||||
client={editClient}
|
||||
/>
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user