"use client"; import { FormEvent, useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; import { api, ApiError, type Tenant, type TenantDomain, type Subscription, type Plan, } from "@/lib/api"; import { getStoredToken } from "@/lib/auth"; import { identityApi, type AdminUser, type TenantMember } from "@/lib/identity"; import { AdminPageHeader, Banner, Button, Field, SelectField, StatusBadge, TextField, cardClass, inputClass, } from "@/components/admin/controls"; const TENANT_STATUSES = [ "draft", "pending_activation", "active", "suspended", "archived", "inactive", ].map((s) => ({ value: s, label: s })); const SUBSCRIPTION_STATUSES = [ "trialing", "active", "past_due", "canceled", "expired", ].map((s) => ({ value: s, label: s })); export default function TenantDetailPage() { const params = useParams<{ id: string }>(); const tenantId = params.id; const [tenant, setTenant] = useState(null); const [domains, setDomains] = useState([]); const [subscription, setSubscription] = useState(null); const [members, setMembers] = useState([]); const [plans, setPlans] = useState([]); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const load = useCallback(async () => { setLoading(true); setError(""); const token = getStoredToken(); try { const t = await api.platformTenants.get(tenantId); setTenant(t); } catch (e) { setError(e instanceof ApiError ? e.message : "دریافت Tenant ناموفق بود"); setLoading(false); return; } const [d, sub, plansPage, mem, usersRes] = await Promise.allSettled([ api.domains.listForTenant(tenantId), api.subscriptions.get(tenantId), api.plans.list(1, 100), token ? identityApi.listMembers(token, tenantId) : Promise.resolve([]), token ? identityApi.listUsers(token, { limit: 100 }) : Promise.resolve([]), ]); if (d.status === "fulfilled") setDomains(d.value); setSubscription(sub.status === "fulfilled" ? sub.value : null); if (plansPage.status === "fulfilled") setPlans(plansPage.value.items); if (mem.status === "fulfilled") setMembers(mem.value); if (usersRes.status === "fulfilled") setUsers(usersRes.value); setLoading(false); }, [tenantId]); useEffect(() => { void load(); }, [load]); if (loading) { return

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

; } if (!tenant) { return (
{error || "Tenant یافت نشد"} ← بازگشت به لیست
); } return (
← بازگشت به لیست
} /> {error && {error}}
); } function TenantEditCard({ tenant, onUpdated, onError, }: { tenant: Tenant; onUpdated: (t: Tenant) => void; onError: (m: string) => void; }) { const [name, setName] = useState(tenant.name); const [status, setStatus] = useState(tenant.status); const [ownerUserId, setOwnerUserId] = useState(tenant.owner_user_id || ""); const [saving, setSaving] = useState(false); const [success, setSuccess] = useState(""); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setSuccess(""); onError(""); setSaving(true); try { const updated = await api.platformTenants.update(tenant.id, { name, status, owner_user_id: ownerUserId.trim() || null, }); onUpdated(updated); setSuccess("تغییرات ذخیره شد."); } catch (err) { onError(err instanceof ApiError ? err.message : "ذخیره ناموفق بود"); } finally { setSaving(false); } }; return (

اطلاعات پایه

{success && {success}} ); } function DomainsCard({ tenantId, domains, onChanged, }: { tenantId: string; domains: TenantDomain[]; onChanged: () => void; }) { const [domain, setDomain] = useState(""); const [type, setType] = useState<"subdomain" | "custom_domain">("custom_domain"); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const handleAdd = async (e: FormEvent) => { e.preventDefault(); setError(""); if (!domain.trim()) return; setSaving(true); try { await api.domains.create(tenantId, { domain: domain.trim(), domain_type: type }); setDomain(""); onChanged(); } catch (err) { setError(err instanceof ApiError ? err.message : "افزودن دامنه ناموفق بود"); } finally { setSaving(false); } }; return (

دامنه‌ها

{domains.length === 0 ? (

دامنه‌ای ثبت نشده است.

) : (
    {domains.map((d) => (
  • {d.domain} {d.domain_type}
  • ))}
)} {error && {error}}
setType(v as "subdomain" | "custom_domain")} options={[ { value: "custom_domain", label: "دامنه اختصاصی" }, { value: "subdomain", label: "زیردامنه" }, ]} disabled={saving} />
); } function SubscriptionCard({ tenantId, subscription, plans, onChanged, }: { tenantId: string; subscription: Subscription | null; plans: Plan[]; onChanged: () => void; }) { const [planId, setPlanId] = useState(plans[0]?.id || ""); const [status, setStatus] = useState("active"); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); useEffect(() => { if (!planId && plans[0]) setPlanId(plans[0].id); }, [plans, planId]); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setError(""); if (!planId) { setError("ابتدا یک پلن انتخاب کنید."); return; } setSaving(true); try { await api.subscriptions.create(tenantId, { plan_id: planId, status: status as Subscription["status"], }); onChanged(); } catch (err) { setError(err instanceof ApiError ? err.message : "ثبت اشتراک ناموفق بود"); } finally { setSaving(false); } }; const planName = subscription ? plans.find((p) => p.id === subscription.plan_id)?.name || subscription.plan_id : null; return (

اشتراک

{subscription ? (
پلن {planName}
وضعیت
) : (

اشتراک فعالی ثبت نشده است.

)} {error && {error}}

{subscription ? "تغییر/ثبت اشتراک جدید" : "ثبت اشتراک"}

{plans.length === 0 ? (

ابتدا از بخش «پلن‌ها» یک پلن بسازید.

) : ( <> ({ value: p.id, label: `${p.name} (${p.code})` }))} disabled={saving} /> )}
); } function MembersCard({ tenantId, members, users, onChanged, }: { tenantId: string; members: TenantMember[]; users: AdminUser[]; onChanged: () => void; }) { const [userId, setUserId] = useState(""); const [role, setRole] = useState<"tenant_admin" | "tenant_member">("tenant_member"); const [saving, setSaving] = useState(false); const [error, setError] = useState(""); const userLabel = (id: string) => { const u = users.find((x) => x.id === id); return u ? u.display_name || u.username || u.mobile || u.email : id; }; const handleAdd = async (e: FormEvent) => { e.preventDefault(); setError(""); const token = getStoredToken(); if (!token) { setError("توکن یافت نشد."); return; } if (!userId) { setError("یک کاربر انتخاب کنید."); return; } setSaving(true); try { await identityApi.addMember(token, tenantId, { user_id: userId, role }); setUserId(""); onChanged(); } catch (err) { setError(err instanceof Error ? err.message : "افزودن عضو ناموفق بود"); } finally { setSaving(false); } }; return (

اعضا

{members.length === 0 ? (

عضوی ثبت نشده است.

) : (
    {members.map((m) => (
  • {userLabel(m.user_id)} {m.role === "tenant_admin" ? "مدیر" : "عضو"}
  • ))}
)} {error && {error}}

افزودن عضو

{users.length === 0 ? (

کاربری برای افزودن یافت نشد. ابتدا از بخش «کاربران» کاربر بسازید.

) : ( <> setRole(v as "tenant_admin" | "tenant_member")} options={[ { value: "tenant_member", label: "عضو" }, { value: "tenant_admin", label: "مدیر" }, ]} disabled={saving} /> )}
); }