1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { a as api } from "./index-D0Bmh-UX.js"; 3import { U as UserAvatar } from "./UserAvatar-BQN4jlY-.js"; 4import { u as usePlayer } from "./main-CznOH3wZ.js"; 5import { cq as Scale, bv as CircleCheckBig, ew as Vote, ex as Gavel, a$ as Shield, bL as Clock, cA as ChevronUp, cB as ChevronDown, bZ as Pause, bU as Play, bM as History } from "./vendor-dVNaLJx_.js"; 6import "./vendor-i18n-R-bMoYp_.js"; 7import "./vendor-react-three-MDLA5jog.js"; 8import "./utils-BYCVoSTN.js"; 9import "./synth-BE1TSTD4.js"; 10import "./wasm-synth-adapter-BrqgBX1y.js"; 11import "./vendor-spessasynth-DH8n6o_V.js"; 12import "./vendor-audio-DH3w0BzW.js"; 13import "./Waveform-MxtdejK7.js"; 14import "./vendor-framer-C8eEgkdL.js"; 15import "./labTools-BATZiaPB.js"; 16const STATUS_COLORS = { 17 ACTIVE: "bg-amber-100 text-amber-700 border-amber-200", 18 NOT_GUILTY: "bg-emerald-100 text-emerald-700 border-emerald-200", 19 SENTENCED: "bg-red-100 text-red-700 border-red-200", 20 EXPIRED: "bg-zinc-100 text-zinc-500 border-zinc-200" 21}; 22const METHOD_LABELS = { 23 AUTO_RATE_LIMIT: "â¡ Rate Limit Exceeded", 24 AUTO_LINK_SPAM: "ð Link Spam Detected", 25 AUTO_DUPLICATE: "ð Duplicate Content", 26 AUTO_BAD_WORD: "ð« Content Violation", 27 AUTO_LOW_TRUST: "ð Low Trust Score", 28 MANUAL_REPORT: "ð© Community Report", 29 AI_MISMATCH: "ð¤ AI Collection Match Dispute" 30}; 31const TimeRemaining = ({ expiresAt }) => { 32 const [remaining, setRemaining] = reactExports.useState(""); 33 reactExports.useEffect(() => { 34 const update = () => { 35 const diff = new Date(expiresAt).getTime() - Date.now(); 36 if (diff <= 0) { 37 setRemaining("Expired"); 38 return; 39 } 40 const d = Math.floor(diff / 864e5); 41 const h = Math.floor(diff % 864e5 / 36e5); 42 const m = Math.floor(diff % 36e5 / 6e4); 43 setRemaining(d > 0 ? `${d}d ${h}h` : h > 0 ? `${h}h ${m}m` : `${m}m`); 44 }; 45 update(); 46 const i = setInterval(update, 6e4); 47 return () => clearInterval(i); 48 }, [expiresAt]); 49 return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: remaining }); 50}; 51const VoteProgressBar = ({ guiltyWeight, notGuiltyWeight }) => { 52 const total = guiltyWeight + notGuiltyWeight; 53 const pct = total > 0 ? notGuiltyWeight / total * 100 : 50; 54 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [ 55 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between text-[11px] font-bold", children: [
56 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-500", children: [ 57 "Not Guilty (", 58 notGuiltyWeight.toFixed(1), 59 ")" 60 ] }), 61 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-red-500", children: [ 62 "Guilty (", 63 guiltyWeight.toFixed(1), 64 ")" 65 ] }) 66 ] }), 67 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-2.5 bg-red-200 dark:bg-red-900/30 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-full bg-emerald-500 rounded-full transition-all duration-500", style: { width: `${pct}%` } }) }) 68 ] }); 69}; 70const CaseCard = ({ jailCase, user, onVoted }) => { 71 var _a, _b; 72 const [expanded, setExpanded] = reactExports.useState(false); 73 const [detail, setDetail] = reactExports.useState(null); 74 const [voting, setVoting] = reactExports.useState(false); 75 const [selectedVerdict, setSelectedVerdict] = reactExports.useState(null); 76 const [selectedSentence, setSelectedSentence] = reactExports.useState(""); 77 const [reason, setReason] = reactExports.useState(""); 78 const { activeTrack, isPlaying, setIsPlaying, playTrack } = usePlayer(); 79 const isSongBindingCase = Boolean(jailCase.evidence && typeof jailCase.evidence === "object" && jailCase.evidence.caseType === "song_midi_binding"); 80 const evidenceData = jailCase.evidence || {}; 81 const isPlayingThisMidi = isSongBindingCase && (activeTrack == null ? void 0 : activeTrack.id) === Number(evidenceData == null ? void 0 : evidenceData.midiId) && isPlaying; 82 const handleAudition = () => { 83 if (!(evidenceData == null ? void 0 : evidenceData.midiId)) return; 84 const miniTrack = { 85 id: Number(evidenceData.midiId), 86 title: evidenceData.midiTitle || "MIDI Preview", 87 artist: evidenceData.midiArtist || "" 88 }; 89 if (isPlayingThisMidi) { 90 setIsPlaying(false); 91 } else { 92 playTrack(miniTrack, [miniTrack]); 93 } 94 }; 95 const myVote = (_a = jailCase.votes) == null ? void 0 : _a.find((v) => v.userId === (user == null ? void 0 : user.id)); 96 const canVote = user && user.id !== -1 && (user.trustScore || 0) >= 60 && jailCase.status === "ACTIVE" && jailCase.userId !== (user == null ? void 0 : user.id); 97 const loadDetail = async () => { 98 if (!expanded && !detail) { 99 const d = await api.getTribunalCase(jailCase.id); 100 setDetail(d); 101 } 102 setExpanded(!expanded); 103 }; 104 const submitVote = async () => { 105 var _a2; 106 if (!selectedVerdict) return; 107 let finalSentence = selectedSentence; 108 if (selectedVerdict === "GUILTY" && !finalSentence) { 109 if (isSongBindingCase) { 110 finalSentence = ((_a2 = sentences[0]) == null ? void 0 : _a2.id) || "warning"; 111 } else { 112 alert("Please select a sentence."); 113 return; 114 } 115 } 116 setVoting(true); 117 try { 118 await api.voteTribunalCase(jailCase.id, selectedVerdict, selectedVerdict === "GUILTY" ? finalSentence : void 0, reason || void 0); 119 setSelectedVerdict(null); 120 onVoted(); 121 } catch (e) { 122 alert(e.message || "Vote failed"); 123 } finally { 124 setVoting(false); 125 } 126 }; 127 const defendant = jailCase.user; 128 const sentences = (detail == null ? void 0 : detail.sentences) || []; 129 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-shadow", children: [ 130 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-4", children: [
131 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-3", children: [ 132 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 133 /* @__PURE__ */ jsxRuntimeExports.jsx(UserAvatar, { src: defendant.avatar, name: defendant.username, size: 40, className: "border-2 border-zinc-300 dark:border-zinc-700" }), 134 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 135 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-bold text-sm text-zinc-900 dark:text-white", children: defendant.username }), 136 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-[11px] text-zinc-500", children: [ 137 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 138 "Trust: ", 139 defendant.trustScore 140 ] }), 141 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "·" }), 142 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 143 "Offenses: ", 144 defendant.jailCount 145 ] }),
146 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "·" }), 147 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [ 148 "Joined ", 149 new Date(defendant.createdAt).toLocaleDateString() 150 ] }) 151 ] }) 152 ] }) 153 ] }), 154 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [ 155 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold border ${STATUS_COLORS[jailCase.status] || "bg-zinc-100 text-zinc-500"}`, children: jailCase.status }), 156 jailCase.status === "ACTIVE" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] text-zinc-400 flex items-center gap-1", children: [ 157 /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { size: 11 }), 158 /* @__PURE__ */ jsxRuntimeExports.jsx(TimeRemaining, { expiresAt: jailCase.expiresAt }) 159 ] }) 160 ] }) 161 ] }),
162 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 bg-amber-50 dark:bg-amber-900/10 border border-amber-100 dark:border-amber-900/20 rounded-lg p-2.5", children: [ 163 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] font-bold text-amber-600 dark:text-amber-400 mb-0.5", children: METHOD_LABELS[jailCase.detectionMethod] || jailCase.detectionMethod }), 164 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-700 dark:text-zinc-300", children: jailCase.reason }) 165 ] }), 166 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(VoteProgressBar, { guiltyWeight: jailCase.guiltyWeight, notGuiltyWeight: jailCase.notGuiltyWeight }) }), 167 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 flex items-center justify-between", children: [ 168 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[11px] text-zinc-400", children: [ 169 ((_b = jailCase.votes) == null ? void 0 : _b.length) || 0, 170 " vote(s) cast" 171 ] }),
172 /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: loadDetail, className: "text-xs text-indigo-500 font-bold flex items-center gap-1 hover:underline", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 173 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { size: 12 }), 174 " Collapse" 175 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 176 /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { size: 12 }), 177 " Details & Vote" 178 ] }) }) 179 ] }) 180 ] }), 181 expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t border-zinc-100 dark:border-zinc-800 p-4 bg-zinc-50 dark:bg-zinc-950 space-y-4", children: [ 182 jailCase.evidence && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 183 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-2", children: "Evidence" }), 184 isSongBindingCase ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl p-3.5 space-y-3", children: [
185 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [ 186 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-black uppercase tracking-wider text-indigo-600 dark:text-indigo-400 bg-indigo-50 dark:bg-indigo-950/40 px-2 py-0.5 rounded border border-indigo-200 dark:border-indigo-800", children: "Collection Song Binding Dispute" }), 187 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-medium text-zinc-400", children: evidenceData.bindingOrigin === "USER_UPLOAD" ? "User-Contributed MIDI" : "AI-Curated Binding" }) 188 ] }), 189 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3 bg-zinc-50 dark:bg-zinc-950 p-3 rounded-lg border border-zinc-100 dark:border-zinc-800/80 text-xs", children: [ 190 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 191 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase font-bold text-zinc-400 block mb-0.5", children: "Target Song" }),
192 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-white font-bold", children: evidenceData.songTitle || "Untitled Song" }) 193 ] }), 194 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 195 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] uppercase font-bold text-zinc-400 block mb-0.5", children: "Bound MIDI File" }), 196 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-900 dark:text-white font-bold", children: evidenceData.midiTitle || `Track #${evidenceData.midiId}` }), 197 evidenceData.midiArtist && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-400 block", children: [ 198 "by ", 199 evidenceData.midiArtist 200 ] }) 201 ] }) 202 ] }), 203 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between p-2.5 bg-indigo-50/50 dark:bg-indigo-950/20 rounded-lg border border-indigo-100 dark:border-indigo-900/30", children: [
204 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [ 205 /* @__PURE__ */ jsxRuntimeExports.jsx( 206 "button", 207 { 208 type: "button", 209 onClick: handleAudition, 210 className: `w-8 h-8 rounded-full flex items-center justify-center transition shadow-xs ${isPlayingThisMidi ? "bg-indigo-600 text-white animate-pulse" : "bg-indigo-600 text-white hover:bg-indigo-700"}`, 211 children: isPlayingThisMidi ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { size: 14, className: "fill-current" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { size: 14, className: "fill-current ml-0.5" }) 212 } 213 ), 214 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 215 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-bold text-zinc-900 dark:text-white", children: "Audition MIDI Track" }), 216 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-zinc-500", children: "Listen to verify if this matches the song" }) 217 ] }) 218 ] }),
219 /* @__PURE__ */ jsxRuntimeExports.jsx( 220 "a", 221 { 222 href: `/track/${evidenceData.midiId}`, 223 target: "_blank", 224 rel: "noreferrer", 225 className: "text-[11px] font-bold text-indigo-600 dark:text-indigo-400 hover:underline", 226 children: "Open Track â" 227 } 228 ) 229 ] }), 230 (evidenceData.reportReason || evidenceData.reportDetails) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-zinc-600 dark:text-zinc-400 bg-zinc-100 dark:bg-zinc-800/60 p-2.5 rounded-lg", children: [ 231 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-bold text-zinc-700 dark:text-zinc-300", children: "Report details: " }), 232 evidenceData.reportReason, 233 " ", 234 evidenceData.reportDetails ? `â "${evidenceData.reportDetails}"` : "" 235 ] }) 236 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "text-xs bg-zinc-100 dark:bg-zinc-900 p-3 rounded-lg overflow-x-auto max-h-40 text-zinc-700 dark:text-zinc-300", children: JSON.stringify(jailCase.evidence, null, 2) }) 237 ] }), 238 (detail == null ? void 0 : detail.pastCases) && detail.pastCases.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
239 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-2 flex items-center gap-1", children: [ 240 /* @__PURE__ */ jsxRuntimeExports.jsx(History, { size: 12 }), 241 " Prior Record (", 242 detail.pastCases.length, 243 " case", 244 detail.pastCases.length !== 1 ? "s" : "", 245 ")" 246 ] }), 247 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1.5", children: detail.pastCases.map((pc) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-[11px] bg-zinc-100 dark:bg-zinc-900 px-3 py-1.5 rounded border border-zinc-200 dark:border-zinc-800", children: [ 248 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 249 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-1.5 py-0.5 rounded text-[9px] font-bold border ${STATUS_COLORS[pc.status] || ""}`, children: pc.status }), 250 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-600 dark:text-zinc-400", children: [ 251 pc.reason.substring(0, 60), 252 pc.reason.length > 60 ? "..." : "" 253 ] }) 254 ] }),
255 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400", children: new Date(pc.createdAt).toLocaleDateString() }) 256 ] }, pc.id)) }) 257 ] }), 258 canVote && !myVote && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-indigo-200 dark:border-indigo-800/30 rounded-lg p-4", children: [ 259 /* @__PURE__ */ jsxRuntimeExports.jsxs("h4", { className: "text-xs font-bold text-indigo-600 dark:text-indigo-400 uppercase mb-3 flex items-center gap-1", children: [ 260 /* @__PURE__ */ jsxRuntimeExports.jsx(Gavel, { size: 12 }), 261 " Cast Your Vote" 262 ] }), 263 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-[11px] text-zinc-500 mb-3", children: [ 264 "Your vote weight: ", 265 /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { className: "text-zinc-700 dark:text-zinc-300", children: Math.max(0.6, Math.min(1, (user.trustScore || 0) / 100)).toFixed(2) }) 266 ] }), 267 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 mb-3", children: [
268 /* @__PURE__ */ jsxRuntimeExports.jsx( 269 "button", 270 { 271 onClick: () => { 272 setSelectedVerdict("NOT_GUILTY"); 273 setSelectedSentence(""); 274 }, 275 className: `flex-1 py-2 rounded-lg text-sm font-bold border-2 transition-all ${selectedVerdict === "NOT_GUILTY" ? "bg-emerald-500 text-white border-emerald-500" : "border-emerald-200 dark:border-emerald-800 text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-900/20"}`, 276 children: isSongBindingCase ? "â Accurate Match (Keep Bound)" : "â Not Guilty" 277 } 278 ), 279 /* @__PURE__ */ jsxRuntimeExports.jsx( 280 "button", 281 { 282 onClick: () => { 283 var _a2; 284 setSelectedVerdict("GUILTY"); 285 if (isSongBindingCase && !selectedSentence) { 286 setSelectedSentence(((_a2 = sentences[0]) == null ? void 0 : _a2.id) || "warning"); 287 } 288 }, 289 className: `flex-1 py-2 rounded-lg text-sm font-bold border-2 transition-all ${selectedVerdict === "GUILTY" ? "bg-red-500 text-white border-red-500" : "border-red-200 dark:border-red-800 text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20"}`, 290 children: isSongBindingCase ? "â Mismatch / Irrelevant (Unbind)" : "â Guilty" 291 } 292 ) 293 ] }), 294 selectedVerdict === "GUILTY" && !isSongBindingCase && sentences.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3", children: [
295 /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-[11px] font-bold text-zinc-500 mb-1", children: "Select Sentence" }), 296 /* @__PURE__ */ jsxRuntimeExports.jsxs( 297 "select", 298 { 299 value: selectedSentence, 300 onChange: (e) => setSelectedSentence(e.target.value), 301 className: "w-full bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-2 text-sm text-black dark:text-white", 302 children: [ 303 /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "", children: "â Choose a sentence â" }), 304 sentences.sort((a, b) => a.severity - b.severity).map((s) => /* @__PURE__ */ jsxRuntimeExports.jsxs("option", { value: s.id, children: [ 305 s.label, 306 " â ", 307 s.effectType === "ban" ? "â ï¸ Permanent" : s.effectType === "suspend" ? `⸠${s.effectValue}d` : `ð Trust â${s.effectValue}` 308 ] }, s.id)) 309 ] 310 } 311 ) 312 ] }),
313 /* @__PURE__ */ jsxRuntimeExports.jsx( 314 "textarea", 315 { 316 placeholder: "Optional: reason for your vote...", 317 value: reason, 318 onChange: (e) => setReason(e.target.value), 319 className: "w-full bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-lg p-2 text-xs text-black dark:text-white resize-none h-16 mb-3" 320 } 321 ), 322 /* @__PURE__ */ jsxRuntimeExports.jsx( 323 "button", 324 { 325 onClick: submitVote, 326 disabled: voting || !selectedVerdict, 327 className: "w-full py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg text-sm font-bold disabled:opacity-50 transition-colors", 328 children: voting ? "Submitting..." : "Submit Vote" 329 } 330 ) 331 ] }), 332 myVote && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-indigo-50 dark:bg-indigo-900/10 border border-indigo-200 dark:border-indigo-800/30 rounded-lg p-3 text-center", children: [
333 /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm font-bold text-indigo-600 dark:text-indigo-400", children: [ 334 "You voted: ", 335 myVote.verdict === "GUILTY" ? isSongBindingCase ? "â Mismatch / Irrelevant" : "â Guilty" : isSongBindingCase ? "â Accurate Match" : "â Not Guilty" 336 ] }), 337 myVote.sentenceId && !isSongBindingCase && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-zinc-500 mt-1", children: [ 338 "Sentence: ", 339 myVote.sentenceId 340 ] }) 341 ] }), 342 !canVote && !myVote && jailCase.status === "ACTIVE" && (user == null ? void 0 : user.id) !== -1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center text-xs text-zinc-400 py-2", children: (user == null ? void 0 : user.id) === jailCase.userId ? "You cannot vote on your own case." : `Trust score ⥠60 required to vote (yours: ${(user == null ? void 0 : user.trustScore) || 0})` }), 343 jailCase.votes && jailCase.votes.length > 0 && /* @__P
343URE__ */ jsxRuntimeExports.jsxs("div", { children: [ 344 /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-bold text-zinc-500 uppercase mb-2", children: "Vote Breakdown" }), 345 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: jailCase.votes.map((v) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between text-[11px] bg-zinc-100 dark:bg-zinc-900 px-3 py-1.5 rounded", children: [ 346 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [ 347 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `font-bold ${v.verdict === "GUILTY" ? "text-red-500" : "text-emerald-500"}`, children: v.verdict === "GUILTY" ? "â" : "â" }), 348 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-600 dark:text-zinc-400", children: v.user.username }), 349 /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-zinc-400", children: [ 350 "(Ã", 351 v.weight.toFixed(2), 352 ")" 353 ] }) 354 ] }),
355 v.reason && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-zinc-400 italic max-w-[200px] truncate", children: v.reason }) 356 ] }, v.id)) }) 357 ] }) 358 ] }) 359 ] }); 360}; 361const TribunalPage = () => { 362 const [user, setUser] = reactExports.useState(null); 363 const [tab, setTab] = reactExports.useState("active"); 364 const [cases, setCases] = reactExports.useState([]); 365 const [myVotes, setMyVotes] = reactExports.useState([]); 366 const [loading, setLoading] = reactExports.useState(true); 367 reactExports.useEffect(() => { 368 try { 369 const session = localStorage.getItem("hamie_session"); 370 if (session) setUser(JSON.parse(session)); 371 } catch { 372 } 373 }, []); 374 const loadCases = async () => { 375 setLoading(true); 376 try { 377 if (tab === "active") { 378 setCases(await api.getTribunalCases("ACTIVE")); 379 } else if (tab === "resolved") { 380 const [ng, s, e] = await Promise.all([ 381 api.getTribunalCases("NOT_GUILTY"), 382 api.getTribunalCases("SENTENCED"), 383 api.getTribunalCases("EXPIRED") 384 ]); 385 setCases([...s, ...ng, ...e].sort((a, b) => new Date(b.resolvedAt || b.createdAt).getTime() - new Date(a.resolvedAt || a.createdAt).getTime())); 386 } else { 387 setMyVotes(await api.getMyTribunalVotes()); 388 } 389 } catch { 390 } finally { 391 setLoading(false); 392 } 393 }; 394 reactExports.useEffect(() => { 395 loadCases(); 396 }, [tab]); 397 const tabs = [ 398 { id: "active", label: "Active Cases", icon: Scale }, 399 { id: "resolved", label: "Resolved", icon: CircleCheckBig }, 400 { id: "my-votes", label: "My Votes", icon: Vote } 401 ]; 402 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-6 max-w-[900px] mx-auto animate-in fade-in", children: [ 403 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-8", children: [
404 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-center gap-3 mb-3", children: [ 405 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 rounded-xl bg-gradient-to-br from-amber-500 to-orange-600 flex items-center justify-center shadow-lg shadow-amber-500/20", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Scale, { className: "text-white", size: 24 }) }), 406 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-left", children: [ 407 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [ 408 /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { className: "text-2xl font-black text-zinc-900 dark:text-white", children: "Hamie Tribunal" }), 409 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-amber-500/10 text-amber-500 border border-amber-500/30", children: "beta" }) 410 ] }),
411 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Community-powered justice for a safer archive" }) 412 ] }) 413 ] }), 414 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-zinc-400 max-w-md mx-auto", children: "Accounts flagged for spam or abuse are reviewed here. Trusted members vote on the verdict and sentence." }) 415 ] }), 416 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex gap-1 bg-zinc-100 dark:bg-zinc-800 rounded-xl p-1 mb-6", children: tabs.map((t) => /* @__PURE__ */ jsxRuntimeExports.jsxs( 417 "button", 418 { 419 onClick: () => setTab(t.id), 420 className: `flex-1 flex items-center justify-center gap-1.5 px-3 py-2 rounded-lg text-sm font-bold transition-all ${tab === t.id ? "bg-white dark:bg-zinc-700 text-amber-600 dark:text-amber-400 shadow-sm" : "text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300"}`, 421 children: [ 422 /* @__PURE__ */ jsxRuntimeExports.jsx(t.icon, { size: 15 }), 423 " ", 424 t.label 425 ] 426 }, 427 t.id 428 )) }),
429 loading ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center py-16 text-zinc-400", children: "Loading cases..." }) : tab === "my-votes" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: myVotes.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-16 text-zinc-400", children: [ 430 /* @__PURE__ */ jsxRuntimeExports.jsx(Vote, { size: 32, className: "mx-auto mb-2 opacity-50" }), 431 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "You haven't voted on any cases yet." }) 432 ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ 433 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-center mb-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-2 px-4 py-2 bg-indigo-50 dark:bg-indigo-900/20 border border-indigo-200 dark:border-indigo-800/30 rounded-full text-sm font-bold text-indigo-600 dark:text-indigo-400", children: [
434 /* @__PURE__ */ jsxRuntimeExports.jsx(Gavel, { size: 14 }), 435 " Justice Served: ", 436 myVotes.length, 437 " case", 438 myVotes.length !== 1 ? "s" : "" 439 ] }) }), 440 myVotes.map((v) => { 441 var _a, _b, _c, _d, _e, _f; 442 return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg p-3 flex items-center justify-between", children: [ 443 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [ 444 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `text-lg ${v.verdict === "GUILTY" ? "text-red-500" : "text-emerald-500"}`, children: v.verdict === "GUILTY" ? "â" : "â" }), 445 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [ 446 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-bold text-zinc-800 dark:text-zinc-200", children: ((_b = (_a = v.case) == null ? void 0 : _a.user) == null ? void 0 : _b.username) || "Unknown" }),
447 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] text-zinc-500", children: ((_d = (_c = v.case) == null ? void 0 : _c.reason) == null ? void 0 : _d.substring(0, 80)) || "No reason" }) 448 ] }) 449 ] }), 450 /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-right", children: [ 451 /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `px-2 py-0.5 rounded text-[10px] font-bold border ${STATUS_COLORS[((_e = v.case) == null ? void 0 : _e.status) || ""] || "bg-zinc-100 text-zinc-400"}`, children: (_f = v.case) == null ? void 0 : _f.status }), 452 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-zinc-400 mt-1", children: new Date(v.createdAt).toLocaleDateString() }) 453 ] }) 454 ] }, v.id); 455 }) 456 ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3", children: cases.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-16 text-zinc-400", children: [
457 /* @__PURE__ */ jsxRuntimeExports.jsx(Shield, { size: 32, className: "mx-auto mb-2 opacity-50" }), 458 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-bold", children: tab === "active" ? "No active cases" : "No resolved cases" }), 459 /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs mt-1", children: tab === "active" ? "The community is clean! ð" : "Cases will appear here once resolved." }) 460 ] }) : cases.map((c) => /* @__PURE__ */ jsxRuntimeExports.jsx(CaseCard, { jailCase: c, user, onVoted: loadCases }, c.id)) }) 461 ] }); 462}; 463export { 464 TribunalPage as default 465};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.