22
33import { useEffect , useState } from "react"
44import { useParams , useRouter } from "next/navigation"
5- import { ArrowLeft , ShieldCheck } from "lucide-react"
5+ import { ArrowLeft , ExternalLink , ShieldCheck } from "lucide-react"
66import { Button } from "@/components/ui/button"
77import { Card , CardContent , CardHeader , CardTitle } from "@/components/ui/card"
88
@@ -88,6 +88,8 @@ export default function StudentDetailPage() {
8888 const [ student , setStudent ] = useState < StudentDetail | null > ( null )
8989 const [ loading , setLoading ] = useState ( true )
9090 const [ error , setError ] = useState < string | null > ( null )
91+ const [ sisLink , setSisLink ] = useState < string | null > ( null )
92+ const [ sisStatus , setSisStatus ] = useState < "loading" | "available" | "unavailable" | "hidden" > ( "loading" )
9193
9294 useEffect ( ( ) => {
9395 if ( ! guid ) return
@@ -102,6 +104,33 @@ export default function StudentDetailPage() {
102104 . catch ( e => { setError ( e . message ) ; setLoading ( false ) } )
103105 } , [ guid ] )
104106
107+ useEffect ( ( ) => {
108+ if ( ! guid ) return
109+ fetch ( `/api/students/${ encodeURIComponent ( guid ) } /sis-link` )
110+ . then ( r => {
111+ if ( r . status === 403 ) {
112+ setSisStatus ( "hidden" )
113+ return null
114+ }
115+ if ( r . status === 404 ) {
116+ setSisStatus ( "unavailable" )
117+ return null
118+ }
119+ if ( ! r . ok ) {
120+ setSisStatus ( "hidden" )
121+ return null
122+ }
123+ return r . json ( )
124+ } )
125+ . then ( data => {
126+ if ( data ?. url ) {
127+ setSisLink ( data . url )
128+ setSisStatus ( "available" )
129+ }
130+ } )
131+ . catch ( ( ) => setSisStatus ( "hidden" ) )
132+ } , [ guid ] )
133+
105134 // ─── Loading skeleton ────────────────────────────────────────────────────
106135
107136 if ( loading ) {
@@ -179,6 +208,29 @@ export default function StudentDetailPage() {
179208 </ div >
180209 </ div >
181210 < div className = "flex items-center gap-2" >
211+ { sisStatus === "available" && sisLink && (
212+ < Button
213+ variant = "outline"
214+ size = "sm"
215+ className = "gap-1.5"
216+ onClick = { ( ) => window . open ( sisLink , "_blank" , "noopener,noreferrer" ) }
217+ >
218+ < ExternalLink className = "h-3.5 w-3.5" />
219+ Open in SIS
220+ </ Button >
221+ ) }
222+ { sisStatus === "unavailable" && (
223+ < Button
224+ variant = "outline"
225+ size = "sm"
226+ className = "gap-1.5 opacity-50 cursor-not-allowed"
227+ disabled
228+ title = "No SIS record linked for this student"
229+ >
230+ < ExternalLink className = "h-3.5 w-3.5" />
231+ Open in SIS
232+ </ Button >
233+ ) }
182234 { student . at_risk_alert && (
183235 < Badge
184236 label = { student . at_risk_alert }
0 commit comments