diff --git a/app.jsx b/app.jsx index 531c407..5ea70ab 100644 --- a/app.jsx +++ b/app.jsx @@ -103,7 +103,8 @@ function App() { React.useEffect(() => { window.dbUsers = dbUsers; - }, [dbUsers]); + window.workspace = workspace; + }, [dbUsers, workspace]); React.useEffect(() => { const handleKeyDown = (e) => { @@ -172,15 +173,10 @@ function App() { const addTask = async ({ title, description, assignee, priority }) => { try { - const t = await api.createTask({ + await api.createTask({ title, description, assignee_id: assignee, priority, added_by: meId, source: 'manual', status: 'open', tags: [] }); - await api.addAudit({ - actor: meId, action: 'task_created', - summary: 'Created task "' + title + '" for ' + (merge(assignee)||{}).name, - target: t.id - }); setAdding(null); } catch(e) { console.error(e); diff --git a/backend/main.py b/backend/main.py index 57838e6..4602a79 100644 --- a/backend/main.py +++ b/backend/main.py @@ -201,6 +201,24 @@ async def create_task(task: schemas.TaskCreate, db: Session = Depends(get_db), c db.commit() db.refresh(db_task) + + # Create audit log entry + assignee_name = "Unassigned" + if db_task.assignee_id: + assignee = db.query(models.User).filter(models.User.id == db_task.assignee_id).first() + if assignee: + assignee_name = assignee.name + + db_audit = models.AuditLog( + id=f"al_{uuid.uuid4().hex[:8]}", + actor=current_user.id, + action='task_created', + summary=f'Created task "{db_task.title}" for {assignee_name}', + target=db_task.id + ) + db.add(db_audit) + db.commit() + await manager.broadcast(json.dumps({"type": "refresh"})) return db_task diff --git a/components.jsx b/components.jsx index 83eb74a..c7a2b0a 100644 --- a/components.jsx +++ b/components.jsx @@ -74,21 +74,36 @@ function SourceTag({ source }) { ); } +function getSafeTimezone() { + const tz = window.workspace ? window.workspace.timezone : undefined; + if (!tz) return undefined; + try { + Intl.DateTimeFormat(undefined, { timeZone: tz }); + return tz; + } catch (e) { + return undefined; // Fallback to browser time if invalid + } +} + function relTime(iso) { - const now = new Date('2026-05-08T10:30:00'); + if (typeof iso === 'string' && !iso.endsWith('Z') && !iso.includes('+')) iso += 'Z'; + const now = new Date(); const then = new Date(iso); const diff = (now - then) / 1000; if (diff < 60) return 'just now'; if (diff < 3600) return Math.floor(diff/60) + 'm ago'; if (diff < 86400) return Math.floor(diff/3600) + 'h ago'; if (diff < 86400*7) return Math.floor(diff/86400) + 'd ago'; - return then.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + + return then.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: getSafeTimezone() }); } function fmtDateTime(iso) { + if (typeof iso === 'string' && !iso.endsWith('Z') && !iso.includes('+')) iso += 'Z'; const d = new Date(iso); - return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + - ' · ' + d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); + const tz = getSafeTimezone(); + return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: tz }) + + ' · ' + d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', timeZone: tz }); } function findUser(id) { diff --git a/dashy.db b/dashy.db index f84c9e8..d95039b 100644 Binary files a/dashy.db and b/dashy.db differ diff --git a/screens.jsx b/screens.jsx index 86f17a0..c827cb9 100644 --- a/screens.jsx +++ b/screens.jsx @@ -917,7 +917,13 @@ function AuditScreen({ entries, onOpen }) { const actor = r.actor === 'system' ? null : findUser(r.actor); return (
  • - {new Date(r.at).toLocaleTimeString('en-US',{hour:'numeric', minute:'2-digit'})} + {(() => { + let safeTz = undefined; + if (window.workspace && window.workspace.timezone) { + try { Intl.DateTimeFormat(undefined, { timeZone: window.workspace.timezone }); safeTz = window.workspace.timezone; } catch(e) {} + } + return new Date(typeof r.at === 'string' && !r.at.endsWith('Z') && !r.at.includes('+') ? r.at + 'Z' : r.at).toLocaleTimeString('en-US',{hour:'numeric', minute:'2-digit', timeZone: safeTz}); +})()} {actor ? : SYS} {actor ? actor.name : 'System'} @@ -1189,6 +1195,7 @@ function WorkspaceTab({ user, isAdmin, dbUsers = [], onSwitchUser, onCreateUser, const [wsName, setWsName] = React.useState(workspace ? workspace.name : ''); const [wsTz, setWsTz] = React.useState(workspace ? workspace.timezone : ''); const [wsSaved, setWsSaved] = React.useState(false); + const [wsError, setWsError] = React.useState(''); // User editing state const [editingUserId, setEditingUserId] = React.useState(null); @@ -1234,6 +1241,16 @@ function WorkspaceTab({ user, isAdmin, dbUsers = [], onSwitchUser, onCreateUser, }; const handleUpdateWorkspace = async () => { + setWsError(''); // Clear previous errors + if (wsTz) { + try { + Intl.DateTimeFormat(undefined, { timeZone: wsTz }); + } catch (e) { + setWsError('Not a proper timezone (e.g., Asia/Tokyo)'); + return; // Stop the save process + } + } + await onUpdateWorkspace({ name: wsName, timezone: wsTz }); setWsSaved(true); setTimeout(() => setWsSaved(false), 2000); @@ -1378,12 +1395,13 @@ function WorkspaceTab({ user, isAdmin, dbUsers = [], onSwitchUser, onCreateUser, {isAdmin && (
    {wsSaved && Saved} - +
    )}