Spaces:
Runtime error
Runtime error
Download frontend/src/components/AnswerFeedback.tsx from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 4.48 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/52ec279e6108c8a40d96dbfbb4d5573a02472df3/frontend/src/components/AnswerFeedback.tsx
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test@52ec279e6108c8a40d96dbfbb4d5573a02472df3/frontend/src/components/AnswerFeedback.tsx
-
curl -L -o AnswerFeedback.tsx https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/52ec279e6108c8a40d96dbfbb4d5573a02472df3/frontend/src/components/AnswerFeedback.tsx
4.48 kB
| import { useState } from "react"; | |
| import { notebooksApi } from "../api/endpoints"; | |
| import type { AnswerFeedback as Feedback } from "../api/types"; | |
| const REASONS: { id: string; label: string }[] = [ | |
| { id: "forgot_chat", label: "Forgot something we said" }, | |
| { id: "mixed_chat", label: "Mixed up an earlier chat" }, | |
| { id: "wrong", label: "Wrong or made up" }, | |
| { id: "missed", label: "Missed something in my posts" }, | |
| { id: "bad_sources", label: "Bad sources" }, | |
| { id: "other", label: "Something else" }, | |
| ]; | |
| const Thumb = ({ down }: { down?: boolean }) => ( | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" style={down ? { transform: "rotate(180deg)" } : undefined}> | |
| <path d="M7 11v9H4a1 1 0 0 1-1-1v-7a1 1 0 0 1 1-1h3Zm0 0 4-8a2 2 0 0 1 2 2v4h5.2a2 2 0 0 1 2 2.3l-1 6A2 2 0 0 1 17.2 20H7" /> | |
| </svg> | |
| ); | |
| /** Thumbs up or down under an answer. A thumbs down opens a few reasons and an optional note. Saved as soon as it is chosen. */ | |
| export function AnswerFeedback({ messageId, initial }: { messageId: string; initial?: Feedback | null }) { | |
| const [fb, setFb] = useState<Feedback | null>(initial ?? null); | |
| const [open, setOpen] = useState(false); | |
| const [reasons, setReasons] = useState<string[]>(initial?.reasons ?? []); | |
| const [comment, setComment] = useState(initial?.comment ?? ""); | |
| const [error, setError] = useState(false); | |
| const [thanks, setThanks] = useState(false); | |
| const save = async (rating: "up" | "down" | null, r: string[] = [], c = "") => { | |
| const before = fb; | |
| setError(false); | |
| setFb(rating ? { rating, reasons: r, comment: c || null } : null); // shows at once; undone if the save fails | |
| try { | |
| const res = await notebooksApi.rate(messageId, { rating, reasons: r, comment: c || undefined }); | |
| setFb(res.feedback); | |
| } catch { | |
| setFb(before); | |
| setError(true); | |
| } | |
| }; | |
| const choose = (rating: "up" | "down") => { | |
| setThanks(false); | |
| if (fb?.rating === rating) { | |
| setOpen(false); | |
| void save(null); | |
| return; | |
| } | |
| setOpen(rating === "down"); | |
| if (rating === "up") { | |
| setReasons([]); | |
| setComment(""); | |
| } | |
| void save(rating, rating === "down" ? reasons : []); | |
| }; | |
| return ( | |
| <div className="fb"> | |
| <div className="fb-row"> | |
| <button type="button" className={`fb-btn${fb?.rating === "up" ? " is-on" : ""}`} aria-pressed={fb?.rating === "up"} aria-label="Good answer" title="Good answer" onClick={() => choose("up")}> | |
| <Thumb /> | |
| </button> | |
| <button type="button" className={`fb-btn${fb?.rating === "down" ? " is-on" : ""}`} aria-pressed={fb?.rating === "down"} aria-label="Bad answer" title="Bad answer" onClick={() => choose("down")}> | |
| <Thumb down /> | |
| </button> | |
| {thanks && <span className="mono muted small">Thanks, saved.</span>} | |
| {error && <span className="error-text small">Could not save that. Try again.</span>} | |
| </div> | |
| {open && fb?.rating === "down" && ( | |
| <div className="fb-why"> | |
| <p className="mono muted small">What went wrong? (optional)</p> | |
| <div className="fb-chips"> | |
| {REASONS.map((r) => ( | |
| <button | |
| key={r.id} | |
| type="button" | |
| className={`fb-chip${reasons.includes(r.id) ? " is-on" : ""}`} | |
| aria-pressed={reasons.includes(r.id)} | |
| onClick={() => setReasons((list) => (list.includes(r.id) ? list.filter((x) => x !== r.id) : [...list, r.id]))} | |
| > | |
| {r.label} | |
| </button> | |
| ))} | |
| </div> | |
| <textarea className="input fb-note" rows={2} maxLength={1000} value={comment} onChange={(e) => setComment(e.target.value)} placeholder="Tell us more, like what it should have said" aria-label="What went wrong" /> | |
| <div className="row end"> | |
| <button type="button" className="btn btn-small" onClick={() => setOpen(false)}> | |
| Skip | |
| </button> | |
| <button | |
| type="button" | |
| className="btn btn-small btn-primary" | |
| onClick={async () => { | |
| await save("down", reasons, comment.trim()); | |
| setOpen(false); | |
| setThanks(true); | |
| }} | |
| > | |
| Send | |
| </button> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |