From a19bc4d3800c445602a400c981e85407eef7670d Mon Sep 17 00:00:00 2001 From: itzaname Date: Fri, 26 Dec 2025 20:32:55 -0500 Subject: [PATCH 1/2] Add mapfix history on maps page --- web/src/app/maps/[mapId]/page.tsx | 290 ++++++++++++++++++++++++++---- web/src/app/ts/Mapfix.ts | 39 ++++ 2 files changed, 290 insertions(+), 39 deletions(-) diff --git a/web/src/app/maps/[mapId]/page.tsx b/web/src/app/maps/[mapId]/page.tsx index d39c483..f519217 100644 --- a/web/src/app/maps/[mapId]/page.tsx +++ b/web/src/app/maps/[mapId]/page.tsx @@ -4,7 +4,7 @@ import { useParams, useNavigate } from "react-router-dom"; import { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { Snackbar, Alert } from "@mui/material"; -import { MapfixStatus, type MapfixInfo } from "@/app/ts/Mapfix"; +import { MapfixStatus, type MapfixInfo, getMapfixStatusInfo } from "@/app/ts/Mapfix"; import LaunchIcon from '@mui/icons-material/Launch'; import { useAssetThumbnail } from "@/app/hooks/useThumbnails"; @@ -23,7 +23,11 @@ import { Stack, CardMedia, Tooltip, - IconButton + IconButton, + List, + ListItem, + ListItemIcon, + Pagination } from "@mui/material"; import NavigateNextIcon from "@mui/icons-material/NavigateNext"; import CalendarTodayIcon from "@mui/icons-material/CalendarToday"; @@ -33,6 +37,11 @@ import BugReportIcon from "@mui/icons-material/BugReport"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"; import DownloadIcon from '@mui/icons-material/Download'; +import HistoryIcon from '@mui/icons-material/History'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import CancelIcon from '@mui/icons-material/Cancel'; +import BuildIcon from '@mui/icons-material/Build'; +import PendingIcon from '@mui/icons-material/Pending'; import {hasRole, RolesConstants} from "@/app/ts/Roles"; import {useTitle} from "@/app/hooks/useTitle"; @@ -45,6 +54,7 @@ export default function MapDetails() { const [copySuccess, setCopySuccess] = useState(false); const [roles, setRoles] = useState(RolesConstants.Empty); const [mapfixes, setMapfixes] = useState([]); + const [fixesPage, setFixesPage] = useState(1); useTitle(map ? `${map.DisplayName}` : 'Loading Map...'); @@ -111,9 +121,8 @@ export default function MapDetails() { allMapfixes = allMapfixes.concat(data.Mapfixes); page++; } while (allMapfixes.length < total); - // Filter out rejected, uploading, uploaded (StatusID > 7) - const active = allMapfixes.filter((fix: MapfixInfo) => fix.StatusID <= MapfixStatus.Validated); - setMapfixes(active); + // Store all mapfixes for history display + setMapfixes(allMapfixes); } catch { setMapfixes([]); } @@ -154,6 +163,16 @@ export default function MapDetails() { } }; + const getStatusIcon = (iconName: string) => { + switch (iconName) { + case "Build": return BuildIcon; + case "Pending": return PendingIcon; + case "CheckCircle": return CheckCircleIcon; + case "Cancel": return CancelIcon; + default: return PendingIcon; + } + }; + const handleSubmitMapfix = () => { navigate(`/maps/${mapId}/fix`); }; @@ -324,7 +343,8 @@ export default function MapDetails() { sx={{ borderRadius: 2, overflow: 'hidden', - position: 'relative' + position: 'relative', + mb: 3 }} > @@ -355,6 +375,231 @@ export default function MapDetails() { /> + + {/* Mapfix Section - Active + History */} + {mapfixes.length > 0 && (() => { + const activeFix = mapfixes.find(fix => fix.StatusID <= MapfixStatus.Validated); + const releasedFixes = mapfixes.filter(fix => fix.StatusID === MapfixStatus.Released); + const hasContent = activeFix || releasedFixes.length > 0; + + if (!hasContent) return null; + + // Pagination for released fixes + const fixesPerPage = 5; + const totalPages = Math.ceil(releasedFixes.length / fixesPerPage); + const startIndex = (fixesPage - 1) * fixesPerPage; + const endIndex = startIndex + fixesPerPage; + const paginatedFixes = releasedFixes + .sort((a, b) => b.CreatedAt - a.CreatedAt) + .slice(startIndex, endIndex); + + return ( + + + + + Mapfixes + + + + + + {/* Active Mapfix - shown first with special styling */} + {activeFix && ( + + 0 ? 2 : 0, + '&:hover': { + backgroundColor: 'rgba(25, 118, 210, 0.12)', + transform: 'translateX(4px)' + }, + textDecoration: 'none', + color: 'inherit', + display: 'block' + }} + > + + + {(() => { + const statusInfo = getMapfixStatusInfo(activeFix.StatusID); + const StatusIcon = getStatusIcon(statusInfo.iconName); + return ( + + ); + })()} + + + + + {activeFix.Description} + + + + + + + + + + + + {activeFix.Creator} + + + + + + {formatDate(activeFix.CreatedAt)} + + + + + + + + + + )} + + {/* Released Fixes History */} + {releasedFixes.length > 0 && ( + <> + {activeFix && ( + + + + + + )} + {paginatedFixes.map((fix, index) => { + const statusInfo = getMapfixStatusInfo(fix.StatusID); + const StatusIcon = getStatusIcon(statusInfo.iconName); + + return ( + + + + + + + + + + {fix.Description} + + + + + + + {fix.Creator} + + + + + + {formatDate(fix.CreatedAt)} + + + + + + + + + {index < paginatedFixes.length - 1 && } + + ); + })} + + {/* Pagination */} + {totalPages > 1 && ( + + setFixesPage(page)} + color="primary" + size="medium" + /> + + )} + + )} + + + ); + })()} {/* Map Details Section */} @@ -399,39 +644,6 @@ export default function MapDetails() { - - {/* Active Mapfix in Map Details */} - {mapfixes.length > 0 && (() => { - const active = mapfixes.find(fix => fix.StatusID <= MapfixStatus.Validated); - const latest = mapfixes.reduce((a, b) => (a.CreatedAt > b.CreatedAt ? a : b)); - const showFix = active || latest; - return ( - - - Active Mapfix - - - - {showFix.Description} - - - - - ); - })()} diff --git a/web/src/app/ts/Mapfix.ts b/web/src/app/ts/Mapfix.ts index 2fabdb1..f62a9b1 100644 --- a/web/src/app/ts/Mapfix.ts +++ b/web/src/app/ts/Mapfix.ts @@ -66,9 +66,48 @@ function MapfixStatusToString(mapfix_status: MapfixStatus): string { } } +interface MapfixStatusInfo { + label: string; + color: 'default' | 'error' | 'warning' | 'success' | 'primary' | 'info'; + iconName: string; +} + +function getMapfixStatusInfo(statusId: MapfixStatus): MapfixStatusInfo { + switch (statusId) { + case MapfixStatus.UnderConstruction: + return { label: "Under Construction", color: "default", iconName: "Build" }; + case MapfixStatus.ChangesRequested: + return { label: "Changes Requested", color: "warning", iconName: "Pending" }; + case MapfixStatus.Submitting: + return { label: "Submitting", color: "info", iconName: "Pending" }; + case MapfixStatus.Submitted: + return { label: "Submitted", color: "info", iconName: "CheckCircle" }; + case MapfixStatus.AcceptedUnvalidated: + return { label: "Accepted (Unvalidated)", color: "primary", iconName: "CheckCircle" }; + case MapfixStatus.Validating: + return { label: "Validating", color: "info", iconName: "Pending" }; + case MapfixStatus.Validated: + return { label: "Validated", color: "success", iconName: "CheckCircle" }; + case MapfixStatus.Uploading: + return { label: "Uploading", color: "info", iconName: "Pending" }; + case MapfixStatus.Uploaded: + return { label: "Uploaded", color: "success", iconName: "CheckCircle" }; + case MapfixStatus.Rejected: + return { label: "Rejected", color: "error", iconName: "Cancel" }; + case MapfixStatus.Released: + return { label: "Released", color: "success", iconName: "CheckCircle" }; + case MapfixStatus.Releasing: + return { label: "Releasing", color: "info", iconName: "Pending" }; + default: + return { label: "Unknown", color: "default", iconName: "Pending" }; + } +} + export { MapfixStatus, MapfixStatusToString, + getMapfixStatusInfo, type MapfixInfo, type MapfixList, + type MapfixStatusInfo, } -- 2.49.1 From 01cfe678480d154fcade97899edb50d0ee4c1cb0 Mon Sep 17 00:00:00 2001 From: itzaname Date: Fri, 26 Dec 2025 20:38:18 -0500 Subject: [PATCH 2/2] Just exclude rejected and released for active list --- web/src/app/maps/[mapId]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/app/maps/[mapId]/page.tsx b/web/src/app/maps/[mapId]/page.tsx index f519217..935b0fe 100644 --- a/web/src/app/maps/[mapId]/page.tsx +++ b/web/src/app/maps/[mapId]/page.tsx @@ -378,7 +378,7 @@ export default function MapDetails() { {/* Mapfix Section - Active + History */} {mapfixes.length > 0 && (() => { - const activeFix = mapfixes.find(fix => fix.StatusID <= MapfixStatus.Validated); + const activeFix = mapfixes.find(fix => fix.StatusID !== MapfixStatus.Rejected && fix.StatusID !== MapfixStatus.Released); const releasedFixes = mapfixes.filter(fix => fix.StatusID === MapfixStatus.Released); const hasContent = activeFix || releasedFixes.length > 0; -- 2.49.1