Browse Source

add time filter.

main
Gisle Aune 3 years ago
parent
commit
cd416ba4c3
  1. 22
      svelte-ui/package-lock.json
  2. 1
      svelte-ui/package.json
  3. 123
      svelte-ui/src/components/DateRangeSelect.svelte
  4. 3
      svelte-ui/src/components/Markdown.svelte
  5. 32
      svelte-ui/src/pages/GoalPage.svelte
  6. 58
      svelte-ui/src/pages/LogsPage.svelte
  7. 22
      svelte-ui/src/stores/goal.ts
  8. 20
      svelte-ui/src/stores/logs.ts

22
svelte-ui/package-lock.json

@ -2757,6 +2757,12 @@
"integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
"dev": true
},
"assignment": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/assignment/-/assignment-2.0.0.tgz",
"integrity": "sha1-/9F7Ib9dayLnd7mJaBqBVFaj3T4=",
"dev": true
},
"async": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
@ -3256,6 +3262,12 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"he": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/he/-/he-0.5.0.tgz",
"integrity": "sha1-LAX/rvkLaOhg8/0rVO9YCYknfuI=",
"dev": true
},
"http-assert": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz",
@ -3355,6 +3367,16 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
},
"insane": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/insane/-/insane-2.6.2.tgz",
"integrity": "sha1-wqtouz4AarRRVg0bRGkXMpwKgSA=",
"dev": true,
"requires": {
"assignment": "2.0.0",
"he": "0.5.0"
}
},
"is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",

1
svelte-ui/package.json

@ -22,6 +22,7 @@
"amazon-cognito-identity-js": "^4.5.6",
"aws-amplify": "^3.3.14",
"fa-svelte": "^3.1.0",
"insane": "^2.6.2",
"lodash-es": "^4.17.20",
"marked": "^1.2.7",
"rollup": "^2.3.4",

123
svelte-ui/src/components/DateRangeSelect.svelte

@ -14,6 +14,8 @@ import EveryMinute from "./EveryMinute.svelte";
export let toValue: string;
export let disabled: boolean;
export let styled: boolean;
export let noFuture: boolean;
export let label: string = "Time Period";
let selected: string = "custom";
let options: DateOption[] = [];
@ -39,12 +41,14 @@ import EveryMinute from "./EveryMinute.svelte";
from: startOfWeek(now),
to: endOfWeek(now),
});
options.push({
id: "next_week",
label: "Next Week",
from: startOfWeek(nextWeek),
to: endOfWeek(nextWeek),
});
if (!noFuture) {
options.push({
id: "next_week",
label: "Next Week",
from: startOfWeek(nextWeek),
to: endOfWeek(nextWeek),
});
}
options.push({
id: "last_week",
label: "Last Week",
@ -58,12 +62,14 @@ import EveryMinute from "./EveryMinute.svelte";
from: current,
to: endOfMonth(current),
});
options.push({
id: "next_month",
label: "Next Month",
from: nextMonth(current),
to: endOfMonth(nextMonth(current)),
});
if (!noFuture) {
options.push({
id: "next_month",
label: "Next Month",
from: nextMonth(current),
to: endOfMonth(nextMonth(current)),
});
}
options.push({
id: "last_month",
label: "Last Month",
@ -77,12 +83,14 @@ import EveryMinute from "./EveryMinute.svelte";
from: startOfYear(now),
to: endOfYear(now),
});
options.push({
id: "next_year",
label: "Next Year",
from: startOfYear(new Date(Date.now() + (366.25 * 86400000))),
to: endOfYear(new Date(Date.now() + (365.25 * 86400000))),
});
if (!noFuture) {
options.push({
id: "next_year",
label: "Next Year",
from: startOfYear(new Date(Date.now() + (366.25 * 86400000))),
to: endOfYear(new Date(Date.now() + (365.25 * 86400000))),
});
}
options.push({
id: "last_year",
label: "Last Year",
@ -112,16 +120,73 @@ import EveryMinute from "./EveryMinute.svelte";
<EveryMinute bind:now={now} />
<div class:styled>
<label for="timeRange">Time Period</label>
<select name="timeRange" disabled={disabled} bind:value={selected}>
{#each options as option (option.id)}
<option value={option.id}>{option.label}</option>
{/each}
</select>
<div>
<label for="timeRange">{label}</label>
<select name="timeRange" disabled={disabled} bind:value={selected}>
{#each options as option (option.id)}
<option value={option.id}>{option.label}</option>
{/each}
</select>
</div>
{#if selected === "custom"}
<label for="startTime">Start Time</label>
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={fromValue} />
<label for="endTime">End Time</label>
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={toValue} />
<div>
<label for="startTime">Start Time</label>
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={fromValue} />
</div>
<div>
<label for="endTime">End Time</label>
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={toValue} />
</div>
{/if}
</div>
</div>
<style>
div.styled {
display: flex;
flex-direction: row;
width: 100%;
}
div.styled > div:first {
width: 20%;
}
div.styled > div {
padding: 0.5em;
margin: 0 auto;
width: 35%;
}
div.styled select, div.styled input {
width: 100%;
margin-bottom: 0.5em;
background: #222;
color: #777;
border: none;
outline: none;
resize: vertical;
}
div.styled select {
padding: 0.46em 0.5ch;
}
div.styled input:focus, div.styled select:focus {
background: #191919;
color: #CCC;
border: none;
outline: none;
}
@media screen and (max-width: 900px) {
div.styled {
flex-direction: column;
margin-bottom: 1em;
}
div.styled > div {
width: 90%;
}
}
</style>

3
svelte-ui/src/components/Markdown.svelte

@ -1,10 +1,11 @@
<script lang="ts">
import marked from "marked";
import insane from "insane";
export let source = "";
let outputHtml: string;
$: outputHtml = marked(source, {sanitize: true, smartypants: true});
$: outputHtml = insane(marked(source, {smartypants: true}));
</script>
<div class="markdown">{@html outputHtml}</div>

32
svelte-ui/src/pages/GoalPage.svelte

@ -4,23 +4,37 @@
import type { ModalData } from "../stores/modal";
import goalStore from "../stores/goal";
import RefreshSelection from "../components/RefreshSelection.svelte";
import { endOfWeek, formatFormTime, startOfWeek } from "../utils/time";
import DateRangeSelect from "../components/DateRangeSelect.svelte";
const mdGoalAdd: ModalData = {name: "goal.add"};
let minTime = new Date(Date.now() - 366 * 86400000);
let minTime = formatFormTime($goalStore.filter.minTime || startOfWeek(new Date()));
let maxTime = formatFormTime(endOfWeek(new Date()));
$: {
if ($goalStore.stale && !$goalStore.loading) {
goalStore.load({minTime});
const min = new Date(minTime);
const max = new Date(maxTime);
if (!$goalStore.loading) {
if ($goalStore.stale || $goalStore.filter.minTime?.getTime() != min.getTime() || $goalStore.filter.maxTime?.getTime() != max.getTime()) {
goalStore.load({
minTime: min,
maxTime: max,
});
}
}
}
</script>
<div class="page">
{#each $goalStore.goals as goal (goal.id)}
<GoalEntry showAllOptions goal={goal} />
{/each}
<Boi open={mdGoalAdd}>Add Goal</Boi>
<DateRangeSelect label="Time Filter" styled bind:fromValue={minTime} bind:toValue={maxTime} />
<div class="goal-list" class:loading={$goalStore.loading}>
{#each $goalStore.goals as goal (goal.id)}
<GoalEntry showAllOptions goal={goal} />
{/each}
<Boi open={mdGoalAdd}>Add Goal</Boi>
</div>
</div>
<RefreshSelection />
@ -33,4 +47,8 @@
margin-top: 0;
box-sizing: border-box;
}
div.goal-list.loading {
opacity: 0.75;
}
</style>

58
svelte-ui/src/pages/LogsPage.svelte

@ -2,33 +2,33 @@
import LogEntry from "../components/LogEntry.svelte";
import type { LogResult } from "../models/log";
import logStore from "../stores/logs";
import { formatDate, formatTime, formatWeekdayDate } from "../utils/time";
import Boi from "../components/Boi.svelte";
import { endOfWeek, formatFormTime, formatWeekdayDate, startOfWeek } from "../utils/time";
import EmptyList from "../components/EmptyList.svelte";
import RefreshSelection from "../components/RefreshSelection.svelte";
import DateRangeSelect from "../components/DateRangeSelect.svelte";
let groupedLogs: {day: number, text: string, logs: LogResult[]}[] = [];
let minTime = $logStore.filter.minTime || new Date(Date.now() - (86400000*30));
let minTime = formatFormTime($logStore.filter.minTime || startOfWeek(new Date()));
let maxTime = formatFormTime(endOfWeek(new Date()));
let emptyMessage = `No logs since ${formatWeekdayDate(minTime)}.`;
function loadMore() {
if (!$logStore.stale && !$logStore.loading) {
minTime = new Date(minTime.getTime() - (86400000*30));
logStore.markStale();
}
}
$: {
if ($logStore.stale && !$logStore.loading) {
logStore.load({
minTime: minTime,
});
const min = new Date(minTime);
const max = new Date(maxTime);
if (!$logStore.loading) {
if ($logStore.stale || $logStore.filter.minTime?.getTime() != min.getTime() || $logStore.filter.maxTime?.getTime() != max.getTime()) {
logStore.load({
minTime: min,
maxTime: max,
});
}
}
}
$: {
if (!$logStore.loading && !$logStore.stale) {
emptyMessage = `No logs since ${formatWeekdayDate(minTime)}.`;
emptyMessage = `No logs between ${formatWeekdayDate(minTime)} and ${formatWeekdayDate(maxTime)}.`;
}
}
@ -81,20 +81,18 @@
</script>
<div class="page">
{#each groupedLogs as logGroup (logGroup.day)}
<h2>{logGroup.text}</h2>
{#each logGroup.logs as log (log.id)}
<LogEntry log={log} />
<DateRangeSelect label="Time Filter" noFuture styled bind:fromValue={minTime} bind:toValue={maxTime} />
<div class="log-list" class:loading={$logStore.loading}>
{#each groupedLogs as logGroup (logGroup.day)}
<h2>{logGroup.text}</h2>
{#each logGroup.logs as log (log.id)}
<LogEntry log={log} />
{/each}
{/each}
{/each}
{#if groupedLogs.length === 0}
<EmptyList icon="list" text={emptyMessage} />
{/if}
{#if !$logStore.loading && !$logStore.stale}
<Boi on:click={loadMore}>Load More</Boi>
{:else}
<Boi disabled>Loading...</Boi>
{/if}
{#if groupedLogs.length === 0}
<EmptyList icon="list" text={emptyMessage} />
{/if}
</div>
</div>
<RefreshSelection />
@ -108,6 +106,10 @@
box-sizing: border-box;
}
div.log-list.loading {
opacity: 0.75;
}
h2 {
font-size: 1.5em;
font-weight: 100;

22
svelte-ui/src/stores/goal.ts

@ -6,6 +6,7 @@ interface GoalStoreData {
loading: boolean
stale: boolean
goals: GoalResult[]
filter: GoalFilter
}
function createGoalStore() {
@ -13,8 +14,12 @@ function createGoalStore() {
loading: false,
stale: true,
goals: [],
filter: {},
});
let lastLoad = 0;
let spamPoints = 3;
return {
subscribe,
@ -24,6 +29,23 @@ function createGoalStore() {
async load(filter: GoalFilter) {
update(v => ({...v, loading: true, filter}));
// Prevent too much spammery when tweaking the date.
const timeSince = Date.now() - lastLoad;
if (timeSince < 3000) {
if (timeSince < 1000) {
spamPoints -= 1;
}
if (spamPoints <= 0) {
await new Promise(resolve => setTimeout(resolve, 3000 - timeSince));
spamPoints = 3;
}
} else {
spamPoints = 3;
}
lastLoad = Date.now();
const goals = await stuffLogClient.listGoals(filter);
const expireds = [];

20
svelte-ui/src/stores/logs.ts

@ -17,6 +17,9 @@ function createProjectStore() {
filter: {},
});
let lastLoad = 0;
let spamPoints = 10;
return {
subscribe,
@ -26,6 +29,23 @@ function createProjectStore() {
async load(filter: LogFilter) {
update(v => ({...v, loading: true, filter}));
// Prevent too much spammery when tweaking the date.
const timeSince = Date.now() - lastLoad;
if (timeSince < 3000) {
if (timeSince < 1000) {
spamPoints -= 1;
}
if (spamPoints <= 0) {
await new Promise(resolve => setTimeout(resolve, 3000 - timeSince));
spamPoints = 10;
}
} else {
spamPoints = 10;
}
lastLoad = Date.now();
const logs = await stuffLogClient.listLogs(filter);
update(v => ({...v, loading: false, stale: false, logs: logs.reverse()}));
},

Loading…
Cancel
Save