import { useEffect, useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { ArrowLeft, ArrowRight, Banknote, BriefcaseBusiness, Edit2, FolderKanban, Tag, Trash2, Users, } from 'lucide-react'; import { getClients } from '../api/clients'; import { getProjects } from '../api/projects'; import { getWorkspaceUserRates, type WorkspaceUserRate } from '../api/rates'; import { getTags } from '../api/tags'; import { deleteWorkspace, fetchWorkspaceMemberships, getWorkspace, type Workspace, type WorkspaceMembership, } from '../api/workspaces'; import { useAppContext } from '../context/AppContext'; import { useWorkspace } from '../context/WorkspaceContext'; import { useTranslation } from '../hooks/useTranslation'; import { CLIENTS_VIEW, PROJECTS_VIEW, TAGS_VIEW, WORKSPACE_DELETE, WORKSPACE_EDIT, WORKSPACE_MEMBERS_VIEW, WORKSPACE_VIEW, canWorkspace, } from '../lib/permissions'; type ResourceCounts = { projects: number; clients: number; tags: number; }; const roleBadgeStyles: Record = { owner: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300', admin: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300', member: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-300', guest: 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300', }; export default function WorkspaceDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { t, lang } = useTranslation(); const { user } = useAppContext(); const { setActiveWorkspace } = useWorkspace(); const [workspace, setWorkspace] = useState(null); const [members, setMembers] = useState([]); const [rates, setRates] = useState([]); const [counts, setCounts] = useState({ projects: 0, clients: 0, tags: 0 }); const [isLoading, setIsLoading] = useState(true); const isRtl = lang === 'fa'; const BackIcon = isRtl ? ArrowRight : ArrowLeft; useEffect(() => { if (!id) return; void loadWorkspace(); }, [id]); const loadWorkspace = async () => { try { const data = await getWorkspace(id!); setWorkspace(data); const canViewMembers = canWorkspace(data.my_role, WORKSPACE_VIEW); const canViewClients = canWorkspace(data.my_role, CLIENTS_VIEW); const canViewProjects = canWorkspace(data.my_role, PROJECTS_VIEW); const canViewTags = canWorkspace(data.my_role, TAGS_VIEW); const canViewRates = canWorkspace(data.my_role, WORKSPACE_EDIT); const tasks: Promise[] = []; if (canViewMembers) { tasks.push( fetchWorkspaceMemberships({ workspace: id!, limit: 200, offset: 0 }).then((response) => { setMembers(response.results || []); }), ); } else { setMembers([]); } if (canViewRates) { tasks.push( getWorkspaceUserRates(id!).then((response) => { setRates(response.results || []); }), ); } else { setRates([]); } tasks.push( Promise.all([ canViewProjects ? getProjects(id!, { limit: 1, offset: 0, is_archived: false }) : Promise.resolve({ count: 0 }), canViewClients ? getClients(id!, '', '', 1, 0) : Promise.resolve({ count: 0 }), canViewTags ? getTags(id!, { limit: 1, offset: 0 }) : Promise.resolve({ count: 0, results: [] }), ]).then(([projectsData, clientsData, tagsData]) => { setCounts({ projects: projectsData.count || 0, clients: clientsData.count || 0, tags: tagsData.count || 0, }); }), ); await Promise.all(tasks); } catch (error) { console.error(error); navigate('/workspaces'); } finally { setIsLoading(false); } }; const handleDelete = async () => { if (!window.confirm(t.workspace?.confirmDelete) || !id) return; try { await deleteWorkspace(id); navigate('/workspaces'); } catch (error) { console.error(error); } }; const activeMembers = members.filter((member) => member.is_active); const roleCounts = useMemo( () => activeMembers.reduce( (acc, member) => { acc[member.role] += 1; return acc; }, { owner: 0, admin: 0, member: 0, guest: 0 }, ), [activeMembers], ); const memberRateMap = useMemo( () => new Map(rates.map((rate) => [rate.user, rate])), [rates], ); const displayNumber = (value: number) => new Intl.NumberFormat(lang === 'fa' ? 'fa-IR' : 'en-US').format(value); const formatDate = (value?: string) => { if (!value) return '-'; try { return new Intl.DateTimeFormat(lang === 'fa' ? 'fa-IR' : 'en-US', { year: 'numeric', month: 'short', day: 'numeric', }).format(new Date(value)); } catch { return value; } }; const getMemberName = (member: WorkspaceMembership) => { const firstName = member.user?.first_name?.trim() || ''; const lastName = member.user?.last_name?.trim() || ''; const fullName = `${firstName} ${lastName}`.trim(); return fullName || member.user?.email || t.workspace?.unknownMember || 'Unknown member'; }; const getMemberContact = (member: WorkspaceMembership) => { return member.user?.mobile || member.user?.email || '-'; }; const getMemberInitials = (member: WorkspaceMembership) => { const firstName = member.user?.first_name?.trim()?.charAt(0) || ''; const lastName = member.user?.last_name?.trim()?.charAt(0) || ''; return `${firstName}${lastName}`.trim().toUpperCase() || getMemberName(member).charAt(0).toUpperCase(); }; const formatRateUnit = (rate?: WorkspaceUserRate) => { if (!rate) return t.rates?.noRate || 'No rate'; const unitLabel = lang === 'fa' ? rate.price_unit?.local_name || rate.price_unit?.code || rate.currency : rate.price_unit?.code || rate.currency; return `${rate.hourly_rate} ${unitLabel}`; }; const workspaceRole = workspace?.my_role; const canEdit = canWorkspace(workspaceRole, WORKSPACE_EDIT); const canDelete = canWorkspace(workspaceRole, WORKSPACE_DELETE); const canViewMembers = canWorkspace(workspaceRole, WORKSPACE_VIEW); const canViewMemberSensitiveDetails = canWorkspace(workspaceRole, WORKSPACE_MEMBERS_VIEW); const canViewReports = canWorkspace(workspaceRole, WORKSPACE_VIEW); if (isLoading || !workspace) { return
{t.workspace?.loading}
; } const openWorkspaceRoute = (path: string) => { setActiveWorkspace(workspace); navigate(path); }; const resourceCards = [ { key: 'projects', title: t.sidebar?.projects || 'Projects', value: displayNumber(counts.projects), icon: FolderKanban, onClick: () => openWorkspaceRoute('/projects'), visible: canWorkspace(workspace.my_role, PROJECTS_VIEW), }, { key: 'clients', title: t.sidebar?.clients || 'Clients', value: displayNumber(counts.clients), icon: BriefcaseBusiness, onClick: () => openWorkspaceRoute('/clients'), visible: canWorkspace(workspace.my_role, CLIENTS_VIEW), }, { key: 'tags', title: t.sidebar?.tags || 'Tags', value: displayNumber(counts.tags), icon: Tag, onClick: () => openWorkspaceRoute('/tags'), visible: canWorkspace(workspace.my_role, TAGS_VIEW), }, { key: 'reports', title: t.sidebar?.reports || 'Reports', value: t.workspace?.resourceOpen || 'Open', icon: Banknote, onClick: () => openWorkspaceRoute('/reports'), visible: canViewReports, }, ].filter((item) => item.visible); return (

{workspace.name}

{workspace.my_role ? t.workspace?.roles[workspace.my_role] : '-'}

{workspace.description || t.workspace?.noDescription}

{canViewReports && ( )} {canEdit && ( )} {canDelete && ( )}
{t.workspace?.statsMembers || 'Members'}

{displayNumber(activeMembers.length)}

{t.workspace?.statsRates || 'Rates set'}

{displayNumber(rates.length)}

{t.workspace?.statsOwnersAdmins || 'Owners & admins'}

{displayNumber(roleCounts.owner + roleCounts.admin)}

{t.workspace?.statsGuests || 'Guests'}

{displayNumber(roleCounts.guest)}

{t.workspace?.resourcesTitle || 'Resources'}

{resourceCards.map((card) => { const Icon = card.icon; return ( ); })}

{t.workspace?.membersSectionTitle || t.workspace?.members || 'Members'}

{canViewMembers ?

{ t.workspace?.membersSectionSubtitle || 'People in this workspace and their current roles.' }

: ''}
{canEdit && ( )}
{canViewMembers ? (
6 ? 'max-h-[36rem] overflow-y-auto' : ''}`}> {activeMembers.length === 0 ? (
{t.workspace?.noMembers || 'No members found.'}
) : ( activeMembers.map((member) => { const rate = memberRateMap.get(member.user.id); const isCurrentUser = member.user.id === user?.id; return (
{member.user?.profile_picture ? ( {getMemberName(member)} ) : ( {getMemberInitials(member)} )}

{getMemberName(member)}

{isCurrentUser && ( {t.workspace?.youLabel || 'You'} )} {t.workspace?.roles[member.role]}
{canViewMemberSensitiveDetails && (

{t.workspace?.mobileNumber || 'Mobile Number'}: {getMemberContact(member)}

)}
{canViewMemberSensitiveDetails && (

{t.rates?.workspaceRate || 'Workspace rate'}

{formatRateUnit(rate)}

)}
); }) )}
) : (
{t.workspace?.membersLocked || 'This member list is not available for your current role.'}
)}
); }