diff --git a/surfsense_web/app/invite/[invite_code]/page.tsx b/surfsense_web/app/invite/[invite_code]/page.tsx index 4ff78ac91..293015744 100644 --- a/surfsense_web/app/invite/[invite_code]/page.tsx +++ b/surfsense_web/app/invite/[invite_code]/page.tsx @@ -1,5 +1,7 @@ "use client"; +import { useAtomValue } from "jotai"; +import { useQuery } from "@tanstack/react-query"; import { AlertCircle, ArrowRight, @@ -16,7 +18,11 @@ import { motion } from "motion/react"; import Image from "next/image"; import Link from "next/link"; import { useParams, useRouter } from "next/navigation"; -import { use, useEffect, useState } from "react"; +import { use, useCallback, useEffect, useState } from "react"; +import { toast } from "sonner"; +import { acceptInviteMutationAtom } from "@/atoms/invites/invites-mutation.atoms"; +import { invitesApiService } from "@/lib/apis/invites-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; import { Button } from "@/components/ui/button"; import { Card, @@ -26,22 +32,46 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { useInviteInfo } from "@/hooks/use-rbac"; import { getBearerToken } from "@/lib/auth-utils"; +import { AcceptInviteResponse } from "@/contracts/types/invites.types"; export default function InviteAcceptPage() { const params = useParams(); const router = useRouter(); const inviteCode = params.invite_code as string; - const { inviteInfo, loading, acceptInvite } = useInviteInfo(inviteCode); + const { data: inviteInfo = null, isLoading: loading } = useQuery({ + queryKey: cacheKeys.invites.info(inviteCode), + enabled: !!inviteCode, + staleTime: 5 * 60 * 1000, + queryFn: async () => { + if (!inviteCode) return null; + return invitesApiService.getInviteInfo({ + invite_code: inviteCode, + }); + }, + }); + + const { mutateAsync: acceptInviteMutation } = useAtomValue(acceptInviteMutationAtom); + + const acceptInvite = useCallback(async () => { + if (!inviteCode) { + toast.error("No invite code provided"); + return null; + } + + try { + const result = await acceptInviteMutation({ invite_code: inviteCode }); + return result; + } catch (err: any) { + toast.error(err.message || "Failed to accept invite"); + throw err; + } + }, [inviteCode, acceptInviteMutation]); + const [accepting, setAccepting] = useState(false); const [accepted, setAccepted] = useState(false); - const [acceptedData, setAcceptedData] = useState<{ - search_space_id: number; - search_space_name: string; - role_name: string; - } | null>(null); + const [acceptedData, setAcceptedData] = useState(null); const [error, setError] = useState(null); const [isLoggedIn, setIsLoggedIn] = useState(null); diff --git a/surfsense_web/contracts/types/invites.types.ts b/surfsense_web/contracts/types/invites.types.ts index 2a9460e53..0359d84d5 100644 --- a/surfsense_web/contracts/types/invites.types.ts +++ b/surfsense_web/contracts/types/invites.types.ts @@ -77,11 +77,10 @@ export const getInviteInfoRequest = z.object({ }); export const getInviteInfoResponse = z.object({ - invite_code: z.string(), search_space_name: z.string(), role_name: z.string().nullable(), - expires_at: z.string().nullable(), is_valid: z.boolean(), + message: z.string().nullable(), }); /** @@ -94,6 +93,8 @@ export const acceptInviteRequest = z.object({ export const acceptInviteResponse = z.object({ message: z.string(), search_space_id: z.number(), + search_space_name: z.string(), + role_name: z.string().nullable(), }); export type Invite = z.infer;