Fixed react refresh bug and fixed white background when clicking on task which was a TASK_AUDIT hangover from react local storagedb now pointing too python fastapi

This commit is contained in:
NPS Agent
2026-05-11 14:04:13 +09:30
parent 3825c7556b
commit 49dc767922
10 changed files with 156 additions and 656 deletions
+8 -133
View File
@@ -106,15 +106,6 @@ function TopBar({ me, isAdmin, tab, setTab, onAdd, onLogs, onLogout, onProfile,
<Icon.Logs />
</IconBtn>
)}
{isAdmin && (
<IconBtn label="Database" onClick={onDB}>
<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.4">
<ellipse cx="8" cy="3.5" rx="5" ry="1.5"/>
<path d="M3 3.5v9c0 .8 2.2 1.5 5 1.5s5-.7 5-1.5v-9"/>
<path d="M3 7.5c0 .8 2.2 1.5 5 1.5s5-.7 5-1.5"/>
</svg>
</IconBtn>
)}
<button className="topbar__me" onClick={onProfile}>
<Avatar user={me} size={28} ring />
<div className="topbar__me-meta">
@@ -407,13 +398,14 @@ function Modal({ children, onClose, title, eyebrow, wide = false }) {
);
}
function TaskDetail({ task, onClose, onMove, onPriority }) {
function TaskDetail({ task, allAudit = [], onClose, onMove, onPriority }) {
if (!task) return null;
const assignee = findUser(task.assignee);
const author = findUser(task.addedBy);
const audit = TASK_AUDIT[task.id] || [
{ at: task.addedAt, actor: task.addedBy, action: 'created', detail: '' }
];
const audit = allAudit.filter(a => a.target === task.id);
if (audit.length === 0) {
audit.push({ at: task.addedAt, actor: task.addedBy, action: 'task_created', summary: '' });
}
return (
<Modal onClose={onClose} wide title={task.title} eyebrow={(task.tags && task.tags.join(' · ')) || 'Task'}>
<div className="detail">
@@ -474,9 +466,9 @@ function TaskDetail({ task, onClose, onMove, onPriority }) {
<strong>
{row.actor === 'system' ? 'System' : (findUser(row.actor) || {}).name}
</strong>
<span> {row.action}</span>
<span> {row.action.replace(/_/g, ' ')}</span>
</div>
{row.detail && <div className="timeline__detail">{row.detail}</div>}
{(row.detail || row.summary) && <div className="timeline__detail">{row.detail || row.summary}</div>}
<div className="timeline__time mono">{fmtDateTime(row.at)}</div>
</div>
</li>
@@ -818,8 +810,7 @@ function ToggleRow({ label, defaultOn = false }) {
);
}
function WorkspaceTab({ user, isAdmin, onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
const dbUsers = DashyDB.listUsers();
function WorkspaceTab({ user, isAdmin, dbUsers, onSwitchUser, onCreateUser, onDeleteUser, onUpdateUserRole }) {
const [adding, setAdding] = React.useState(false);
const [newName, setNewName] = React.useState('');
const [newRole, setNewRole] = React.useState('');
@@ -935,123 +926,7 @@ function WorkspaceTab({ user, isAdmin, onSwitchUser, onCreateUser, onDeleteUser,
);
}
function DatabaseInspector({ onClose }) {
const [tab, setTab] = React.useState(DashyDB.tableNames()[0] || 'tasks');
const [sql, setSql] = React.useState('SELECT id, title, assignee_id, priority, status\nFROM tasks\nORDER BY added_at DESC;');
const [result, setResult] = React.useState(null);
const [err, setErr] = React.useState(null);
const tableNames = DashyDB.tableNames();
const runQuery = () => {
try {
setErr(null);
const r = DashyDB.rawExec(sql);
setResult(r);
} catch (e) { setErr(String(e.message || e)); setResult(null); }
};
const downloadDB = () => {
const blob = DashyDB.exportFile();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'dashy.db'; a.click();
URL.revokeObjectURL(url);
};
const cols = tab && DashyDB.tableInfo(tab);
const rows = tab && DashyDB.query('SELECT * FROM ' + tab + ' LIMIT 200');
return (
<Modal onClose={onClose} title="Database" eyebrow="dashy.db · SQLite" wide>
<div className="dbins">
<nav className="dbins__nav">
<div className="dbins__nav-h mono">tables</div>
{tableNames.map(n => (
<button key={n} className={"dbins__tab" + (tab===n?' is-on':'')} onClick={() => setTab(n)}>
<span className="dbins__tab-name">{n}</span>
<span className="dbins__tab-count mono">{DashyDB.rowCount(n)}</span>
</button>
))}
<button className={"dbins__tab" + (tab==='__sql__'?' is-on':'')} onClick={() => setTab('__sql__')}>
<span className="dbins__tab-name">SQL </span>
</button>
<div className="dbins__nav-foot">
<button className="btn btn--soft btn--sm" onClick={downloadDB}>Export .db</button>
<button className="btn btn--ghost btn--sm" onClick={() => { if (confirm('Wipe and reseed?')) DashyDB.reset(); }}>Reset</button>
</div>
</nav>
<div className="dbins__body">
{tab !== '__sql__' && cols && (
<>
<div className="dbins__schema">
<div className="dbins__schema-h mono">schema · {tab}</div>
<div className="dbins__cols">
{cols.map(c => (
<span key={c.name} className="dbins__col">
<strong>{c.name}</strong>
<span className="mono">{c.type}{c.pk ? ' · PK' : ''}{c.notnull ? ' · NOT NULL' : ''}</span>
</span>
))}
</div>
</div>
<DBTable cols={cols.map(c => c.name)} rows={rows} />
</>
)}
{tab === '__sql__' && (
<div className="dbins__sql">
<div className="dbins__sql-bar">
<span className="mono dbins__sql-eyebrow">SQL · runs against dashy.db</span>
<button className="btn btn--primary btn--sm" onClick={runQuery}>Run ↵</button>
</div>
<textarea
className="dbins__sql-input"
value={sql}
onChange={e => setSql(e.target.value)}
spellCheck={false}
/>
{err && <div className="dbins__err">{err}</div>}
{result && result[0] && (
<DBTable cols={result[0].columns} rows={result[0].values.map(row => Object.fromEntries(row.map((v,i)=>[result[0].columns[i],v])))} />
)}
{result && result.length === 0 && (
<div className="dbins__ok mono">ok · no rows returned</div>
)}
</div>
)}
</div>
</div>
</Modal>
);
}
function DBTable({ cols, rows }) {
if (!rows || !rows.length) return <div className="dbins__empty mono">— empty —</div>;
return (
<div className="dbins__tablewrap">
<table className="dbins__table">
<thead><tr>{cols.map(c => <th key={c}>{c}</th>)}</tr></thead>
<tbody>
{rows.map((r, i) => (
<tr key={i}>
{cols.map(c => {
const v = r[c];
let display;
if (v === null || v === undefined) display = <span className="dbins__null mono">NULL</span>;
else if (typeof v === 'string' && v.length > 80) display = v.slice(0, 80) + '';
else display = String(v);
return <td key={c} className={typeof v === 'number' ? 'is-num' : ''}>{display}</td>;
})}
</tr>
))}
</tbody>
</table>
</div>
);
}
Object.assign(window, {
LoginScreen, TopBar, OverviewScreen, UserScreen, AddTaskModal, Modal, TaskDetail, AuditScreen, HeadsUp, BrandMark,
SettingsScreen,
DatabaseInspector,
});