You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
6.4 KiB
234 lines
6.4 KiB
<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";
|
|
|
|
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} />
|
|
</div>
|
|
<div class="filter-group">
|
|
<label for="project">Project</label>
|
|
<ProjectSelect name="project" disabled={projectGroupId === ""} optional bind:value={projectId} groupId={projectGroupId} forceGroup />
|
|
</div>
|
|
<div class="filter-group">
|
|
<label for="item">Item</label>
|
|
<ItemSelect name="item" optional bind:value={itemId} enableWholeGroup />
|
|
</div>
|
|
</div>
|
|
<div class="error">{error}</div>
|
|
<div class="log-list" class:loading={$logStore.loading}>
|
|
{#each groupedLogs as logGroup (logGroup.day.getTime())}
|
|
<h2>{logGroup.text}</h2>
|
|
{#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>
|