@@ -5,8 +5,9 @@ import { useReactToPrint } from "react-to-print";
55import { Card , CardHeader , CardTitle , CardContent } from "@/components/ui/card" ;
66import { Progress } from "@/components/ui/progress" ;
77import { Button } from "@/components/ui/button" ;
8- import { Check , X , Download } from "lucide-react" ;
8+ import { Check , X , Download , Shield , LogIn } from "lucide-react" ;
99import { EXTERNAL } from '@/constant' ;
10+ import { getUserProfile , getLoginUrl , type UserProfile } from '@/lib/utils' ;
1011import LoginMask from './LoginMask' ;
1112import PrintableReport from './PrintableReport' ;
1213
@@ -61,6 +62,8 @@ export default function ReportCard() {
6162 const [ error , setError ] = useState ( "" ) ;
6263 const [ report , setReport ] = useState < Report | null > ( null ) ;
6364 const [ reportId , setReportId ] = useState < string | null > ( null ) ;
65+ const [ currentUser , setCurrentUser ] = useState < UserProfile | null > ( null ) ;
66+ const [ authChecked , setAuthChecked ] = useState ( false ) ;
6467 const printRef = useRef < HTMLDivElement > ( null ) ;
6568
6669 const blacklist = [ "Bounteer Production" , "" ] ;
@@ -111,6 +114,15 @@ export default function ReportCard() {
111114 }
112115 } ;
113116
117+ useEffect ( ( ) => {
118+ async function checkAuth ( ) {
119+ const profile = await getUserProfile ( EXTERNAL . directus_url ) ;
120+ setCurrentUser ( profile ) ;
121+ setAuthChecked ( true ) ;
122+ }
123+ checkAuth ( ) ;
124+ } , [ ] ) ;
125+
114126 useEffect ( ( ) => {
115127 const params = new URLSearchParams ( window . location . search ) ;
116128 const id = params . get ( "id" ) ;
@@ -120,6 +132,10 @@ export default function ReportCard() {
120132 return ;
121133 }
122134
135+ if ( ! authChecked ) {
136+ return ;
137+ }
138+
123139 const fetchReport = async ( ) => {
124140 setLoading ( true ) ;
125141 try {
@@ -144,7 +160,20 @@ export default function ReportCard() {
144160 if ( ! res . ok ) {
145161 throw new Error ( json ?. errors ?. [ 0 ] ?. message || `Fetch failed (${ res . status } )` ) ;
146162 }
147- setReport ( json . data ?? null ) ;
163+
164+ const reportData = json . data ?? null ;
165+
166+ // Check access control: only allow the creator of the submission to view the report
167+ if ( reportData && currentUser ) {
168+ const submissionUserId = reportData . submission ?. user_created ?. id ;
169+ if ( submissionUserId && submissionUserId !== currentUser . id ) {
170+ throw new Error ( "Access denied. You can only view reports for your own submissions." ) ;
171+ }
172+ } else if ( reportData && ! currentUser ) {
173+ throw new Error ( "Please log in to view this report." ) ;
174+ }
175+
176+ setReport ( reportData ) ;
148177 } catch ( e : any ) {
149178 setError ( e . message || "Unexpected error" ) ;
150179 } finally {
@@ -153,7 +182,7 @@ export default function ReportCard() {
153182 } ;
154183
155184 fetchReport ( ) ;
156- } , [ ] ) ;
185+ } , [ authChecked ] ) ;
157186
158187 const prosList =
159188 report ?. pros
@@ -211,6 +240,42 @@ export default function ReportCard() {
211240 }
212241
213242 if ( error ) {
243+ // Check if it's an access denied error
244+ const isAccessDenied = error . includes ( "Access denied" ) || error . includes ( "Please log in" ) ;
245+ const isLoginRequired = error . includes ( "Please log in" ) ;
246+
247+ if ( isAccessDenied ) {
248+ return (
249+ < div className = "flex flex-col items-center justify-center py-12 px-6" >
250+ < div className = "mx-auto w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mb-4" >
251+ { isLoginRequired ? (
252+ < LogIn className = "w-8 h-8 text-red-600" />
253+ ) : (
254+ < Shield className = "w-8 h-8 text-red-600" />
255+ ) }
256+ </ div >
257+ < h2 className = "text-xl font-semibold text-gray-900 mb-2" >
258+ { isLoginRequired ? "Authentication Required" : "Access Denied" }
259+ </ h2 >
260+ < p className = "text-gray-600 text-center mb-4 max-w-md" >
261+ { error }
262+ </ p >
263+ { isLoginRequired && (
264+ < Button
265+ onClick = { ( ) => {
266+ const nextPath = window . location . pathname + window . location . search ;
267+ window . location . href = getLoginUrl ( EXTERNAL . directus_url , EXTERNAL . auth_idp_key , nextPath ) ;
268+ } }
269+ className = "flex items-center gap-2"
270+ >
271+ < LogIn className = "h-4 w-4" />
272+ Login to View Report
273+ </ Button >
274+ ) }
275+ </ div >
276+ ) ;
277+ }
278+
214279 return (
215280 < div className = "mb-6 p-3 rounded-lg bg-red-50 text-red-700 text-sm" >
216281 { error }
0 commit comments