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:
+8
-133
@@ -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,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user