"use client"; import * as React from "react"; import { useQuery } from "@tanstack/react-query"; import { Mail, Phone, UserRound } from "lucide-react"; import AsyncSearchableCombobox from "@/components/AsyncSearchableCombobox"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import type { UserListSchema, UserProfileSchema } from "@/lib/types"; import { api } from "@/lib/api"; import { formatJalali, formatNumberPersian, resolveErrorMessage } from "@/lib/utils"; import { useToast } from "@/hooks/use-toast"; const USERS_PAGE_SIZE = 25; function fullName(user: Pick) { return [user.first_name, user.last_name].filter(Boolean).join(" ") || user.username; } function initials(user: Pick) { const base = [user.first_name, user.last_name].filter(Boolean).join(" ") || user.username; return base.slice(0, 2).toUpperCase(); } function InfoRow({ label, value }: { label: string; value?: React.ReactNode }) { return (
{label} {value || "—"}
); } function UserDetailDialog({ user, open, onOpenChange, }: { user: UserProfileSchema | null; open: boolean; onOpenChange: (open: boolean) => void; }) { if (!user) return null; return ( جزئیات کاربر
{initials(user)}

{fullName(user)}

{user.username}

{user.is_active ? "فعال" : "غیرفعال"} {user.is_staff ? Staff : null} {user.is_superuser ? Superuser : null}
{user.bio ? (
بیوگرافی
{user.bio}
) : null}
); } export default function AdminUsersPage() { const { toast } = useToast(); const [filters, setFilters] = React.useState({ search: "", studentId: "", university: null as string | null, major: null as string | null, isActive: "all", }); const [debouncedSearch, setDebouncedSearch] = React.useState(""); const [page, setPage] = React.useState(1); const [selectedUserId, setSelectedUserId] = React.useState(null); React.useEffect(() => { const timer = window.setTimeout(() => setDebouncedSearch(filters.search.trim()), 300); return () => window.clearTimeout(timer); }, [filters.search]); const usersQuery = useQuery({ queryKey: ["admin", "users", filters, debouncedSearch, page], queryFn: () => api.listUsers({ search: debouncedSearch || undefined, student_id: filters.studentId || undefined, university: filters.university || undefined, major: filters.major || undefined, is_active: filters.isActive === "all" ? undefined : filters.isActive === "active" ? "true" : "false", limit: USERS_PAGE_SIZE, offset: (page - 1) * USERS_PAGE_SIZE, }), }); const selectedUserQuery = useQuery({ queryKey: ["admin", "users", selectedUserId, "detail"], queryFn: () => api.getUserDetail(selectedUserId as number), enabled: selectedUserId != null, }); React.useEffect(() => { if (usersQuery.error) { toast({ title: "خطا در بارگذاری کاربران", description: resolveErrorMessage(usersQuery.error), variant: "destructive", }); } }, [usersQuery.error, toast]); const users = usersQuery.data ?? []; const hasMore = users.length === USERS_PAGE_SIZE; const handleFilterChange = (field: keyof typeof filters, value: string | null) => { setFilters((prev) => ({ ...prev, [field]: value ?? "" })); setPage(1); }; const loadMajors = React.useCallback(async (params: { search: string; limit: number; offset: number }) => { const data = await api.getMajorsPaged(params); return { count: data.count, results: data.results.map((item) => ({ value: item.code, label: item.label })), }; }, []); const loadUniversities = React.useCallback(async (params: { search: string; limit: number; offset: number }) => { const data = await api.getUniversitiesPaged(params); return { count: data.count, results: data.results.map((item) => ({ value: item.code, label: item.label })), }; }, []); return (

کاربران

مدیریت و جستجوی کاربران سامانه

فیلترها جستجو با نام، ایمیل، موبایل، دانشگاه یا رشته
handleFilterChange("search", event.target.value)} /> handleFilterChange("studentId", event.target.value)} /> handleFilterChange("university", value)} loadOptions={loadUniversities} placeholder="دانشگاه" />
handleFilterChange("major", value)} loadOptions={loadMajors} placeholder="رشته" />
لیست کاربران برای مشاهده جزئیات، روی هر ردیف کلیک کنید. {usersQuery.isLoading ? (

در حال بارگذاری...

) : users.length === 0 ? (

کاربری یافت نشد.

) : (
{users.map((user) => ( setSelectedUserId(user.id)} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") setSelectedUserId(user.id); }} > ))}
کاربر موبایل ایمیل
{initials(user)}
{fullName(user)}
{user.username}
{user.mobile || "—"} {user.email || "—"}
)}
صفحه {formatNumberPersian(page)}
{ if (!open) setSelectedUserId(null); }} /> {selectedUserQuery.isFetching && selectedUserId ? (
در حال بارگذاری جزئیات...
) : null}
); }