|
|
<script lang="ts"> import LogEntry from "../components/LogEntry.svelte"; import type { LogFilter, LogResult } from "../models/log"; import logStore from "../stores/logs"; import { addDays, formatWeekdayDate, startOfDay } from "../utils/time"; import { allOffsets, RelativeDateRange } from "../utils/date-range"; import EmptyList from "../components/EmptyList.svelte"; import RefreshSelection from "../components/RefreshSelection.svelte"; import DateRangeSelect from "../components/DateRangeSelect.svelte"; import EveryMinute from "../components/EveryMinute.svelte"; import ProjectGroupSelect from "../components/ProjectGroupSelect.svelte"; import ProjectSelect from "../components/ProjectSelect.svelte"; import ItemSelect from "../components/ItemSelect.svelte"; import projectStore from "../stores/project"; import projectGroupStore from "../stores/projectGroup"; import ItemProgress from "../components/ItemProgress.svelte"; const lsValue = localStorage.getItem("sl2.logspage.range")
let groupedLogs: {day: Date, text: string, logs: LogResult[]}[] = []; let range = allOffsets.find(o => o.name === lsValue) || new RelativeDateRange(-7, "day"); let now = new Date(); let error = ""; let emptyMessage = "Loading...";
let min: Date; let max: Date;
let projectGroupId = localStorage.getItem("sl2.logspage.project_group_id") || ""; let projectId = localStorage.getItem("sl2.logspage.project_id") || ""; let itemId = localStorage.getItem("sl2.logspage.item_id") || "";
$: [min, max] = range.calculate(now);
$: localStorage.setItem("sl2.logspage.range", range.name); $: localStorage.setItem("sl2.logspage.project_group_id", projectGroupId); $: localStorage.setItem("sl2.logspage.project_id", projectId); $: localStorage.setItem("sl2.logspage.item_id", itemId);
$: { if ($projectStore.stale && !$projectStore.loading) { projectStore.load({}); } }
$: { if ($projectGroupStore.stale && !$projectGroupStore.loading) { projectGroupStore.load(); } }
$: { error = "";
if (!Number.isNaN(min.getTime()) && !Number.isNaN(max.getTime())) { if (min.getTime() < max.getTime()) { if (!$logStore.loading) { const filter: LogFilter = { minTime: min, maxTime: max, itemIds: itemId ? itemId.split(",") : void(0), projectIds: projectId ? [projectId] : void(0), projectGroupIds: (!projectId && projectGroupId) ? [projectGroupId] : void(0), }
if ($logStore.stale || JSON.stringify(filter) !== JSON.stringify($logStore.filter)) { logStore.load(filter); } } } else { error = "Time only goes one way in this universe, you should respect that." } } else { error = "The dates must be valid." } }
$: { if (!$logStore.loading && !$logStore.stale) { emptyMessage = `No matching logs between ${formatWeekdayDate(min)} and ${formatWeekdayDate(max)}.`; } else { emptyMessage = "Loading..."; } }
$: { if (!$logStore.loading) { groupedLogs = [];
if ($logStore.logs.length > 0) {
const today = new Date(); const tomorrow = addDays(today, 1); const yesterday = addDays(today, -1);
let current = new Date($logStore.logs[0].loggedTime);
let remainingLogs = $logStore.logs;
while (remainingLogs.length > 0) { const currentDay = startOfDay(current);
const currentLogs: LogResult[] = []; for (const log of remainingLogs) { if (Date.parse(log.loggedTime) >= currentDay.getTime()) { currentLogs.push(log); } else { break; } }
if (currentLogs.length > 0) { remainingLogs = remainingLogs.slice(currentLogs.length);
let text = formatWeekdayDate(current); if (current.getTime() === tomorrow.getTime()) { text = "Tomorrow"; } else if (current.getTime() === today.getTime()) { text = "Today"; } else if (current.getTime() === yesterday.getTime()) { text = "Yesterday"; } groupedLogs.push({day: current, text, logs: currentLogs}); }
current = addDays(current, -1); } } } } </script>
<div class="page"> <DateRangeSelect label="Time Filter" noFuture styled bind:value={range} /> <div class="extra-filters"> <div class="filter-group"> <label for="projectGroup">Project Group</label> <ProjectGroupSelect name="projectGroup" optional bind:value={projectGroupId} placeholder="Any" /> </div> <div class="filter-group"> <label for="project">Project</label> <ProjectSelect name="project" disabled={projectGroupId === ""} optional bind:value={projectId} groupId={projectGroupId} forceGroup placeholder="Any" /> </div> <div class="filter-group"> <label for="item">Item</label> <ItemSelect name="item" optional bind:value={itemId} enableWholeGroup placeholder="Any" /> </div> </div> <div class="error">{error}</div> <ItemProgress logs={$logStore.logs} centered /> <div class="log-list" class:loading={$logStore.loading}> {#each groupedLogs as logGroup (logGroup.day.getTime())} <h2>{logGroup.text}</h2> <ItemProgress logs={logGroup.logs} centered /> {#each logGroup.logs as log (log.id)} <LogEntry log={log} /> {/each} {/each} {#if groupedLogs.length === 0} <EmptyList icon="list" text={emptyMessage} /> {/if} </div> </div> <RefreshSelection /> <EveryMinute bind:now={now} />
<style> div.error { color: #ff4545; text-align: center; } div.error:empty { display: none; }
div.page { display: block; margin: auto; max-width: 100%; width: 640px; margin-top: 0; box-sizing: border-box; }
div.log-list.loading { opacity: 0.75; }
div.extra-filters { display: flex; flex-direction: row; } div.filter-group { flex: 1; padding: 0 0.5em; margin: 0 auto; } div.filter-group label { line-height: 1.5em; } div.filter-group :global(select) { width: 100%; margin-bottom: 0.5em;
background: #222; color: #777; border: none; outline: none; resize: vertical; } div.filter-group :global(select:focus) { background: #191919; color: #CCC; border: none; outline: none; }
h2 { font-size: 1.5em; font-weight: 100; text-align: center; margin: 0; margin-top: 1em; }
@media screen and (max-width: 900px) { div.extra-filters { flex-direction: column; }
div.filter-group { width: 90%; } }
</style>
|