Spaces:
Sleeping
Sleeping
| /** Bottom sheet to finish a pending entry: Worked | Partial | Failed. */ | |
| import { useEffect, useState } from "preact/hooks"; | |
| import { patchEntry, type Entry } from "../api"; | |
| import { Button } from "./Button"; | |
| import { ResultChips, type ResolveResult } from "./ResultChips"; | |
| import { Sheet } from "./Sheet"; | |
| import { useToast } from "./Toast"; | |
| type Props = { | |
| open: boolean; | |
| queue: Entry[]; | |
| onClose: () => void; | |
| onQueueChange: (next: Entry[]) => void; | |
| onResolved: () => void; | |
| }; | |
| export function ResolvePendingSheet({ | |
| open, | |
| queue, | |
| onClose, | |
| onQueueChange, | |
| onResolved, | |
| }: Props) { | |
| const toast = useToast(); | |
| const current = queue[0] ?? null; | |
| const [result, setResult] = useState<ResolveResult | null>(null); | |
| const [remedy, setRemedy] = useState(""); | |
| const [busy, setBusy] = useState(false); | |
| useEffect(() => { | |
| if (!current) return; | |
| setResult(null); | |
| setRemedy(current.remedy || ""); | |
| }, [current?.id]); | |
| if (!open) return null; | |
| const save = async () => { | |
| if (!current || !result) return; | |
| setBusy(true); | |
| try { | |
| await patchEntry(current.id, { result, remedy: remedy.trim() }); | |
| toast.show("Saved"); | |
| onResolved(); | |
| const rest = queue.slice(1); | |
| if (rest.length) { | |
| onQueueChange(rest); | |
| } else { | |
| onClose(); | |
| } | |
| } catch (err) { | |
| toast.show(err instanceof Error ? err.message : "Could not save", "error"); | |
| } finally { | |
| setBusy(false); | |
| } | |
| }; | |
| return ( | |
| <Sheet | |
| open={open} | |
| title="Finish entry" | |
| onClose={onClose} | |
| tall | |
| > | |
| {current ? ( | |
| <div class="resolve-sheet stack"> | |
| <div class="resolve-snippet surface-card"> | |
| {current.activity ? ( | |
| <p class="muted resolve-activity">{current.activity}</p> | |
| ) : null} | |
| <p>{current.happened}</p> | |
| </div> | |
| <div> | |
| <span class="field-label">Result</span> | |
| <ResultChips | |
| value={result} | |
| excludePending | |
| onChange={(value) => setResult(value as ResolveResult)} | |
| /> | |
| </div> | |
| <div> | |
| <label class="field-label" for="resolve-remedy"> | |
| Remedy | |
| </label> | |
| <input | |
| id="resolve-remedy" | |
| class="field-input" | |
| type="text" | |
| value={remedy} | |
| placeholder="What did you try?" | |
| onInput={(e) => setRemedy((e.target as HTMLInputElement).value)} | |
| /> | |
| </div> | |
| {queue.length > 1 ? ( | |
| <p class="muted">{queue.length - 1} more pending after this</p> | |
| ) : null} | |
| <div class="resolve-sticky"> | |
| <Button | |
| disabled={busy || !result} | |
| onClick={save} | |
| className="btn-large" | |
| > | |
| {busy ? "Saving…" : "Save result"} | |
| </Button> | |
| </div> | |
| </div> | |
| ) : ( | |
| <p class="muted">Nothing pending.</p> | |
| )} | |
| </Sheet> | |
| ); | |
| } | |