"use client"; /** * Invite redemption screen. * * Lives outside the ``(app)`` group on purpose: the invitee is not a member of * anything yet, so the authenticated shell would bounce them to /login before * they could accept. Here we render a standalone card, and only ask for a * session when there is actually an invite to redeem. */ import Link from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import * as React from "react"; import type { InvitePreview } from "@agy/contracts"; import { Badge, Button, Card, CardHint, CardTitle, ErrorState, LoadingState, } from "@agy/ui"; import { ApiError, api } from "@/lib/api"; import { useSession } from "@/lib/session"; const ROLE_LABELS: Record = { owner: "مالک", admin: "مدیر", manager: "سرپرست", member: "عضو", viewer: "بیننده", }; function Centered({ children }: { children: React.ReactNode }) { return (
{children}
); } function AcceptInviteCard() { const router = useRouter(); const params = useSearchParams(); const token = params.get("token") ?? ""; const { ready, user, refresh, setActiveOrganization } = useSession(); const [preview, setPreview] = React.useState(null); const [error, setError] = React.useState(null); const [loading, setLoading] = React.useState(true); const [accepting, setAccepting] = React.useState(false); // The invitee may not have an account yet; after they sign in or sign up we // send them straight back here so the token is not lost. const nextPath = `/accept-invite?token=${encodeURIComponent(token)}`; React.useEffect(() => { if (!ready) return; if (!token || !user) { setLoading(false); return; } let cancelled = false; setLoading(true); setError(null); api .previewInvite(token) .then((result) => { if (!cancelled) setPreview(result); }) .catch((err) => { if (!cancelled) { setError(err instanceof ApiError ? err.message : "دریافت اطلاعات دعوت‌نامه ناموفق بود."); } }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [ready, token, user]); const onAccept = async () => { setError(null); setAccepting(true); try { const result = await api.acceptInvite(token); // Land the invitee inside the organization they just joined rather than // whichever tenant happened to be active before. setActiveOrganization(result.organization_id); await refresh(); router.replace("/dashboard"); } catch (err) { setError(err instanceof ApiError ? err.message : "پذیرش دعوت‌نامه ناموفق بود."); } finally { setAccepting(false); } }; if (!ready) { return ( ); } if (!token) { return ( لینک دعوت ناقص است توکن دعوت در آدرس وجود ندارد. لینک کامل را از فرستنده دوباره بگیرید.
بازگشت به داشبورد
); } if (!user) { return ( دعوت به یک سازمان برای پذیرش این دعوت باید وارد شوید. اگر هنوز حساب ندارید، با همان ایمیلی که دعوت‌نامه برایش ارسال شده ثبت‌نام کنید.
); } if (loading) { return ( ); } if (error || !preview) { return (
بازگشت به داشبورد
); } return ( دعوت به {preview.organization_name} برای {preview.email} با نقش{" "} {ROLE_LABELS[preview.role] ?? preview.role} دعوت شده‌اید.
{ROLE_LABELS[preview.role] ?? preview.role} {preview.already_member && شما از قبل عضو هستید}

با حساب دیگری وارد شده‌اید؟{" "} تعویض حساب

); } export default function AcceptInvitePage() { // `useSearchParams` forces the route to be client-rendered; Next requires a // Suspense boundary around it so the static shell can still be emitted. return ( } > ); }