fix(v2): all reported issues — missing windows, broken features, layouts
Missing features (now added): 1. Vital Sharing window — polls /vital-sharing/peers, shows peer vitals with HP/STA/MANA bars, connection status, position, tags 2. Combat Stats window — full Mag-Tools style with monster list (left), damage breakdown grid (right), session/lifetime toggle, element matrix 3. Issues Board window — CRUD with categories, resolve/reopen, comments 4. Quest Status — links to /quest-status.html (separate page like v1) 5. Sidebar: added Issues + Vitals buttons, Quest link, Combat button per player row (6 buttons now: Chat/Stats/Inv/Char/Combat/Radar) Fixed functionality: 6. Radar — fixed command to "start_radar"/"stop_radar" (was wrong path) 7. Character window — redesigned with v1-style tabbed layout: Left tabs: Attributes (vitals bars + attribute grid) | Skills (specialized/trained grouped) | Titles Right tabs: Augs | Ratings | Other (allegiance) Header: level, race, gender, XP, luminance, deaths, skill credits 8. Stats window — proper Grafana iframe grid (4 panels 2x2) with time range selector (1H/6H/24H/7D) Color palette: expanded to 60 distinct colors (was 30) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b77450b6eb
commit
52e1bcd6b8
12 changed files with 710 additions and 226 deletions
94
frontend/src/components/windows/IssuesWindow.tsx
Normal file
94
frontend/src/components/windows/IssuesWindow.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { DraggableWindow } from './DraggableWindow';
|
||||
import { apiFetch } from '../../api/client';
|
||||
|
||||
interface Issue {
|
||||
id: number; title: string; description: string; category: string;
|
||||
created: string; resolved: boolean; author: string;
|
||||
comments?: Array<{ id: number; text: string; author: string; created: string }>;
|
||||
}
|
||||
|
||||
interface Props { id: string; zIndex: number; }
|
||||
|
||||
const CAT_COLORS: Record<string, string> = {
|
||||
plugin: '#4488ff', overlord: '#44cc44', nav: '#ffaa00', macro: '#cc44cc', other: '#888',
|
||||
};
|
||||
|
||||
export const IssuesWindow: React.FC<Props> = ({ id, zIndex }) => {
|
||||
const [issues, setIssues] = useState<Issue[]>([]);
|
||||
const [title, setTitle] = useState('');
|
||||
const [desc, setDesc] = useState('');
|
||||
const [category, setCategory] = useState('plugin');
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
const data = await apiFetch<{ issues: Issue[] }>('/issues');
|
||||
setIssues((data.issues ?? []).sort((a, b) => (a.resolved ? 1 : 0) - (b.resolved ? 1 : 0)));
|
||||
} catch { /* ignore */ }
|
||||
}, []);
|
||||
|
||||
useEffect(() => { refresh(); }, [refresh]);
|
||||
|
||||
const addIssue = async () => {
|
||||
if (!title.trim()) return;
|
||||
await fetch('/api/issues', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
|
||||
body: JSON.stringify({ title: title.trim(), description: desc.trim(), category }) });
|
||||
setTitle(''); setDesc('');
|
||||
refresh();
|
||||
};
|
||||
|
||||
const toggleResolve = async (issue: Issue) => {
|
||||
await fetch(`/api/issues/${issue.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, credentials: 'include',
|
||||
body: JSON.stringify({ resolved: !issue.resolved }) });
|
||||
refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<DraggableWindow id={id} title="Issues Board" zIndex={zIndex} width={540} height={520}>
|
||||
{/* Issue list */}
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: 6, fontSize: '0.75rem' }}>
|
||||
{issues.length === 0 ? (
|
||||
<div style={{ padding: 12, color: '#666', textAlign: 'center' }}>No issues</div>
|
||||
) : issues.map(issue => (
|
||||
<div key={issue.id} style={{ padding: '6px 8px', marginBottom: 4, background: '#1f1f1f', borderRadius: 3,
|
||||
border: '1px solid #333', opacity: issue.resolved ? 0.5 : 1 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ fontSize: '0.6rem', padding: '1px 6px', borderRadius: 3,
|
||||
background: CAT_COLORS[issue.category] ?? '#888', color: '#111', fontWeight: 600 }}>
|
||||
{issue.category}
|
||||
</span>
|
||||
<span style={{ flex: 1, fontWeight: 500 }}>{issue.title}</span>
|
||||
<button onClick={() => toggleResolve(issue)} style={{ fontSize: '0.65rem', padding: '1px 6px',
|
||||
background: issue.resolved ? '#333' : 'rgba(68,204,68,0.15)', color: issue.resolved ? '#888' : '#4c4',
|
||||
border: '1px solid #444', borderRadius: 3, cursor: 'pointer' }}>
|
||||
{issue.resolved ? '↻ Reopen' : '✓ Resolve'}
|
||||
</button>
|
||||
</div>
|
||||
{issue.description && <div style={{ color: '#888', marginTop: 3, fontSize: '0.7rem' }}>{issue.description}</div>}
|
||||
<div style={{ color: '#555', fontSize: '0.6rem', marginTop: 2 }}>
|
||||
by {issue.author} · {new Date(issue.created).toLocaleDateString()}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Add issue form */}
|
||||
<div style={{ padding: 6, borderTop: '1px solid #333', display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
<input value={title} onChange={e => setTitle(e.target.value)} placeholder="Issue title..."
|
||||
style={{ flex: 1, padding: '3px 6px', fontSize: '0.75rem', background: '#222', color: '#eee', border: '1px solid #444', borderRadius: 3 }} />
|
||||
<select value={category} onChange={e => setCategory(e.target.value)}
|
||||
style={{ padding: '3px 4px', fontSize: '0.7rem', background: '#222', color: '#eee', border: '1px solid #444', borderRadius: 3 }}>
|
||||
<option value="plugin">Plugin</option><option value="overlord">Overlord</option>
|
||||
<option value="nav">Nav</option><option value="macro">Macro</option><option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
<textarea value={desc} onChange={e => setDesc(e.target.value)} placeholder="Description..."
|
||||
rows={2} style={{ flex: 1, padding: '3px 6px', fontSize: '0.7rem', background: '#222', color: '#eee', border: '1px solid #444', borderRadius: 3, resize: 'vertical' }} />
|
||||
<button onClick={addIssue} style={{ padding: '4px 12px', background: 'rgba(68,136,255,0.15)', color: '#6aadff',
|
||||
border: '1px solid rgba(68,136,255,0.3)', borderRadius: 3, cursor: 'pointer', alignSelf: 'flex-end', fontSize: '0.7rem' }}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue