Skip to content

Commit 356631d

Browse files
committed
0.1.4 report user shield
1 parent bea82bc commit 356631d

File tree

4 files changed

+71
-5
lines changed

4 files changed

+71
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [0.1.4] - 2025-09-18
44
### Added
55
- printable report
6+
- shield to prevent others looking at your report
67

78
## [0.1.3] - 2025-09-18
89
### Modified

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@bounteer/page",
33
"type": "module",
4-
"version": "0.1.3",
4+
"version": "0.1.4",
55
"private": true,
66
"scripts": {
77
"dev": "astro dev",

src/components/interactive/ReportCard.tsx

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { useReactToPrint } from "react-to-print";
55
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
66
import { Progress } from "@/components/ui/progress";
77
import { Button } from "@/components/ui/button";
8-
import { Check, X, Download } from "lucide-react";
8+
import { Check, X, Download, Shield, LogIn } from "lucide-react";
99
import { EXTERNAL } from '@/constant';
10+
import { getUserProfile, getLoginUrl, type UserProfile } from '@/lib/utils';
1011
import LoginMask from './LoginMask';
1112
import 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}

src/pages/role-fit-index/report.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import ReportCard from "@/components/interactive/ReportCard";
88
<Layout title="Role Fit Index Report - Bounteer">
99
<Header />
1010
<main class="py-16 bg-gray-50">
11-
<div class="container-custom max-w-8xl mx-auto px-4 pb-8">
11+
<div class="container-custom max-w-5xl mx-auto px-4 pb-8">
1212
<ReportCard client:only="react" />
1313
<div class="text-center mt-8 space-x-6">
1414
<a

0 commit comments

Comments
 (0)