gh_UserManager/apps/web/app/admin/organizations/page.tsx
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

143 lines
5.5 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 { useEffect, useState } from "react";
import { Search, Building2, RefreshCw, Users } from "lucide-react";
import { AdminLayout } from "@/components/admin/admin-layout";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { useLanguage } from "@/components/language-provider";
interface Org {
id: string;
name: string;
slug: string;
description: string;
is_active: boolean;
created_at: string;
member_count: number;
user_role: string;
}
export default function AdminOrganizationsPage() {
const { lang, dir } = useLanguage();
const [orgs, setOrgs] = useState<Org[]>([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
useEffect(() => {
const token = localStorage.getItem("access_token");
if (!token) {
window.location.href = "/login?redirect=/admin/organizations";
return;
}
const fetchData = async () => {
const baseURL = process.env.NEXT_PUBLIC_API_URL;
const qs = new URLSearchParams();
if (search) qs.set("search", search);
try {
const res = await fetch(`${baseURL}/organizations/?${qs.toString()}`, {
headers: { Authorization: `Bearer ${token}` },
});
const data = await res.json();
setOrgs(data.results || []);
} catch (e) {
console.error(e);
} finally {
setLoading(false);
}
};
const timer = setTimeout(fetchData, search ? 500 : 0);
return () => clearTimeout(timer);
}, [search]);
const t = (fa: string, en: string) => (lang === "fa" ? fa : en);
if (loading) {
return (
<AdminLayout>
<div className="flex items-center justify-center h-64">
<RefreshCw className="h-8 w-8 animate-spin text-brand-400" />
</div>
</AdminLayout>
);
}
return (
<AdminLayout>
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-xl font-bold text-white">{t("سازمان\u200cها", "Organizations")}</h2>
<Button size="sm">
{t("ایجاد سازمان", "Create Organization")}
</Button>
</div>
<div className="relative flex-1 max-w-md">
<Search className={`absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-slate-500 ${dir === "rtl" ? "left-3 right-auto" : ""}`} />
<input
type="text"
placeholder={t("جستجو در سازمان‌ها...", "Search organizations...")}
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-12 pr-4 py-2.5 rounded-lg border border-white/10 bg-white/5 text-white focus:outline-none focus:ring-2 focus:ring-brand-500/50"
dir={dir}
/>
</div>
<div className="overflow-x-auto rounded-xl border border-white/10">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-white/10 bg-white/[0.02]">
<th className="px-4 py-3 text-start text-xs font-medium text-slate-400">{t("سازمان", "Organization")}</th>
<th className="px-4 py-3 text-start text-xs font-medium text-slate-400">{t("وضعیت", "Status")}</th>
<th className="px-4 py-3 text-start text-xs font-medium text-slate-400">{t("اعضا", "Members")}</th>
<th className="px-4 py-3 text-start text-xs font-medium text-slate-400">{t("نقش شما", "Your Role")}</th>
<th className="px-4 py-3 text-start text-xs font-medium text-slate-400">{t("ساخته شده", "Created")}</th>
</tr>
</thead>
<tbody>
{orgs.map((org) => (
<tr key={org.id} className="border-b border-white/5">
<td className="px-4 py-3">
<div className="flex items-center gap-3">
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-brand-gradient/10 text-sm font-bold text-brand-200">
<Building2 className="h-5 w-5" />
</span>
<div>
<div className="font-medium text-white">{org.name}</div>
<div className="text-xs text-slate-500">{org.slug}</div>
</div>
</div>
</td>
<td className="px-4 py-3">
<Badge variant={org.is_active ? "default" : "secondary"} className="text-xs">
{org.is_active ? t("فعال", "Active") : t("غیرفعال", "Inactive")}
</Badge>
</td>
<td className="px-4 py-3 text-slate-300">{org.member_count}</td>
<td className="px-4 py-3">
<Badge variant="secondary" className="text-xs capitalize">
{t(org.user_role, org.user_role)}
</Badge>
</td>
<td className="px-4 py-3 text-slate-400">
{new Date(org.created_at).toLocaleDateString(lang === "fa" ? "fa-IR" : "en-US")}
</td>
</tr>
))}
</tbody>
</table>
{orgs.length === 0 && !loading && (
<div className="py-12 text-center text-slate-500">
{t("سازمانی یافت نشد", "No organizations found")}
</div>
)}
</div>
</div>
</AdminLayout>
);
}