Release Healer Man 0.1.38

This commit is contained in:
phenom committed 2026-09-12 23:55:24 -04:00
1 parent 361734bc41
commit b417b7c3c3
48 files changed
+1982 -90

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
import { useMemo, useRef, useState, type FormEvent } from "react";
import { useShellStore } from "../app/shellStore";
import { DUNGEON_REPORT_COMMENT_MAX_LENGTH, submitDungeonReport } from "../app/dungeonReports";
import { dungeonById } from "../game/dungeonCatalog";
import { useGameStore } from "../game/store";
import { useMenuController, type MenuAction } from "../input/useMenuController";
import { ControllerButton } from "./ControllerButton";
export function DungeonIssueReportPanel() {
const dungeonId = useGameStore((state) => state.activeDungeonId);
const openOverlay = useGameStore((state) => state.openOverlay);
const session = useShellStore((state) => state.session);
const [comment, setComment] = useState("");
const [submitting, setSubmitting] = useState(false);
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState("");
const busy = useRef(false);
const form = useRef<HTMLFormElement>(null);
const back = () => { if (!busy.current) openOverlay("pause"); };
const canSubmit = Boolean(comment.trim()) && comment.length <= DUNGEON_REPORT_COMMENT_MAX_LENGTH && !submitting;
const actions = useMemo<MenuAction[]>(() => [
{ id: "report-back", run: back, enabled: !submitting },
{ id: "report-submit", run: () => form.current?.requestSubmit(), enabled: canSubmit && !submitted },
], [canSubmit, openOverlay, submitted, submitting]);
const controller = useMenuController(actions, { initialId: "report-back", onBack: back });
const submit = async (event: FormEvent) => {
event.preventDefault();
if (!canSubmit || busy.current || submitted) return;
busy.current = true;
setSubmitting(true);
setError("");
try {
await submitDungeonReport(session, dungeonId, comment);
setSubmitted(true);
} catch (failure) {
setError(failure instanceof Error ? failure.message : "Your report could not be submitted. Please try again.");
} finally {
busy.current = false;
setSubmitting(false);
}
};
return (
<div className="modal-backdrop" role="presentation">
<section className="pause-menu dungeon-issue-report" role="dialog" aria-modal="true" aria-labelledby="issue-report-title">
<p className="eyebrow">Dungeon feedback</p>
<h2 id="issue-report-title">Report an issue</h2>
<p>{dungeonById(dungeonId)?.title ?? dungeonId}</p>
{submitted ? <p role="status">Report submitted. You can follow its status in View Reports on the main menu.</p> : (
<form ref={form} onSubmit={(event) => void submit(event)}>
<label htmlFor="dungeon-issue-comment">Describe the issue</label>
<textarea id="dungeon-issue-comment" autoFocus required maxLength={DUNGEON_REPORT_COMMENT_MAX_LENGTH}
value={comment} disabled={submitting} onChange={(event) => setComment(event.target.value)}
aria-describedby="dungeon-issue-count" placeholder="What happened, and what were you doing when it happened?" />
<small id="dungeon-issue-count">{comment.length} / {DUNGEON_REPORT_COMMENT_MAX_LENGTH} characters</small>
{error && <p role="alert" className="dungeon-report-error">{error}</p>}
<ControllerButton controlId="report-submit" selectedId={controller.selectedId} select={controller.select}
className="button button--primary" type="submit" disabled={!canSubmit}>
{submitting ? "Submitting..." : "Submit report"}
</ControllerButton>
</form>
)}
<ControllerButton controlId="report-back" selectedId={controller.selectedId} select={controller.select}
className="button" type="button" onClick={back} disabled={submitting}>Back to pause menu</ControllerButton>
</section>
</div>
);
}