Skip to content

Commit b606136

Browse files
author
Peter Nguyen
committed
fix from passing props to using react-query
1 parent b46d01e commit b606136

3 files changed

Lines changed: 64 additions & 32 deletions

File tree

frontend-nextjs/src/app/[lang]/dashboard/invite/[inviteId]/invite-client.tsx

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@
22

33
import { Button } from "@/components/ui/button";
44
import { ApiError } from "@/lib/api";
5-
import { acceptInvite, InviteDetails } from "@/models/invite";
5+
import { acceptInvite, inviteQueryOptions } from "@/models/invite";
66
import Link from "next/link";
77
import { useEffect, useState } from "react";
88
import { useRouter } from "next/navigation";
9+
import { useQuery } from "@tanstack/react-query";
910

1011
type Props = {
1112
code: string;
12-
invite: InviteDetails;
1313
dict: any;
14-
mockMode?: boolean;
1514
};
1615

17-
export default function InviteClient({ code, invite, dict, mockMode = false }: Props) {
16+
export default function InviteClient({ code, dict }: Props) {
1817
const [status, setStatus] = useState<"idle" | "loading" | "success" | "error">("idle");
1918
const [message, setMessage] = useState<string | null>(null);
2019
const router = useRouter();
20+
const { data: invite } = useQuery(inviteQueryOptions(code));
2121

2222
// After successful acceptance, redirect after a short delay.
2323
useEffect(() => {
@@ -32,43 +32,51 @@ export default function InviteClient({ code, invite, dict, mockMode = false }: P
3232
const handleAccept = async () => {
3333
setStatus("loading");
3434
setMessage(null);
35-
try {
36-
if (!mockMode) {
37-
await acceptInvite(code);
38-
}
39-
setStatus("success");
40-
setMessage(dict.dashboard.invite.accepted);
41-
} catch (err) {
42-
setStatus("error");
43-
if (err instanceof ApiError) {
44-
if (err.status === 401) {
45-
setMessage(dict.dashboard.invite.login_required ?? "Please log in to accept this invite.");
35+
36+
acceptInvite(code)
37+
.then(() => {
38+
setStatus("success");
39+
setMessage(dict.dashboard.invite.accepted);
40+
})
41+
.catch((err) => {
42+
setStatus("error");
43+
if (err instanceof ApiError) {
44+
if (err.status === 401) {
45+
setMessage(dict.dashboard.invite.login_required ?? "Please log in to accept this invite.");
46+
} else {
47+
setMessage(err.message);
48+
}
4649
} else {
47-
setMessage(err.message);
50+
setMessage("Something went wrong. Please try again.");
4851
}
49-
} else {
50-
setMessage("Something went wrong. Please try again.");
51-
}
52-
}
52+
});
5353
};
5454

55-
const inviteInvalid = invite.expired || invite.used || status === "success";
55+
const inviteInvalid = !invite || invite.expired || invite.used || status === "success";
5656

5757
return (
5858
<div className="max-w-xl mx-auto p-6 flex flex-col gap-4">
5959
<h1 className="text-2xl font-bold">{dict.dashboard.invite.title}</h1>
6060
<p className="text-sm text-muted-foreground">
61-
{dict.dashboard.invite.invited_by.replace("{org}", invite.organisation_name)}
62-
</p>
63-
<p className="text-sm text-muted-foreground">
64-
{dict.dashboard.invite.sent_to}: <span className="font-medium">{invite.email}</span>
61+
{invite
62+
? dict.dashboard.invite.invited_by.replace("{org}", invite.organisation_name)
63+
: dict?.common?.loading ?? "Loading..."}
6564
</p>
66-
{invite.expired && (
65+
{/* Show the email the invite was sent to */}
66+
{invite && (
67+
<p className="text-sm text-muted-foreground">
68+
{dict.dashboard.invite.sent_to}: <span className="font-medium">{invite.email}</span>
69+
</p>
70+
)}
71+
{/* Show the expired message if the invite has expired */}
72+
{invite?.expired && (
6773
<p className="text-sm text-red-600">{dict.dashboard.invite.expired}</p>
6874
)}
69-
{invite.used && (
75+
{/* Show the used message if the invite has been used */}
76+
{invite?.used && (
7077
<p className="text-sm text-red-600">{dict.dashboard.invite.used}</p>
7178
)}
79+
{/* Show the message if there is an error */}
7280
{message && (
7381
<p className={`text-sm ${status === "success" ? "text-green-600" : "text-red-600"}`}>
7482
{message}
@@ -87,6 +95,7 @@ export default function InviteClient({ code, invite, dict, mockMode = false }: P
8795
>
8896
{status === "loading" ? "Loading..." : dict.dashboard.invite.accept_cta}
8997
</Button>
98+
{/* Show the wrong account message if the account is not invited */}
9099
<p className="text-xs text-muted-foreground">
91100
{dict.dashboard.invite.wrong_account}
92101
</p>
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getDictionary } from "@/app/[lang]/dictionaries";
2-
import { getInvite } from "@/models/invite";
2+
import { inviteQueryOptions } from "@/models/invite";
3+
import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query";
34
import { notFound } from "next/navigation";
45
import InviteClient from "./invite-client";
56

@@ -10,14 +11,18 @@ export default async function Page({ params }: { params: Params }) {
1011

1112
const dict = await getDictionary(lang);
1213

13-
let invite;
1414
try {
15-
invite = await getInvite(inviteId);
15+
const queryClient = new QueryClient();
16+
await queryClient.prefetchQuery(inviteQueryOptions(inviteId));
17+
18+
return (
19+
<HydrationBoundary state={dehydrate(queryClient)}>
20+
<InviteClient code={inviteId} dict={dict} />
21+
</HydrationBoundary>
22+
);
1623
} catch {
1724
return notFound();
1825
}
19-
20-
return <InviteClient code={inviteId} invite={invite} dict={dict} />;
2126
}
2227

2328

frontend-nextjs/src/models/invite.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { apiRequest } from "@/lib/api";
2+
import type { QueryKey } from "@tanstack/react-query";
23
import { AppMessage } from "./app";
34

45
export type InviteDetails = {
@@ -33,4 +34,21 @@ export async function acceptInvite(code: string): Promise<AppMessage> {
3334
});
3435
}
3536

37+
/**
38+
* Gets the query key for the invite.
39+
* @param code - The invite code
40+
* @returns The query key
41+
*/
42+
export const inviteQueryKey = (code: string): QueryKey => ["invite", code];
43+
44+
/**
45+
* Gets the query options for the invite.
46+
* @param code - The invite code
47+
* @returns The query options
48+
*/
49+
export const inviteQueryOptions = (code: string) => ({
50+
queryKey: inviteQueryKey(code),
51+
queryFn: () => getInvite(code),
52+
});
53+
3654

0 commit comments

Comments
 (0)