74 lines
3.7 KiB
TypeScript
74 lines
3.7 KiB
TypeScript
"use client";
|
||
|
||
import { useQuery } from "@tanstack/react-query";
|
||
import { Card, CardContent, PageHeader, StatCard } from "@/components/ds";
|
||
import { useCurrentHub } from "@/modules/workspace/hooks/useCurrentHub";
|
||
import { workspaceApi } from "@/modules/workspace/services/workspace-api";
|
||
import { useTenantMembers } from "@/modules/workspace/hooks/useTenantMembers";
|
||
import { CONTENT_STATUS, labelOf, personLabel } from "@/modules/workspace/labels";
|
||
import { WorkspaceEmptyState, WorkspacePageError, WorkspacePageLoader } from "@/modules/workspace/pages/shared";
|
||
|
||
export const WorkspaceReports = function WorkspaceReportsPage() {
|
||
const { tenantId, hubId, loading } = useCurrentHub();
|
||
const people = useTenantMembers();
|
||
const directory = Object.fromEntries((people.data ?? []).map((person) => [person.id, person.label]));
|
||
const dash = useQuery({
|
||
queryKey: ["workspace", tenantId, "dashboard", hubId],
|
||
queryFn: () => workspaceApi.hubs.dashboard(tenantId!, hubId!),
|
||
enabled: !!tenantId && !!hubId,
|
||
});
|
||
|
||
if (!tenantId || loading || dash.isLoading) return <WorkspacePageLoader />;
|
||
if (!hubId) return <WorkspaceEmptyState title="مرکز کار لازم است" description="ابتدا مرکز کار را انتخاب کنید." />;
|
||
if (dash.error) return <WorkspacePageError error={dash.error} onRetry={() => dash.refetch()} />;
|
||
const data = dash.data!;
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<PageHeader title="گزارشها" description="اعداد از پایگاه داده فضای کاری خوانده میشوند؛ مقدار ثابت وجود ندارد." />
|
||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||
<StatCard label="پروژههای فعال" value={data.overview.active_projects} />
|
||
<StatCard label="وظایف باز" value={data.overview.open_tasks} />
|
||
<StatCard label="عقبافتاده" value={data.overview.overdue_tasks} />
|
||
<StatCard label="صف تأیید" value={data.overview.pending_approvals} />
|
||
<StatCard label="در انتظار بررسی" value={data.overview.content_awaiting_review} />
|
||
<StatCard label="زمانبندیشده" value={data.overview.scheduled_content} />
|
||
<StatCard label="اعضای تیم" value={data.overview.team_members} />
|
||
<StatCard label="مهلت نزدیک" value={data.overview.upcoming_deadlines} />
|
||
</div>
|
||
<div className="grid gap-4 lg:grid-cols-2">
|
||
<Card>
|
||
<CardContent className="space-y-2 p-5">
|
||
<h2 className="font-semibold text-secondary">وضعیت تولید</h2>
|
||
{Object.entries(data.content).map(([status, count]) => (
|
||
<div key={status} className="flex justify-between text-sm">
|
||
<span>{labelOf(CONTENT_STATUS, status)}</span>
|
||
<span>{count}</span>
|
||
</div>
|
||
))}
|
||
</CardContent>
|
||
</Card>
|
||
<Card>
|
||
<CardContent className="space-y-2 p-5">
|
||
<h2 className="font-semibold text-secondary">بار کاری</h2>
|
||
{data.workload.length === 0 ? (
|
||
<p className="text-sm text-[var(--muted)]">تخصیصی ثبت نشده است.</p>
|
||
) : (
|
||
data.workload.map((row) => (
|
||
<div key={row.user_id} className="flex justify-between text-sm">
|
||
<span>{personLabel(row.user_id, directory)}</span>
|
||
<span>
|
||
{row.tasks} وظیفه / {row.content} محتوا
|
||
</span>
|
||
</div>
|
||
))
|
||
)}
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default WorkspaceReports;
|