22
33import { useSubscription } from "@/hooks/useSubscription" ;
44import { useRouter } from "next/navigation" ;
5- import { useEffect } from "react" ;
5+ import { useEffect , useState } from "react" ;
6+ import { useSession } from "next-auth/react" ;
67
78export default function ProDashboardPage ( ) {
89 const { isPaidUser, isLoading } = useSubscription ( ) ;
910 const router = useRouter ( ) ;
11+ const { data : session } = useSession ( ) ;
12+ const [ error , setError ] = useState < string | null > ( null ) ;
13+ const [ isJoining , setIsJoining ] = useState ( false ) ;
1014
1115 useEffect ( ( ) => {
1216 if ( ! isLoading && ! isPaidUser ) {
1317 router . push ( "/pricing" ) ;
1418 }
1519 } , [ isPaidUser , isLoading , router ] ) ;
1620
21+ const handleJoinSlack : ( ) => Promise < void > = async ( ) => {
22+ if ( isJoining ) return ;
23+
24+ setIsJoining ( true ) ;
25+ setError ( null ) ;
26+
27+ try {
28+ if ( ! session ?. user ) {
29+ setError ( "Please sign in to join the community" ) ;
30+ return ;
31+ }
32+
33+ const accessToken = session ?. accessToken ;
34+
35+ if ( ! accessToken || typeof accessToken !== "string" ) {
36+ setError ( "Authentication token not found" ) ;
37+ return ;
38+ }
39+
40+ const apiUrl = process . env . NEXT_PUBLIC_API_URL || "http://localhost:4000" ;
41+ const response = await fetch ( `${ apiUrl } /join-community` , {
42+ method : "GET" ,
43+ headers : {
44+ Authorization : `Bearer ${ accessToken } ` ,
45+ } ,
46+ } ) ;
47+
48+ if ( ! response . ok ) {
49+ let errorMessage = "Failed to join community" ;
50+ try {
51+ const errorData = await response . json ( ) ;
52+ errorMessage = errorData . error || errorMessage ;
53+ } catch {
54+ // if json parsing fails, use default message
55+ }
56+ setError ( errorMessage ) ;
57+ return ;
58+ }
59+
60+ let responseData : { slackInviteUrl ?: string } ;
61+ try {
62+ responseData = await response . json ( ) ;
63+ } catch {
64+ setError ( "Invalid response from server" ) ;
65+ return ;
66+ }
67+
68+ const { slackInviteUrl } = responseData ;
69+
70+ if ( ! slackInviteUrl || typeof slackInviteUrl !== "string" ) {
71+ setError ( "Invalid Slack invite URL received" ) ;
72+ return ;
73+ }
74+
75+ // validate url format
76+ try {
77+ new URL ( slackInviteUrl ) ;
78+ } catch {
79+ setError ( "Invalid Slack invite URL format" ) ;
80+ return ;
81+ }
82+
83+ window . location . href = slackInviteUrl ;
84+ } catch ( err ) {
85+ console . error ( "Failed to join community:" , err ) ;
86+ setError ( "Failed to connect to server" ) ;
87+ } finally {
88+ setIsJoining ( false ) ;
89+ }
90+ } ;
91+
1792 if ( isLoading ) {
1893 return (
1994 < div className = "w-full h-full flex items-center justify-center bg-ox-content" >
@@ -33,6 +108,18 @@ export default function ProDashboardPage() {
33108 hi investors, ajeetunc is on the way to deliver the shareholder value.
34109 soon you'll see all the pro perks here. thanks for investing
35110 </ h1 >
111+ { isPaidUser && (
112+ < div className = "mt-6" >
113+ < button
114+ onClick = { handleJoinSlack }
115+ disabled = { isJoining }
116+ className = "px-4 py-2 bg-brand-purple hover:bg-brand-purple-light text-text-primary font-medium rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
117+ >
118+ { isJoining ? "Joining..." : "Join Slack" }
119+ </ button >
120+ { error && < p className = "text-error-text text-sm mt-2" > { error } </ p > }
121+ </ div >
122+ ) }
36123 </ div >
37124 </ div >
38125 ) ;
0 commit comments