From 690adf5a8629ceff824a45e1fb93568e4b3f216b Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 19:05:06 -0700 Subject: [PATCH 01/15] ai lol --- .../[submissionId]/_reviewButtons.tsx | 89 ++++++++++++++++--- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 822327c..703dab4 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -45,7 +45,6 @@ function ReviewButton(props: ReviewButton) { } export default function ReviewButtons(props: ReviewId) { - const submissionId = props.submissionId // When is each button visible? // Multiple buttons can be visible at once. // Action | Role | When Current Status is One of: @@ -59,16 +58,78 @@ export default function ReviewButtons(props: ReviewId) { // RequestChanges | Reviewer | Validated, Accepted, Submitted // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading - return ( - <section className="review-set"> - <ReviewButton color="info" name="Submit" action="submit" submissionId={submissionId}/> - <ReviewButton color="info" name="Revoke" action="revoke" submissionId={submissionId}/> - <ReviewButton color="info" name="Accept" action="trigger-validate" submissionId={submissionId}/> - <ReviewButton color="info" name="Validate" action="retry-validate" submissionId={submissionId}/> - <ReviewButton color="error" name="Reject" action="reject" submissionId={submissionId}/> - <ReviewButton color="info" name="Upload" action="trigger-upload" submissionId={submissionId}/> - <ReviewButton color="error" name="Reset Uploading (fix softlocked status)" action="reset-uploading" submissionId={submissionId}/> - <ReviewButton color="error" name="Reset Validating (fix softlocked status)" action="reset-validating" submissionId={submissionId}/> - </section> - ) -} + const { submissionId } = props; + const [roles, setRoles] = useState<string[]>([]); + const [status, setStatus] = useState<string | null>(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchData() { + try { + // Fetch user roles + const rolesResponse = await fetch("/api/session/roles"); + const rolesData = await rolesResponse.json(); + + // Fetch submission status + const statusResponse = await fetch( + `/api/submissions/${submissionId}/status` + ); + const statusData = await statusResponse.json(); + + setRoles(rolesData); + setStatus(statusData.status); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + } + + fetchData(); + }, [submissionId]); + + if (loading) return <p>Loading...</p>; + + const visibleButtons: ReviewButtonProps[] = []; + + if (roles.includes("Submitter")) { + if (["UnderConstruction", "ChangesRequested"].includes(status!)) { + visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); + } + if (["Submitted", "ChangesRequested"].includes(status!)) { + visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); + } + } + + if (roles.includes("Reviewer")) { + if (status === "Submitted") { + visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); + visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); + } + if (status === "Accepted") { + visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); + } + if (status === "Validating") { + visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); + } + if (["Validated", "Accepted", "Submitted"].includes(status!)) { + visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); + } + } + + if (roles.includes("MapAdmin")) { + if (status === "Validated") { + visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); + } + if (status === "Uploading") { + visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); + } + } + + return ( + <section className="review-set"> + {visibleButtons.map((btn) => ( + <ReviewButton key={btn.action} {...btn} /> + ))} + </section> + ); -- 2.47.1 From 98ac17d4786a7dbd9cf1dcaac09a4da270a0f2f4 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:17:48 -0700 Subject: [PATCH 02/15] neat --- .../[submissionId]/_reviewButtons.tsx | 136 +++++++++--------- 1 file changed, 69 insertions(+), 67 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 703dab4..e3cc4b9 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -1,8 +1,9 @@ import { Button, ButtonOwnProps } from "@mui/material"; +import { useState, useEffect } from "react"; type Actions = "Completed" | "Submit" | "Reject" | "Revoke" -type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" -type Review = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" +type ApiActions = Lowercase<Actions> | "request-changes" | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" +type Review = Actions | "Request Changes" | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" interface ReviewButton { name: Review, @@ -58,78 +59,79 @@ export default function ReviewButtons(props: ReviewId) { // RequestChanges | Reviewer | Validated, Accepted, Submitted // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading - const { submissionId } = props; - const [roles, setRoles] = useState<string[]>([]); - const [status, setStatus] = useState<string | null>(null); - const [loading, setLoading] = useState(true); + const { submissionId } = props; + const [roles, setRoles] = useState<string[]>([]); + const [status, setStatus] = useState<string | null>(null); + const [loading, setLoading] = useState(true); - useEffect(() => { - async function fetchData() { - try { - // Fetch user roles - const rolesResponse = await fetch("/api/session/roles"); - const rolesData = await rolesResponse.json(); + useEffect(() => { + async function fetchData() { + try { + // Fetch user roles + const rolesResponse = await fetch("/api/session/roles"); + const rolesData = await rolesResponse.json(); - // Fetch submission status - const statusResponse = await fetch( - `/api/submissions/${submissionId}/status` - ); - const statusData = await statusResponse.json(); + // Fetch submission status + const statusResponse = await fetch( + `/api/submissions/${submissionId}/status` + ); + const statusData = await statusResponse.json(); - setRoles(rolesData); - setStatus(statusData.status); - } catch (error) { - console.error("Error fetching data:", error); - } finally { - setLoading(false); - } - } + setRoles(rolesData); + setStatus(statusData.status); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + } - fetchData(); - }, [submissionId]); + fetchData(); + }, [submissionId]); - if (loading) return <p>Loading...</p>; + if (loading) return <p>Loading...</p>; - const visibleButtons: ReviewButtonProps[] = []; + const visibleButtons: ReviewButton[] = []; - if (roles.includes("Submitter")) { - if (["UnderConstruction", "ChangesRequested"].includes(status!)) { - visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); - } - if (["Submitted", "ChangesRequested"].includes(status!)) { - visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); - } - } + if (roles.includes("Submitter")) { + if (["UnderConstruction", "ChangesRequested"].includes(status!)) { + visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); + } + if (["Submitted", "ChangesRequested"].includes(status!)) { + visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); + } + } - if (roles.includes("Reviewer")) { - if (status === "Submitted") { - visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); - visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); - } - if (status === "Accepted") { - visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); - } - if (status === "Validating") { - visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); - } - if (["Validated", "Accepted", "Submitted"].includes(status!)) { - visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); - } - } + if (roles.includes("Reviewer")) { + if (status === "Submitted") { + visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); + visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); + } + if (status === "Accepted") { + visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); + } + if (status === "Validating") { + visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); + } + if (["Validated", "Accepted", "Submitted"].includes(status!)) { + visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); + } + } - if (roles.includes("MapAdmin")) { - if (status === "Validated") { - visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); - } - if (status === "Uploading") { - visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); - } - } + if (roles.includes("MapAdmin")) { + if (status === "Validated") { + visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); + } + if (status === "Uploading") { + visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); + } + } - return ( - <section className="review-set"> - {visibleButtons.map((btn) => ( - <ReviewButton key={btn.action} {...btn} /> - ))} - </section> - ); + return ( + <section className="review-set"> + {visibleButtons.map((btn) => ( + <ReviewButton key={btn.action} {...btn} /> + ))} + </section> + ); +} -- 2.47.1 From 0955fbd88e9d7aea3b8063594e6ff2b9dec230ba Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:33:45 -0700 Subject: [PATCH 03/15] me + ai = javascript --- .../[submissionId]/_reviewButtons.tsx | 29 ++++++++++--------- web/src/app/ts/Roles.ts | 15 ++++++++++ 2 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 web/src/app/ts/Roles.ts diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index e3cc4b9..5a10850 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -1,3 +1,5 @@ +import { Roles } from "@/app/ts/Roles"; +import { SubmissionStatus } from "@/app/ts/Submission"; import { Button, ButtonOwnProps } from "@mui/material"; import { useState, useEffect } from "react"; @@ -60,8 +62,8 @@ export default function ReviewButtons(props: ReviewId) { // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading const { submissionId } = props; - const [roles, setRoles] = useState<string[]>([]); - const [status, setStatus] = useState<string | null>(null); + const [roles, setRoles] = useState<Roles>(Roles.Empty); + const [status, setStatus] = useState<SubmissionStatus>(SubmissionStatus.UnderConstruction); const [loading, setLoading] = useState(true); useEffect(() => { @@ -93,36 +95,37 @@ export default function ReviewButtons(props: ReviewId) { const visibleButtons: ReviewButton[] = []; - if (roles.includes("Submitter")) { - if (["UnderConstruction", "ChangesRequested"].includes(status!)) { + const is_submitter = false; // TODO: MY_USER === submission.Submitter + if (is_submitter) { + if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(status!)) { visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); } - if (["Submitted", "ChangesRequested"].includes(status!)) { + if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(status!)) { visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); } } - if (roles.includes("Reviewer")) { - if (status === "Submitted") { + if (roles&Roles.SubmissionReview) { + if (status === SubmissionStatus.Submitted) { visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); } - if (status === "Accepted") { + if (status === SubmissionStatus.Accepted) { visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); } - if (status === "Validating") { + if (status === SubmissionStatus.Validating) { visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); } - if (["Validated", "Accepted", "Submitted"].includes(status!)) { + if ([SubmissionStatus.Validated, SubmissionStatus.Accepted, SubmissionStatus.Submitted].includes(status!)) { visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); } } - if (roles.includes("MapAdmin")) { - if (status === "Validated") { + if (roles&Roles.SubmissionUpload) { + if (status === SubmissionStatus.Validated) { visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); } - if (status === "Uploading") { + if (status === SubmissionStatus.Uploading) { visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); } } diff --git a/web/src/app/ts/Roles.ts b/web/src/app/ts/Roles.ts new file mode 100644 index 0000000..af94e36 --- /dev/null +++ b/web/src/app/ts/Roles.ts @@ -0,0 +1,15 @@ +// Submissions roles bitflag +enum Roles { + SubmissionUpload = 1 << 6, + SubmissionReview = 1 << 5, + SubmissionRelease = 1 << 4, + ScriptWrite = 1 << 3, + MapfixUpload = 1 << 2, + MapfixReview = 1 << 1, + MapDownload = 1 << 0, + Empty = 0 +} + +export { + Roles, +} -- 2.47.1 From 1e880cb95c55cc5eb47e2a90cad89d5da36c1e90 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:39:02 -0700 Subject: [PATCH 04/15] parse int --- web/src/app/submissions/[submissionId]/_reviewButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 5a10850..5b1cdbe 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -71,7 +71,7 @@ export default function ReviewButtons(props: ReviewId) { try { // Fetch user roles const rolesResponse = await fetch("/api/session/roles"); - const rolesData = await rolesResponse.json(); + const rolesData = parseInt(await rolesResponse.text()); // Fetch submission status const statusResponse = await fetch( -- 2.47.1 From 5e8fd0744ca5b2bcad1b5fc4250f8c6a6bfe685f Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:49:41 -0700 Subject: [PATCH 05/15] plumb status --- .../[submissionId]/_reviewButtons.tsx | 29 +++++++------------ .../app/submissions/[submissionId]/page.tsx | 5 ++-- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 5b1cdbe..71fb83e 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -15,7 +15,8 @@ interface ReviewButton { } interface ReviewId { - submissionId: string + submissionId: string, + submissionStatus: number, } async function ReviewButtonClicked(action: ApiActions, submissionId: string) { @@ -61,9 +62,8 @@ export default function ReviewButtons(props: ReviewId) { // RequestChanges | Reviewer | Validated, Accepted, Submitted // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading - const { submissionId } = props; + const { submissionId, submissionStatus } = props; const [roles, setRoles] = useState<Roles>(Roles.Empty); - const [status, setStatus] = useState<SubmissionStatus>(SubmissionStatus.UnderConstruction); const [loading, setLoading] = useState(true); useEffect(() => { @@ -73,14 +73,7 @@ export default function ReviewButtons(props: ReviewId) { const rolesResponse = await fetch("/api/session/roles"); const rolesData = parseInt(await rolesResponse.text()); - // Fetch submission status - const statusResponse = await fetch( - `/api/submissions/${submissionId}/status` - ); - const statusData = await statusResponse.json(); - setRoles(rolesData); - setStatus(statusData.status); } catch (error) { console.error("Error fetching data:", error); } finally { @@ -97,35 +90,35 @@ export default function ReviewButtons(props: ReviewId) { const is_submitter = false; // TODO: MY_USER === submission.Submitter if (is_submitter) { - if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(status!)) { + if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); } - if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(status!)) { + if ([SubmissionStatus.Submitted, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", submissionId }); } } if (roles&Roles.SubmissionReview) { - if (status === SubmissionStatus.Submitted) { + if (submissionStatus === SubmissionStatus.Submitted) { visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); } - if (status === SubmissionStatus.Accepted) { + if (submissionStatus === SubmissionStatus.Accepted) { visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", submissionId }); } - if (status === SubmissionStatus.Validating) { + if (submissionStatus === SubmissionStatus.Validating) { visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); } - if ([SubmissionStatus.Validated, SubmissionStatus.Accepted, SubmissionStatus.Submitted].includes(status!)) { + if ([SubmissionStatus.Validated, SubmissionStatus.Accepted, SubmissionStatus.Submitted].includes(submissionStatus!)) { visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); } } if (roles&Roles.SubmissionUpload) { - if (status === SubmissionStatus.Validated) { + if (submissionStatus === SubmissionStatus.Validated) { visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); } - if (status === SubmissionStatus.Uploading) { + if (submissionStatus === SubmissionStatus.Uploading) { visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", submissionId }); } } diff --git a/web/src/app/submissions/[submissionId]/page.tsx b/web/src/app/submissions/[submissionId]/page.tsx index c78ba8b..3440604 100644 --- a/web/src/app/submissions/[submissionId]/page.tsx +++ b/web/src/app/submissions/[submissionId]/page.tsx @@ -17,6 +17,7 @@ import "./(styles)/page.scss"; interface ReviewId { submissionId: string; assetId: number; + submissionStatus: number; } function Ratings() { @@ -47,7 +48,7 @@ function RatingArea(submission: ReviewId) { <MapImage id={submission.assetId}/> </section> <Ratings/> - <ReviewButtons submissionId={submission.submissionId}/> + <ReviewButtons submissionId={submission.submissionId} submissionStatus={submission.submissionStatus}/> </aside> ) } @@ -97,7 +98,7 @@ export default function SubmissionInfoPage() { <Webpage> <main className="map-page-main"> <section className="review-section"> - <RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId}/> + <RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID}/> <TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} comments={[]}/> </section> </main> -- 2.47.1 From c253874ce36330aae3ce6b0930fb5a7dccb926b7 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:54:51 -0700 Subject: [PATCH 06/15] grab user + plumb submitter --- .../app/submissions/[submissionId]/_reviewButtons.tsx | 9 +++++++-- web/src/app/submissions/[submissionId]/page.tsx | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 71fb83e..73e1581 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -17,6 +17,7 @@ interface ReviewButton { interface ReviewId { submissionId: string, submissionStatus: number, + submissionSubmitter: number, } async function ReviewButtonClicked(action: ApiActions, submissionId: string) { @@ -63,6 +64,7 @@ export default function ReviewButtons(props: ReviewId) { // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading const { submissionId, submissionStatus } = props; + const [user, setUser] = useState<number|null>(null); const [roles, setRoles] = useState<Roles>(Roles.Empty); const [loading, setLoading] = useState(true); @@ -72,8 +74,12 @@ export default function ReviewButtons(props: ReviewId) { // Fetch user roles const rolesResponse = await fetch("/api/session/roles"); const rolesData = parseInt(await rolesResponse.text()); + // Fetch user roles + const userResponse = await fetch("/api/session/user"); + const userData = await userResponse.json(); setRoles(rolesData); + setUser(userData.userId); } catch (error) { console.error("Error fetching data:", error); } finally { @@ -88,8 +94,7 @@ export default function ReviewButtons(props: ReviewId) { const visibleButtons: ReviewButton[] = []; - const is_submitter = false; // TODO: MY_USER === submission.Submitter - if (is_submitter) { + if (user === props.submissionSubmitter) { if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); } diff --git a/web/src/app/submissions/[submissionId]/page.tsx b/web/src/app/submissions/[submissionId]/page.tsx index 3440604..9b8d4d1 100644 --- a/web/src/app/submissions/[submissionId]/page.tsx +++ b/web/src/app/submissions/[submissionId]/page.tsx @@ -18,6 +18,7 @@ interface ReviewId { submissionId: string; assetId: number; submissionStatus: number; + submissionSubmitter: number, } function Ratings() { @@ -48,7 +49,7 @@ function RatingArea(submission: ReviewId) { <MapImage id={submission.assetId}/> </section> <Ratings/> - <ReviewButtons submissionId={submission.submissionId} submissionStatus={submission.submissionStatus}/> + <ReviewButtons submissionId={submission.submissionId} submissionStatus={submission.submissionStatus} submissionSubmitter={submission.submissionSubmitter}/> </aside> ) } @@ -98,7 +99,7 @@ export default function SubmissionInfoPage() { <Webpage> <main className="map-page-main"> <section className="review-section"> - <RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID}/> + <RatingArea assetId={submission.AssetID} submissionId={dynamicId.submissionId} submissionStatus={submission.StatusID} submissionSubmitter={submission.Submitter}/> <TitleAndComments name={submission.DisplayName} creator={submission.Creator} review={submission.StatusID} status_message={submission.StatusMessage} asset_id={submission.AssetID} comments={[]}/> </section> </main> -- 2.47.1 From 1b87f0a396170d2a7fb52794fe30a2722254cfbe Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 20:58:17 -0700 Subject: [PATCH 07/15] gotta go fast --- .../app/submissions/[submissionId]/_reviewButtons.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 73e1581..b156a73 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -71,12 +71,10 @@ export default function ReviewButtons(props: ReviewId) { useEffect(() => { async function fetchData() { try { - // Fetch user roles - const rolesResponse = await fetch("/api/session/roles"); - const rolesData = parseInt(await rolesResponse.text()); - // Fetch user roles - const userResponse = await fetch("/api/session/user"); - const userData = await userResponse.json(); + const [rolesData, userData] = await Promise.all([ + fetch("/api/session/roles").then(rolesResponse => rolesResponse.text()).then(text => parseInt(text, 10)), + fetch("/api/session/user").then(userResponse => userResponse.json()) + ]); setRoles(rolesData); setUser(userData.userId); -- 2.47.1 From 73a3ac61bd68ab20e1959a701b9a89c104e32724 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 21:07:15 -0700 Subject: [PATCH 08/15] comment mapfix vs submission status --- web/src/app/ts/Mapfix.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/app/ts/Mapfix.ts b/web/src/app/ts/Mapfix.ts index 0a5bd25..0570db0 100644 --- a/web/src/app/ts/Mapfix.ts +++ b/web/src/app/ts/Mapfix.ts @@ -8,6 +8,7 @@ const enum MapfixStatus { Uploading = 6, Uploaded = 7, Rejected = 8, + // MapfixStatus does not have a Released state } interface MapfixInfo { -- 2.47.1 From 13651df94d9f2913c1425bbb17bf1b46d001e762 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Thu, 3 Apr 2025 21:17:18 -0700 Subject: [PATCH 09/15] case sensitive? --- web/src/app/submissions/[submissionId]/_reviewButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index b156a73..a25a0f5 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -77,7 +77,7 @@ export default function ReviewButtons(props: ReviewId) { ]); setRoles(rolesData); - setUser(userData.userId); + setUser(userData.UserID); } catch (error) { console.error("Error fetching data:", error); } finally { -- 2.47.1 From b62b2fcf7bd6c5ba3ed5878d175468c7bcaf1504 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 14:29:27 -0700 Subject: [PATCH 10/15] roles is not an enum --- .../[submissionId]/_reviewButtons.tsx | 8 ++--- web/src/app/ts/Roles.ts | 34 ++++++++++++------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index a25a0f5..d721b2e 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -1,4 +1,4 @@ -import { Roles } from "@/app/ts/Roles"; +import { Roles, RolesConstants } from "@/app/ts/Roles"; import { SubmissionStatus } from "@/app/ts/Submission"; import { Button, ButtonOwnProps } from "@mui/material"; import { useState, useEffect } from "react"; @@ -65,7 +65,7 @@ export default function ReviewButtons(props: ReviewId) { // ResetUploading | MapAdmin | Uploading const { submissionId, submissionStatus } = props; const [user, setUser] = useState<number|null>(null); - const [roles, setRoles] = useState<Roles>(Roles.Empty); + const [roles, setRoles] = useState<Roles>(RolesConstants.Empty); const [loading, setLoading] = useState(true); useEffect(() => { @@ -101,7 +101,7 @@ export default function ReviewButtons(props: ReviewId) { } } - if (roles&Roles.SubmissionReview) { + if (roles&RolesConstants.SubmissionReview) { if (submissionStatus === SubmissionStatus.Submitted) { visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); @@ -117,7 +117,7 @@ export default function ReviewButtons(props: ReviewId) { } } - if (roles&Roles.SubmissionUpload) { + if (roles&RolesConstants.SubmissionUpload) { if (submissionStatus === SubmissionStatus.Validated) { visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", submissionId }); } diff --git a/web/src/app/ts/Roles.ts b/web/src/app/ts/Roles.ts index af94e36..57a7d9d 100644 --- a/web/src/app/ts/Roles.ts +++ b/web/src/app/ts/Roles.ts @@ -1,15 +1,25 @@ -// Submissions roles bitflag -enum Roles { - SubmissionUpload = 1 << 6, - SubmissionReview = 1 << 5, - SubmissionRelease = 1 << 4, - ScriptWrite = 1 << 3, - MapfixUpload = 1 << 2, - MapfixReview = 1 << 1, - MapDownload = 1 << 0, - Empty = 0 +type Roles = number; + +// Constants +const RolesConstants = { + All: -1 as Roles, + SubmissionUpload: 1 << 6 as Roles, + SubmissionReview: 1 << 5 as Roles, + SubmissionRelease: 1 << 4 as Roles, + ScriptWrite: 1 << 3 as Roles, + MapfixUpload: 1 << 2 as Roles, + MapfixReview: 1 << 1 as Roles, + MapDownload: 1 << 0 as Roles, + Empty: 0 as Roles, +}; + +// Operations +function hasRole(flags: Roles, role: Roles): boolean { + return (flags & role) === role; } export { - Roles, -} + type Roles, + RolesConstants, + hasRole, +}; -- 2.47.1 From 6c2d759ca50faaeb511211066c4ff68bae03a9d1 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 14:44:06 -0700 Subject: [PATCH 11/15] roles is json --- web/src/app/submissions/[submissionId]/_reviewButtons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index d721b2e..0209ce3 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -72,11 +72,11 @@ export default function ReviewButtons(props: ReviewId) { async function fetchData() { try { const [rolesData, userData] = await Promise.all([ - fetch("/api/session/roles").then(rolesResponse => rolesResponse.text()).then(text => parseInt(text, 10)), + fetch("/api/session/roles").then(rolesResponse => rolesResponse.json()), fetch("/api/session/user").then(userResponse => userResponse.json()) ]); - setRoles(rolesData); + setRoles(rolesData.Roles); setUser(userData.UserID); } catch (error) { console.error("Error fetching data:", error); -- 2.47.1 From 6328db7d14ce09a29acc4b1f5fc90ae0aaa14a23 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 14:53:27 -0700 Subject: [PATCH 12/15] hide more buttons in specific cases --- .../submissions/[submissionId]/_reviewButtons.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index 0209ce3..eb97f2a 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -92,7 +92,8 @@ export default function ReviewButtons(props: ReviewId) { const visibleButtons: ReviewButton[] = []; - if (user === props.submissionSubmitter) { + const is_submitter = user === props.submissionSubmitter; + if (is_submitter) { if ([SubmissionStatus.UnderConstruction, SubmissionStatus.ChangesRequested].includes(submissionStatus!)) { visibleButtons.push({ name: "Submit", action: "submit", color: "info", submissionId }); } @@ -102,7 +103,9 @@ export default function ReviewButtons(props: ReviewId) { } if (roles&RolesConstants.SubmissionReview) { - if (submissionStatus === SubmissionStatus.Submitted) { + // you can't review your own submission! + // note that this means there needs to be more than one person with SubmissionReview + if (!is_submitter && submissionStatus === SubmissionStatus.Submitted) { visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", submissionId }); visibleButtons.push({ name: "Reject", action: "reject", color: "error", submissionId }); } @@ -112,7 +115,12 @@ export default function ReviewButtons(props: ReviewId) { if (submissionStatus === SubmissionStatus.Validating) { visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", submissionId }); } - if ([SubmissionStatus.Validated, SubmissionStatus.Accepted, SubmissionStatus.Submitted].includes(submissionStatus!)) { + // this button serves the same purpose as Revoke if you are both + // the map submitter and have SubmissionReview when status is Submitted + if ( + [SubmissionStatus.Validated, SubmissionStatus.Accepted].includes(submissionStatus!) + || !is_submitter && submissionStatus == SubmissionStatus.Submitted + ) { visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", submissionId }); } } -- 2.47.1 From f38e5d32f9999b3a6317254afa686105202cfad6 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 15:00:03 -0700 Subject: [PATCH 13/15] do it for mapfiex --- .../mapfixes/[mapfixId]/_reviewButtons.tsx | 96 ++++++++++++++++--- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx index 24d75c4..e62d42c 100644 --- a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx +++ b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx @@ -1,8 +1,11 @@ +import { Roles, RolesConstants } from "@/app/ts/Roles"; +import { MapfixStatus } from "@/app/ts/Mapfix"; import { Button, ButtonOwnProps } from "@mui/material"; +import { useState, useEffect } from "react"; type Actions = "Completed" | "Submit" | "Reject" | "Revoke" -type ApiActions = Lowercase<Actions> | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" -type Review = Actions | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" +type ApiActions = Lowercase<Actions> | "request-changes" | "trigger-validate" | "retry-validate" | "trigger-upload" | "reset-uploading" | "reset-validating" +type Review = Actions | "Request Changes" | "Accept" | "Validate" | "Upload" | "Reset Uploading (fix softlocked status)" | "Reset Validating (fix softlocked status)" | "Request Changes" interface ReviewButton { name: Review, @@ -12,7 +15,9 @@ interface ReviewButton { } interface ReviewId { - mapfixId: string + mapfixId: string, + mapfixStatus: number, + mapfixSubmitter: number, } async function ReviewButtonClicked(action: ApiActions, mapfixId: string) { @@ -45,7 +50,6 @@ function ReviewButton(props: ReviewButton) { } export default function ReviewButtons(props: ReviewId) { - const mapfixId = props.mapfixId // When is each button visible? // Multiple buttons can be visible at once. // Action | Role | When Current Status is One of: @@ -59,16 +63,82 @@ export default function ReviewButtons(props: ReviewId) { // RequestChanges | Reviewer | Validated, Accepted, Submitted // Upload | MapAdmin | Validated // ResetUploading | MapAdmin | Uploading + const { mapfixId, mapfixStatus } = props; + const [user, setUser] = useState<number|null>(null); + const [roles, setRoles] = useState<Roles>(RolesConstants.Empty); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchData() { + try { + const [rolesData, userData] = await Promise.all([ + fetch("/api/session/roles").then(rolesResponse => rolesResponse.json()), + fetch("/api/session/user").then(userResponse => userResponse.json()) + ]); + + setRoles(rolesData.Roles); + setUser(userData.UserID); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + } + + fetchData(); + }, [mapfixId]); + + if (loading) return <p>Loading...</p>; + + const visibleButtons: ReviewButton[] = []; + + const is_submitter = user === props.mapfixSubmitter; + if (is_submitter) { + if ([MapfixStatus.UnderConstruction, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) { + visibleButtons.push({ name: "Submit", action: "submit", color: "info", mapfixId }); + } + if ([MapfixStatus.Submitted, MapfixStatus.ChangesRequested].includes(mapfixStatus!)) { + visibleButtons.push({ name: "Revoke", action: "revoke", color: "info", mapfixId }); + } + } + + if (roles&RolesConstants.MapfixReview) { + // you can't review your own mapfix! + // note that this means there needs to be more than one person with MapfixReview + if (!is_submitter && mapfixStatus === MapfixStatus.Submitted) { + visibleButtons.push({ name: "Accept", action: "trigger-validate", color: "info", mapfixId }); + visibleButtons.push({ name: "Reject", action: "reject", color: "error", mapfixId }); + } + if (mapfixStatus === MapfixStatus.Accepted) { + visibleButtons.push({ name: "Validate", action: "retry-validate", color: "info", mapfixId }); + } + if (mapfixStatus === MapfixStatus.Validating) { + visibleButtons.push({ name: "Reset Validating (fix softlocked status)", action: "reset-validating", color: "error", mapfixId }); + } + // this button serves the same purpose as Revoke if you are both + // the map submitter and have MapfixReview when status is Submitted + if ( + [MapfixStatus.Validated, MapfixStatus.Accepted].includes(mapfixStatus!) + || !is_submitter && mapfixStatus == MapfixStatus.Submitted + ) { + visibleButtons.push({ name: "Request Changes", action: "request-changes", color: "error", mapfixId }); + } + } + + if (roles&RolesConstants.MapfixUpload) { + if (mapfixStatus === MapfixStatus.Validated) { + visibleButtons.push({ name: "Upload", action: "trigger-upload", color: "info", mapfixId }); + } + if (mapfixStatus === MapfixStatus.Uploading) { + visibleButtons.push({ name: "Reset Uploading (fix softlocked status)", action: "reset-uploading", color: "error", mapfixId }); + } + } + return ( <section className="review-set"> - <ReviewButton color="info" name="Submit" action="submit" mapfixId={mapfixId}/> - <ReviewButton color="info" name="Revoke" action="revoke" mapfixId={mapfixId}/> - <ReviewButton color="info" name="Accept" action="trigger-validate" mapfixId={mapfixId}/> - <ReviewButton color="info" name="Validate" action="retry-validate" mapfixId={mapfixId}/> - <ReviewButton color="error" name="Reject" action="reject" mapfixId={mapfixId}/> - <ReviewButton color="info" name="Upload" action="trigger-upload" mapfixId={mapfixId}/> - <ReviewButton color="error" name="Reset Uploading (fix softlocked status)" action="reset-uploading" mapfixId={mapfixId}/> - <ReviewButton color="error" name="Reset Validating (fix softlocked status)" action="reset-validating" mapfixId={mapfixId}/> + {visibleButtons.map((btn) => ( + <ReviewButton key={btn.action} {...btn} /> + ))} </section> - ) + ); } -- 2.47.1 From 6aa0e85c0b630aa03600ca69e1c5b7fae1e31fb5 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 15:02:46 -0700 Subject: [PATCH 14/15] page --- web/src/app/mapfixes/[mapfixId]/page.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/src/app/mapfixes/[mapfixId]/page.tsx b/web/src/app/mapfixes/[mapfixId]/page.tsx index cc38cb3..43d3f2e 100644 --- a/web/src/app/mapfixes/[mapfixId]/page.tsx +++ b/web/src/app/mapfixes/[mapfixId]/page.tsx @@ -15,7 +15,9 @@ import { useState, useEffect } from "react"; import "./(styles)/page.scss"; interface ReviewId { - mapfixId: string + mapfixId: string, + mapfixStatus: number; + mapfixSubmitter: number, } function Ratings() { @@ -46,7 +48,7 @@ function RatingArea(mapfix: ReviewId) { <MapImage/> </section> <Ratings/> - <ReviewButtons mapfixId={mapfix.mapfixId}/> + <ReviewButtons mapfixId={mapfix.mapfixId} mapfixStatus={mapfix.mapfixStatus} mapfixSubmitter={mapfix.mapfixSubmitter}/> </aside> ) } @@ -96,7 +98,7 @@ export default function MapfixInfoPage() { <Webpage> <main className="map-page-main"> <section className="review-section"> - <RatingArea mapfixId={dynamicId.mapfixId}/> + <RatingArea mapfixId={dynamicId.mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter}/> <TitleAndComments name={mapfix.DisplayName} creator={mapfix.Creator} review={mapfix.StatusID} status_message={mapfix.StatusMessage} asset_id={mapfix.AssetID} comments={[]}/> </section> </main> -- 2.47.1 From 7990e25c0a087784fb91f02c01ec1b50a1a6dfb9 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Fri, 4 Apr 2025 15:07:01 -0700 Subject: [PATCH 15/15] no available actions --- web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx | 10 +++++++--- .../app/submissions/[submissionId]/_reviewButtons.tsx | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx index e62d42c..55480b6 100644 --- a/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx +++ b/web/src/app/mapfixes/[mapfixId]/_reviewButtons.tsx @@ -136,9 +136,13 @@ export default function ReviewButtons(props: ReviewId) { return ( <section className="review-set"> - {visibleButtons.map((btn) => ( - <ReviewButton key={btn.action} {...btn} /> - ))} + {visibleButtons.length === 0 ? ( + <p>No available actions</p> + ) : ( + visibleButtons.map((btn) => ( + <ReviewButton key={btn.action} {...btn} /> + )) + )} </section> ); } diff --git a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx index eb97f2a..069cfd6 100644 --- a/web/src/app/submissions/[submissionId]/_reviewButtons.tsx +++ b/web/src/app/submissions/[submissionId]/_reviewButtons.tsx @@ -136,9 +136,13 @@ export default function ReviewButtons(props: ReviewId) { return ( <section className="review-set"> - {visibleButtons.map((btn) => ( - <ReviewButton key={btn.action} {...btn} /> - ))} + {visibleButtons.length === 0 ? ( + <p>No available actions</p> + ) : ( + visibleButtons.map((btn) => ( + <ReviewButton key={btn.action} {...btn} /> + )) + )} </section> ); } -- 2.47.1