Submitter stuff
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
ic3w0lf
2025-06-27 23:29:58 -06:00
parent 7f939810cf
commit 26314ff837
2 changed files with 40 additions and 69 deletions

View File

@@ -1,56 +1,8 @@
import { Paper, Grid, Typography, Box } from "@mui/material";
import { Paper, Grid, Typography } from "@mui/material";
import { ReviewItemHeader } from "./ReviewItemHeader";
import { CopyableField } from "@/app/_components/review/CopyableField";
import { SubmissionInfo } from "@/app/ts/Submission";
import { MapfixInfo } from "@/app/ts/Mapfix";
import { useState, useEffect } from "react";
import Link from "next/link";
import LaunchIcon from '@mui/icons-material/Launch'; // Import the icon
// New component to fetch and display submitter info
function SubmitterInfo({ submitterId }: { submitterId: string | number }) {
const [name, setName] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!submitterId) return;
const fetchUserName = async () => {
try {
setLoading(true);
// Use the internal proxy API route
const response = await fetch(`/proxy/users/${submitterId}`);
if (!response.ok) {
throw new Error('Failed to fetch user');
}
const data = await response.json();
setName(`@${data.name}`);
} catch (error) {
console.error("Error fetching user name:", error);
setName(String(submitterId)); // Fallback to ID on error
} finally {
setLoading(false);
}
};
fetchUserName();
}, [submitterId]);
if (loading) {
return <Typography variant="body1">Loading...</Typography>;
}
return (
<Link href={`https://www.roblox.com/users/${submitterId}/profile`} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none', color: 'inherit' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, '&:hover': { textDecoration: 'underline' } }}>
<Typography>
{name || submitterId}
</Typography>
<LaunchIcon sx={{ fontSize: '1rem', color: 'text.secondary' }} />
</Box>
</Link>
);
}
// Define a field configuration for specific types
interface FieldConfig {
@@ -82,14 +34,12 @@ export function ReviewItem({
if (isSubmission) {
// Fields for Submission
fields = [
{ key: 'Submitter', label: 'Submitter' },
{ key: 'AssetID', label: 'Asset ID' },
{ key: 'UploadedAssetID', label: 'Uploaded Asset ID' },
];
} else if (isMapfix) {
// Fields for Mapfix
fields = [
{ key: 'Submitter', label: 'Submitter' },
{ key: 'AssetID', label: 'Asset ID' },
{ key: 'TargetAssetID', label: 'Target Asset ID' },
];
@@ -108,16 +58,6 @@ export function ReviewItem({
<Grid container spacing={2} sx={{ mt: 2 }}>
{fields.map((field) => {
const fieldValue = (item as never)[field.key];
if (field.key === 'Submitter') {
return (
<Grid item xs={12} sm={6} key={field.key}>
<Typography variant="subtitle2" color="text.secondary">{field.label}</Typography>
<SubmitterInfo submitterId={fieldValue} />
</Grid>
);
}
const displayValue = fieldValue === 0 || fieldValue == null ? 'N/A' : fieldValue;
const isAssetId = field.key.includes('AssetID') && fieldValue !== 0 && fieldValue != null;

View File

@@ -3,19 +3,52 @@ import { StatusChip } from "@/app/_components/statusChip";
import { SubmissionStatus } from "@/app/ts/Submission";
import { MapfixStatus } from "@/app/ts/Mapfix";
import {Status, StatusMatches} from "@/app/ts/Status";
import { useState, useEffect } from "react";
import Link from "next/link";
import LaunchIcon from '@mui/icons-material/Launch';
type StatusIdType = SubmissionStatus | MapfixStatus;
function SubmitterName({ submitterId }: { submitterId: number }) {
const [name, setName] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!submitterId) return;
const fetchUserName = async () => {
try {
setLoading(true);
const response = await fetch(`/proxy/users/${submitterId}`);
if (!response.ok) throw new Error('Failed to fetch user');
const data = await response.json();
setName(`@${data.name}`);
} catch {
setName(String(submitterId));
} finally {
setLoading(false);
}
};
fetchUserName();
}, [submitterId]);
if (loading) return <Typography variant="body1">Loading...</Typography>;
return <Link href={`https://www.roblox.com/users/${submitterId}/profile`} target="_blank" rel="noopener noreferrer" style={{ textDecoration: 'none', color: 'inherit' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, '&:hover': { textDecoration: 'underline' } }}>
<Typography>
{name || submitterId}
</Typography>
<LaunchIcon sx={{ fontSize: '1rem', color: 'text.secondary' }} />
</Box>
</Link>
}
interface ReviewItemHeaderProps {
displayName: string;
statusId: StatusIdType;
statusId: SubmissionStatus | MapfixStatus;
creator: string | null | undefined;
submitterId: number;
}
export const ReviewItemHeader = ({ displayName, statusId, creator }: ReviewItemHeaderProps) => {
export const ReviewItemHeader = ({ displayName, statusId, creator, submitterId }: ReviewItemHeaderProps) => {
const isProcessing = StatusMatches(statusId, [Status.Validating, Status.Uploading, Status.Submitting]);
const pulse = keyframes`
0%, 100% { opacity: 0.2; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1); }
@@ -25,7 +58,7 @@ export const ReviewItemHeader = ({ displayName, statusId, creator }: ReviewItemH
<>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h4" component="h1" gutterBottom>
{displayName}
{displayName} by {creator}
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{isProcessing && (
@@ -60,9 +93,7 @@ export const ReviewItemHeader = ({ displayName, statusId, creator }: ReviewItemH
src={`/thumbnails/user/${submitterId}`}
sx={{ mr: 1, width: 24, height: 24 }}
/>
<Typography variant="body1">
by {creator || "Unknown Creator"}
</Typography>
<SubmitterName submitterId={submitterId} />
</Box>
</>
);