Generated
+22
@@ -2757,6 +2757,12 @@
|
|||||||
"integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
|
"integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"assignment": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/assignment/-/assignment-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-/9F7Ib9dayLnd7mJaBqBVFaj3T4=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
|
||||||
@@ -3256,6 +3262,12 @@
|
|||||||
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
"dev": true
|
"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": {
|
"http-assert": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.1.tgz",
|
||||||
@@ -3355,6 +3367,16 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true
|
"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": {
|
"is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"amazon-cognito-identity-js": "^4.5.6",
|
"amazon-cognito-identity-js": "^4.5.6",
|
||||||
"aws-amplify": "^3.3.14",
|
"aws-amplify": "^3.3.14",
|
||||||
"fa-svelte": "^3.1.0",
|
"fa-svelte": "^3.1.0",
|
||||||
|
"insane": "^2.6.2",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"marked": "^1.2.7",
|
"marked": "^1.2.7",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import EveryMinute from "./EveryMinute.svelte";
|
|||||||
export let toValue: string;
|
export let toValue: string;
|
||||||
export let disabled: boolean;
|
export let disabled: boolean;
|
||||||
export let styled: boolean;
|
export let styled: boolean;
|
||||||
|
export let noFuture: boolean;
|
||||||
|
export let label: string = "Time Period";
|
||||||
|
|
||||||
let selected: string = "custom";
|
let selected: string = "custom";
|
||||||
let options: DateOption[] = [];
|
let options: DateOption[] = [];
|
||||||
@@ -39,12 +41,14 @@ import EveryMinute from "./EveryMinute.svelte";
|
|||||||
from: startOfWeek(now),
|
from: startOfWeek(now),
|
||||||
to: endOfWeek(now),
|
to: endOfWeek(now),
|
||||||
});
|
});
|
||||||
options.push({
|
if (!noFuture) {
|
||||||
id: "next_week",
|
options.push({
|
||||||
label: "Next Week",
|
id: "next_week",
|
||||||
from: startOfWeek(nextWeek),
|
label: "Next Week",
|
||||||
to: endOfWeek(nextWeek),
|
from: startOfWeek(nextWeek),
|
||||||
});
|
to: endOfWeek(nextWeek),
|
||||||
|
});
|
||||||
|
}
|
||||||
options.push({
|
options.push({
|
||||||
id: "last_week",
|
id: "last_week",
|
||||||
label: "Last Week",
|
label: "Last Week",
|
||||||
@@ -58,12 +62,14 @@ import EveryMinute from "./EveryMinute.svelte";
|
|||||||
from: current,
|
from: current,
|
||||||
to: endOfMonth(current),
|
to: endOfMonth(current),
|
||||||
});
|
});
|
||||||
options.push({
|
if (!noFuture) {
|
||||||
id: "next_month",
|
options.push({
|
||||||
label: "Next Month",
|
id: "next_month",
|
||||||
from: nextMonth(current),
|
label: "Next Month",
|
||||||
to: endOfMonth(nextMonth(current)),
|
from: nextMonth(current),
|
||||||
});
|
to: endOfMonth(nextMonth(current)),
|
||||||
|
});
|
||||||
|
}
|
||||||
options.push({
|
options.push({
|
||||||
id: "last_month",
|
id: "last_month",
|
||||||
label: "Last Month",
|
label: "Last Month",
|
||||||
@@ -77,12 +83,14 @@ import EveryMinute from "./EveryMinute.svelte";
|
|||||||
from: startOfYear(now),
|
from: startOfYear(now),
|
||||||
to: endOfYear(now),
|
to: endOfYear(now),
|
||||||
});
|
});
|
||||||
options.push({
|
if (!noFuture) {
|
||||||
id: "next_year",
|
options.push({
|
||||||
label: "Next Year",
|
id: "next_year",
|
||||||
from: startOfYear(new Date(Date.now() + (366.25 * 86400000))),
|
label: "Next Year",
|
||||||
to: endOfYear(new Date(Date.now() + (365.25 * 86400000))),
|
from: startOfYear(new Date(Date.now() + (366.25 * 86400000))),
|
||||||
});
|
to: endOfYear(new Date(Date.now() + (365.25 * 86400000))),
|
||||||
|
});
|
||||||
|
}
|
||||||
options.push({
|
options.push({
|
||||||
id: "last_year",
|
id: "last_year",
|
||||||
label: "Last Year",
|
label: "Last Year",
|
||||||
@@ -112,16 +120,73 @@ import EveryMinute from "./EveryMinute.svelte";
|
|||||||
|
|
||||||
<EveryMinute bind:now={now} />
|
<EveryMinute bind:now={now} />
|
||||||
<div class:styled>
|
<div class:styled>
|
||||||
<label for="timeRange">Time Period</label>
|
<div>
|
||||||
<select name="timeRange" disabled={disabled} bind:value={selected}>
|
<label for="timeRange">{label}</label>
|
||||||
{#each options as option (option.id)}
|
<select name="timeRange" disabled={disabled} bind:value={selected}>
|
||||||
<option value={option.id}>{option.label}</option>
|
{#each options as option (option.id)}
|
||||||
{/each}
|
<option value={option.id}>{option.label}</option>
|
||||||
</select>
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
{#if selected === "custom"}
|
{#if selected === "custom"}
|
||||||
<label for="startTime">Start Time</label>
|
<div>
|
||||||
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={fromValue} />
|
<label for="startTime">Start Time</label>
|
||||||
<label for="endTime">End Time</label>
|
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={fromValue} />
|
||||||
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={toValue} />
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="endTime">End Time</label>
|
||||||
|
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={toValue} />
|
||||||
|
</div>
|
||||||
{/if}
|
{/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>
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import marked from "marked";
|
import marked from "marked";
|
||||||
|
import insane from "insane";
|
||||||
export let source = "";
|
export let source = "";
|
||||||
|
|
||||||
let outputHtml: string;
|
let outputHtml: string;
|
||||||
|
|
||||||
$: outputHtml = marked(source, {sanitize: true, smartypants: true});
|
$: outputHtml = insane(marked(source, {smartypants: true}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="markdown">{@html outputHtml}</div>
|
<div class="markdown">{@html outputHtml}</div>
|
||||||
|
|||||||
@@ -4,23 +4,37 @@
|
|||||||
import type { ModalData } from "../stores/modal";
|
import type { ModalData } from "../stores/modal";
|
||||||
import goalStore from "../stores/goal";
|
import goalStore from "../stores/goal";
|
||||||
import RefreshSelection from "../components/RefreshSelection.svelte";
|
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"};
|
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) {
|
const min = new Date(minTime);
|
||||||
goalStore.load({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>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
{#each $goalStore.goals as goal (goal.id)}
|
<DateRangeSelect label="Time Filter" styled bind:fromValue={minTime} bind:toValue={maxTime} />
|
||||||
<GoalEntry showAllOptions goal={goal} />
|
<div class="goal-list" class:loading={$goalStore.loading}>
|
||||||
{/each}
|
{#each $goalStore.goals as goal (goal.id)}
|
||||||
<Boi open={mdGoalAdd}>Add Goal</Boi>
|
<GoalEntry showAllOptions goal={goal} />
|
||||||
|
{/each}
|
||||||
|
<Boi open={mdGoalAdd}>Add Goal</Boi>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<RefreshSelection />
|
<RefreshSelection />
|
||||||
|
|
||||||
@@ -33,4 +47,8 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.goal-list.loading {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -2,33 +2,33 @@
|
|||||||
import LogEntry from "../components/LogEntry.svelte";
|
import LogEntry from "../components/LogEntry.svelte";
|
||||||
import type { LogResult } from "../models/log";
|
import type { LogResult } from "../models/log";
|
||||||
import logStore from "../stores/logs";
|
import logStore from "../stores/logs";
|
||||||
import { formatDate, formatTime, formatWeekdayDate } from "../utils/time";
|
import { endOfWeek, formatFormTime, formatWeekdayDate, startOfWeek } from "../utils/time";
|
||||||
import Boi from "../components/Boi.svelte";
|
|
||||||
import EmptyList from "../components/EmptyList.svelte";
|
import EmptyList from "../components/EmptyList.svelte";
|
||||||
import RefreshSelection from "../components/RefreshSelection.svelte";
|
import RefreshSelection from "../components/RefreshSelection.svelte";
|
||||||
|
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
||||||
|
|
||||||
let groupedLogs: {day: number, text: string, logs: LogResult[]}[] = [];
|
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)}.`;
|
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) {
|
const min = new Date(minTime);
|
||||||
logStore.load({
|
const max = new Date(maxTime);
|
||||||
minTime: minTime,
|
|
||||||
});
|
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) {
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
{#each groupedLogs as logGroup (logGroup.day)}
|
<DateRangeSelect label="Time Filter" noFuture styled bind:fromValue={minTime} bind:toValue={maxTime} />
|
||||||
<h2>{logGroup.text}</h2>
|
<div class="log-list" class:loading={$logStore.loading}>
|
||||||
{#each logGroup.logs as log (log.id)}
|
{#each groupedLogs as logGroup (logGroup.day)}
|
||||||
<LogEntry log={log} />
|
<h2>{logGroup.text}</h2>
|
||||||
|
{#each logGroup.logs as log (log.id)}
|
||||||
|
<LogEntry log={log} />
|
||||||
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{#if groupedLogs.length === 0}
|
||||||
{#if groupedLogs.length === 0}
|
<EmptyList icon="list" text={emptyMessage} />
|
||||||
<EmptyList icon="list" text={emptyMessage} />
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
{#if !$logStore.loading && !$logStore.stale}
|
|
||||||
<Boi on:click={loadMore}>Load More</Boi>
|
|
||||||
{:else}
|
|
||||||
<Boi disabled>Loading...</Boi>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
<RefreshSelection />
|
<RefreshSelection />
|
||||||
|
|
||||||
@@ -108,6 +106,10 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.log-list.loading {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ interface GoalStoreData {
|
|||||||
loading: boolean
|
loading: boolean
|
||||||
stale: boolean
|
stale: boolean
|
||||||
goals: GoalResult[]
|
goals: GoalResult[]
|
||||||
|
filter: GoalFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
function createGoalStore() {
|
function createGoalStore() {
|
||||||
@@ -13,8 +14,12 @@ function createGoalStore() {
|
|||||||
loading: false,
|
loading: false,
|
||||||
stale: true,
|
stale: true,
|
||||||
goals: [],
|
goals: [],
|
||||||
|
filter: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let lastLoad = 0;
|
||||||
|
let spamPoints = 3;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
|
||||||
@@ -24,6 +29,23 @@ function createGoalStore() {
|
|||||||
|
|
||||||
async load(filter: GoalFilter) {
|
async load(filter: GoalFilter) {
|
||||||
update(v => ({...v, loading: true, filter}));
|
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 goals = await stuffLogClient.listGoals(filter);
|
||||||
|
|
||||||
const expireds = [];
|
const expireds = [];
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ function createProjectStore() {
|
|||||||
filter: {},
|
filter: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let lastLoad = 0;
|
||||||
|
let spamPoints = 10;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
|
||||||
@@ -26,6 +29,23 @@ function createProjectStore() {
|
|||||||
|
|
||||||
async load(filter: LogFilter) {
|
async load(filter: LogFilter) {
|
||||||
update(v => ({...v, loading: true, filter}));
|
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);
|
const logs = await stuffLogClient.listLogs(filter);
|
||||||
update(v => ({...v, loading: false, stale: false, logs: logs.reverse()}));
|
update(v => ({...v, loading: false, stale: false, logs: logs.reverse()}));
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user