MosswartOverlord/static/assets/QuestStatusWindow-DL34Ths2.js
Erik 1c1c43d28b feat(dashboard): logout button + admin user-management window
Logout: new sidebar link 'Log out (username)' that POSTs /api/logout
(clears session cookie) and navigates to /login. Visible to everyone.
Replaces 'no logout functionality' state where users could only get
out by deleting cookies manually.

Admin window: new 'Admin · Users' window (only shown when current
user.is_admin) lists all users in a table with:
  - Add user (username + password + admin checkbox)
  - Reset password inline per row
  - Toggle admin per row
  - Delete user per row (blocked for self)
Wraps the existing /api-admin/users CRUD endpoints in main.py.

Plumbing: useCurrentUser hook fetches /me on mount; apiPatch+apiDelete
helpers added to api/client.ts; new endpoint wrappers exported from
api/endpoints.ts; AdminUsersWindow.tsx registered in WindowRenderer
under id prefix 'adminusers'; CSS for admin table/form/buttons and
the muted-red logout link.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 20:10:10 +02:00

1 line
2 KiB
JavaScript

import{r as c,j as t,D as u,a as f}from"./index-3SHiQu4l.js";import"./react-yfL0ty4i.js";const j=({id:p,zIndex:x})=>{const[s,h]=c.useState(null);c.useEffect(()=>{const e=async()=>{try{h(await f("/quest-status"))}catch{}};e();const o=setInterval(e,3e4);return()=>clearInterval(o)},[]);const i=s?Object.keys(s.quest_data).sort():[],l=new Set;if(s)for(const e of Object.values(s.quest_data))for(const o of Object.keys(e))l.add(o);const n=Array.from(l).sort();return t.jsx(u,{id:p,title:"Quest Status",zIndex:x,width:780,height:500,children:t.jsx("div",{style:{flex:1,overflow:"auto",fontSize:"0.72rem"},children:s?i.length===0?t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"No quest data available"}):t.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t.jsx("thead",{children:t.jsxs("tr",{style:{position:"sticky",top:0,background:"#1a1a1a",zIndex:1},children:[t.jsx("th",{style:{textAlign:"left",padding:"4px 8px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.65rem",fontWeight:600,minWidth:140},children:"Character"}),n.map(e=>t.jsx("th",{style:{textAlign:"center",padding:"4px 6px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.6rem",fontWeight:600,maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:e,children:e.replace(" Timer","").replace(" Pickup","")},e))]})}),t.jsx("tbody",{children:i.map(e=>{const o=s.quest_data[e]||{};return t.jsxs("tr",{style:{borderBottom:"1px solid #222"},children:[t.jsx("td",{style:{padding:"3px 8px",color:"#ccc",fontWeight:500,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",maxWidth:160},children:e}),n.map(d=>{const r=o[d],a=r==="READY";return t.jsx("td",{style:{textAlign:"center",padding:"3px 6px",color:a?"#4c4":r?"#ca0":"#333",fontWeight:a?600:400,fontSize:a?"0.7rem":"0.68rem"},children:r||"—"},d)})]},e)})})]}):t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"Loading quest data..."})})})};export{j as QuestStatusWindow};