## CHANGES

- Added IPC/API to fetch earliest recorded stats timestamp instantly 🧭
- Stats DB now computes earliest date across key tracking tables accurately 🧠
- Settings modal displays “since YYYY-MM-DD” alongside stats database size 📅
This commit is contained in:
Pedram Amini
2026-01-31 18:04:53 -05:00
parent 37e55bd106
commit 3f5fc40b22
5 changed files with 69 additions and 1 deletions

View File

@@ -244,6 +244,15 @@ export function registerStatsHandlers(deps: StatsHandlerDependencies): void {
})
);
// Get earliest stat timestamp (for UI display)
ipcMain.handle(
'stats:get-earliest-timestamp',
withIpcErrorLogging(handlerOpts('getEarliestTimestamp'), async () => {
const db = getStatsDB();
return db.getEarliestStatTimestamp();
})
);
// Record session creation (launched)
ipcMain.handle(
'stats:record-session-created',

View File

@@ -178,6 +178,10 @@ export function createStatsApi() {
// Get database size in bytes
getDatabaseSize: (): Promise<number> => ipcRenderer.invoke('stats:get-database-size'),
// Get earliest stat timestamp (null if no entries)
getEarliestTimestamp: (): Promise<number | null> =>
ipcRenderer.invoke('stats:get-earliest-timestamp'),
// Record session creation (for lifecycle tracking)
recordSessionCreated: (event: SessionCreatedEvent): Promise<string | null> =>
ipcRenderer.invoke('stats:record-session-created', event),

View File

@@ -666,6 +666,40 @@ export class StatsDB {
}
}
/**
* Get the timestamp of the earliest stat entry in the database.
* Checks query_events, auto_run_sessions, and session_lifecycle tables.
* Returns null if no entries exist.
*/
getEarliestStatTimestamp(): number | null {
if (!this.db) throw new Error('Database not initialized');
// Query minimum start_time from each table and find the overall minimum
const queryEventsMin = this.db
.prepare('SELECT MIN(start_time) as min_time FROM query_events')
.get() as { min_time: number | null } | undefined;
const autoRunMin = this.db
.prepare('SELECT MIN(start_time) as min_time FROM auto_run_sessions')
.get() as { min_time: number | null } | undefined;
const lifecycleMin = this.db
.prepare('SELECT MIN(created_at) as min_time FROM session_lifecycle')
.get() as { min_time: number | null } | undefined;
const timestamps = [
queryEventsMin?.min_time,
autoRunMin?.min_time,
lifecycleMin?.min_time,
].filter((t): t is number => t !== null && t !== undefined);
if (timestamps.length === 0) {
return null;
}
return Math.min(...timestamps);
}
/**
* Run VACUUM on the database to reclaim unused space and optimize structure.
*

View File

@@ -339,6 +339,7 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
// Stats data management state
const [statsDbSize, setStatsDbSize] = useState<number | null>(null);
const [statsEarliestDate, setStatsEarliestDate] = useState<string | null>(null);
const [statsClearing, setStatsClearing] = useState(false);
const [statsClearResult, setStatsClearResult] = useState<{
success: boolean;
@@ -379,7 +380,7 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
setSyncError('Failed to load storage settings');
});
// Load stats database size
// Load stats database size and earliest timestamp
window.maestro.stats
.getDatabaseSize()
.then((size) => {
@@ -389,6 +390,21 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
console.error('Failed to load stats database size:', err);
});
window.maestro.stats
.getEarliestTimestamp()
.then((timestamp) => {
if (timestamp) {
const date = new Date(timestamp);
const formatted = date.toISOString().split('T')[0]; // YYYY-MM-DD
setStatsEarliestDate(formatted);
} else {
setStatsEarliestDate(null);
}
})
.catch((err) => {
console.error('Failed to load earliest stats timestamp:', err);
});
// Reset stats clear state
setStatsClearResult(null);
}
@@ -1894,6 +1910,9 @@ export const SettingsModal = memo(function SettingsModal(props: SettingsModalPro
{statsDbSize !== null
? (statsDbSize / 1024 / 1024).toFixed(2) + ' MB'
: 'Loading...'}
{statsEarliestDate && (
<span style={{ color: theme.colors.textDim }}> (since {statsEarliestDate})</span>
)}
</span>
</div>

View File

@@ -2211,6 +2211,8 @@ interface MaestroAPI {
}>;
// Get database size in bytes
getDatabaseSize: () => Promise<number>;
// Get earliest stat timestamp (null if no entries exist)
getEarliestTimestamp: () => Promise<number | null>;
// Record session creation (launched)
recordSessionCreated: (event: {
sessionId: string;