import { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { CheckCircle2, ChevronDown, ChevronUp, Folder, RefreshCw } from "lucide-react";
import { dashboardTaskKey, devLogin, getDashboardToday, getDevRoles } from "../lib/api";
import type { DashboardTask, Project } from "../lib/api";
import { Card, CardSkeleton, Chip, EmptyState, ErrorState, PageHeader, PanelsMenu, ProgressBar } from "../components/ui";
import { formatDate, isOverdue } from "../lib/format";
import { roleLabels, scopeLabel } from "../lib/roles";
import { useAuthStore } from "../store/auth";
type SectionKey = "overdue" | "due_today" | "awaiting_qc" | "failed_qc" | "blocked" | "upcoming";
interface SectionMeta {
key: SectionKey;
title: string;
tone: "red" | "amber" | "sky" | "slate";
}
/**
* بخشهای داشبورد به تفکیک نقش:
* مدیر پروژه نگاه مدیریتی دارد (تأخیرها و QC همه پروژهها) و سرپرست پروژه نگاه
* عملیاتی (کار امروز و کاری که باید همین حالا اجرا/بازرسی شود).
*/
const managerSections: SectionMeta[] = [
{ key: "overdue", title: "عقبافتاده", tone: "red" },
{ key: "due_today", title: "کارهای امروز", tone: "sky" },
{ key: "awaiting_qc", title: "منتظر QC", tone: "amber" },
{ key: "failed_qc", title: "QC مردود", tone: "red" },
{ key: "blocked", title: "مسدود شده", tone: "slate" },
{ key: "upcoming", title: "هفته آینده", tone: "sky" },
];
const supervisorSections: SectionMeta[] = [
{ key: "due_today", title: "کارهای امروز", tone: "sky" },
{ key: "overdue", title: "عقبافتاده", tone: "red" },
{ key: "awaiting_qc", title: "منتظر اجرای بازرسی", tone: "amber" },
{ key: "failed_qc", title: "بازرسی مردود", tone: "red" },
{ key: "blocked", title: "مسدود شده (پیشنیاز ناتمام)", tone: "slate" },
{ key: "upcoming", title: "هفته آینده", tone: "sky" },
];
function TaskRow({ task, showBlockedBy = false }: { task: DashboardTask; showBlockedBy?: boolean }) {
const overdue = isOverdue(task.due_date);
// شناسهٔ پایدار: UUID تسک بومی، وگرنه op_wp_id میراث
const taskKey = dashboardTaskKey(task);
return (
{task.subject}
{formatDate(task.due_date)}
{task.project_name}
{showBlockedBy && task.blocked_by && (
مسدود توسط: {task.blocked_by}
)}
);
}
function SummaryBox({ tone, label, value }: { tone: "red" | "amber" | "green"; label: string; value: number }) {
const cls = {
red: "border-red-100 bg-red-50 text-red-600",
amber: "border-amber-100 bg-amber-50 text-amber-600",
green: "border-emerald-100 bg-emerald-50 text-emerald-600",
}[tone];
return (
);
}
/** کاشی مدیریتی یک پروژه — فقط در داشبورد مدیر پروژه */
function ProjectTile({ project }: { project: Project }) {
return (
{project.name}
{project.stats.total} فعالیت
{project.stats.overdue > 0 && {project.stats.overdue} عقبافتاده}
{project.stats.awaiting_qc > 0 && {project.stats.awaiting_qc} منتظر QC}
{project.stats.failed_qc > 0 && {project.stats.failed_qc} مردود}
);
}
/** انتخابگر نقش — فقط در فاز توسعه (سرور آن را با PM_DEV_ROLE_SWITCH کنترل میکند) */
function DevRoleSwitcher() {
const user = useAuthStore((s) => s.user);
const setAuth = useAuthStore((s) => s.setAuth);
const navigate = useNavigate();
const queryClient = useQueryClient();
const isDevMode = !import.meta.env.PROD;
const rolesQuery = useQuery({
queryKey: ["dev-roles"],
queryFn: getDevRoles,
staleTime: 60_000,
retry: false,
enabled: isDevMode,
});
const switchMutation = useMutation({
mutationFn: (username: string) => devLogin(username),
onSuccess: (data) => {
queryClient.clear();
setAuth(data.token, data.user);
navigate("/", { replace: true });
},
onError: () => {
queryClient.invalidateQueries({ queryKey: ["dev-roles"] });
},
});
if (!isDevMode || rolesQuery.isError || !rolesQuery.data || rolesQuery.data.length === 0) {
return null;
}
const roles = rolesQuery.data;
return (
);
}
export default function TodayPage() {
const queryClient = useQueryClient();
const user = useAuthStore((s) => s.user);
const [showUpcoming, setShowUpcoming] = useState(false);
const todayQuery = useQuery({ queryKey: ["today"], queryFn: getDashboardToday });
const data = todayQuery.data;
const refresh = () => {
void queryClient.invalidateQueries({ queryKey: ["today"] });
};
const isManager = user?.role === "project_manager";
const role = data?.role ?? user?.role ?? "project_supervisor";
const sections = isManager ? managerSections : supervisorSections;
const projects = data?.projects ?? [];
const totalItems = data
? Object.values(data.sections).reduce((sum, list) => sum + list.length, 0)
: 0;
return (
<>
}
right={
}
/>
{/* هویت نقش و محدوده دسترسی */}
{roleLabels[role]}
{scopeLabel(user)}
{isManager
? "نمای مدیریتی همه پروژهها"
: "نمای عملیاتی پروژههای اختصاصیافته"}
{todayQuery.isPending ? (
) : todayQuery.isError ? (
) : data ? (
<>
{/* نمای مدیریتی پروژهها — فقط مدیر پروژه */}
{isManager && projects.length > 0 && (
وضعیت پروژهها
همه پروژهها
{projects.map((p) => (
))}
)}
{/* ناظر بدون پروژه اختصاصیافته */}
{!isManager && (data?.scope?.project_count ?? user?.scope?.project_count ?? 0) === 0 && totalItems === 0 && (
)}
{totalItems === 0 && (isManager || (data?.scope?.project_count ?? user?.scope?.project_count ?? 0) > 0) ? (
) : (
{sections.map(({ key, title, tone }) => {
const items = data.sections[key];
if (!items || items.length === 0) return null;
const isUpcoming = key === "upcoming";
if (isUpcoming && !showUpcoming) {
return (
);
}
return (
{title}
{items.length} مورد
{isUpcoming && (
)}
{items.map((t) => (
))}
);
})}
)}
>
) : null}
>
);
}