TorbatYar/frontend/modules/workspace/features/teams.tsx
Mortezakoohjani a483b47369 feat(workspace): complete production workspace and content operations
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-12 11:54:17 +03:30

89 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState } from "react";
import Link from "next/link";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { Button, Card, CardContent, Dialog, Input, PageHeader, Textarea } from "@/components/ds";
import { useCurrentHub } from "@/modules/workspace/hooks/useCurrentHub";
import { workspaceApi } from "@/modules/workspace/services/workspace-api";
import { TEAM_STATUS, labelOf } from "@/modules/workspace/labels";
import { WorkspaceEmptyState, WorkspacePageError, WorkspacePageLoader } from "@/modules/workspace/pages/shared";
export const WorkspaceTeams = function WorkspaceTeamsPage() {
const queryClient = useQueryClient();
const { tenantId, hubId, loading: hubLoading } = useCurrentHub();
const [open, setOpen] = useState(false);
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const teams = useQuery({
queryKey: ["workspace", tenantId, "teams", hubId],
queryFn: () => workspaceApi.teams.list(tenantId!, hubId || undefined),
enabled: !!tenantId,
});
const create = useMutation({
mutationFn: () => workspaceApi.teams.create(tenantId!, { work_hub_id: hubId, name, description }),
onSuccess: () => {
toast.success("تیم ایجاد شد");
setOpen(false);
setName("");
setDescription("");
void queryClient.invalidateQueries({ queryKey: ["workspace", tenantId, "teams"] });
},
onError: (err) => toast.error(err instanceof Error ? err.message : "ایجاد تیم ناموفق بود"),
});
if (!tenantId || hubLoading || teams.isLoading) return <WorkspacePageLoader />;
if (!hubId) {
return <WorkspaceEmptyState title="مرکز کار لازم است" description="ابتدا یک مرکز کار بسازید." />;
}
if (teams.error) return <WorkspacePageError error={teams.error} onRetry={() => teams.refetch()} />;
return (
<div className="space-y-6">
<PageHeader
title="تیم‌ها"
description="اعضای تیم را با نقش مالک، مدیر، عضو یا ناظر مدیریت کنید."
actions={<Button onClick={() => setOpen(true)}>تیم جدید</Button>}
/>
{(teams.data ?? []).length === 0 ? (
<WorkspaceEmptyState
title="تیمی ثبت نشده"
description="برای واگذاری پروژه و محتوا، یک تیم بسازید."
action={<Button onClick={() => setOpen(true)}>ایجاد تیم</Button>}
/>
) : (
<div className="grid gap-4 md:grid-cols-2">
{(teams.data ?? []).map((team) => (
<Card key={String(team.id)}>
<CardContent className="space-y-2 p-5">
<div className="flex items-start justify-between">
<h2 className="font-semibold text-secondary">{String(team.name || "تیم")}</h2>
<span className="text-xs text-[var(--muted)]">{labelOf(TEAM_STATUS, String(team.status || ""))}</span>
</div>
<p className="text-sm text-[var(--muted)]">{String(team.description || "بدون توضیح")}</p>
<Link href={`/workspace/teams/${team.id}`}>
<Button variant="outline" className="w-full">
اعضای تیم
</Button>
</Link>
</CardContent>
</Card>
))}
</div>
)}
<Dialog open={open} onClose={() => setOpen(false)} title="تیم جدید">
<div className="space-y-3">
<Input value={name} onChange={(event) => setName(event.target.value)} placeholder="نام تیم" />
<Textarea value={description} onChange={(event) => setDescription(event.target.value)} placeholder="توضیح" />
<Button className="w-full" disabled={!name.trim() || create.isPending} onClick={() => create.mutate()}>
ایجاد
</Button>
</div>
</Dialog>
</div>
);
};
export default WorkspaceTeams;