fix(submissions): surface each linked build's outcome to the submitter
/me/submissions (and the admin queue) now attach build_status/build_error by a read-only Builder.Get — until now a failed build was visible only on the admin-tier /images/build routes, so the person who submitted the modpack never learned the build died. A missing build row renders as "no outcome"; any other lookup failure surfaces instead of being swallowed. The panel's My Submissions page renders the outcome in the expanded row, localised.
This commit is contained in:
9 files changed
+225
-8
No files matched your search
@@ -19,6 +19,13 @@
|
||||
"table_created_at": "Submitted At",
|
||||
"table_reviewed_by": "Reviewed By",
|
||||
"table_reject_reason": "Reject Reason",
|
||||
"table_build_status": "Build Status",
|
||||
"table_build_error": "Build Error",
|
||||
"build_status_pending": "Queued",
|
||||
"build_status_building": "Building",
|
||||
"build_status_succeeded": "Succeeded",
|
||||
"build_status_failed": "Failed",
|
||||
"build_status_cancelled": "Cancelled",
|
||||
"no_submissions_title": "No Submissions Found",
|
||||
"no_submissions_hint": "You haven't submitted any modpacks yet.",
|
||||
"search_placeholder": "Search submissions...",
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
"table_created_at": "提交时间",
|
||||
"table_reviewed_by": "审核人",
|
||||
"table_reject_reason": "拒绝原因",
|
||||
"table_build_status": "构建状态",
|
||||
"table_build_error": "构建失败原因",
|
||||
"build_status_pending": "排队中",
|
||||
"build_status_building": "构建中",
|
||||
"build_status_succeeded": "构建成功",
|
||||
"build_status_failed": "构建失败",
|
||||
"build_status_cancelled": "已取消",
|
||||
"no_submissions_title": "暂无提交记录",
|
||||
"no_submissions_hint": "您还没有提交过任何模组包。",
|
||||
"search_placeholder": "搜索提交记录...",
|
||||
|
||||
@@ -263,6 +263,8 @@ export interface Submission {
|
||||
status: SubmissionStatus;
|
||||
image_ref?: string;
|
||||
build_id?: string;
|
||||
build_status?: BuildStatus;
|
||||
build_error?: string;
|
||||
reviewed_by?: string;
|
||||
reject_reason?: string;
|
||||
created_at: string;
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -37,10 +38,29 @@ import { SubmissionStatusBadge } from "@/components/SubmissionStatusBadge";
|
||||
import { api, humanizeError } from "@/lib/api";
|
||||
import { useAsync } from "@/lib/hooks";
|
||||
import { formatRelative, formatAbsolute } from "@/lib/format";
|
||||
import type { Submission, SubmissionStatus } from "@/lib/types";
|
||||
import type { BuildStatus, Submission, SubmissionStatus } from "@/lib/types";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
// The linked build's outcome as shown in a row's expanded details. Colors mirror
|
||||
// the admin build page; the labels are player-facing, so they come from this
|
||||
// page's namespace instead of the raw status string.
|
||||
const BUILD_STATUS_STYLE: Record<BuildStatus, string> = {
|
||||
pending: "bg-amber-500/10 text-amber-500 border-amber-500/20 animate-pulse",
|
||||
building: "bg-blue-500/10 text-blue-500 border-blue-500/20 animate-pulse",
|
||||
succeeded: "bg-emerald-500/10 text-emerald-500 border-emerald-500/20",
|
||||
failed: "bg-rose-500/10 text-rose-500 border-rose-500/20",
|
||||
cancelled: "bg-zinc-500/10 text-zinc-400 border-zinc-500/20",
|
||||
};
|
||||
|
||||
const BUILD_STATUS_I18N: Record<BuildStatus, string> = {
|
||||
pending: "build_status_pending",
|
||||
building: "build_status_building",
|
||||
succeeded: "build_status_succeeded",
|
||||
failed: "build_status_failed",
|
||||
cancelled: "build_status_cancelled",
|
||||
};
|
||||
|
||||
function formatBytes(bytes: number, decimals = 2) {
|
||||
if (bytes === 0) return "0 Bytes";
|
||||
const k = 1024;
|
||||
@@ -411,6 +431,32 @@ export function MySubmissionsPage() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Linked build outcome — a submitter's only view of a
|
||||
failed build (the build pages are admin-tier). */}
|
||||
{sub.build_id && sub.build_status && (
|
||||
<div className="pt-1 border-t border-border/20 mt-2 flex flex-col gap-1.5">
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<span className="font-semibold text-foreground">{t("table_build_status")}:</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={cn(
|
||||
"font-medium select-none pointer-events-none",
|
||||
BUILD_STATUS_STYLE[sub.build_status],
|
||||
)}
|
||||
>
|
||||
{t(BUILD_STATUS_I18N[sub.build_status])}
|
||||
</Badge>
|
||||
<code className="font-mono text-[10px] text-muted-foreground/75">{sub.build_id}</code>
|
||||
</div>
|
||||
{sub.build_status === "failed" && sub.build_error && (
|
||||
<div className="text-rose-500 font-medium break-all whitespace-pre-wrap">
|
||||
<span className="font-semibold text-foreground mr-1.5">{t("table_build_error")}:</span>
|
||||
{sub.build_error}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user