fix date range behavior to be more strongly typed, last 7 days is not an alias for a static date range.
This commit is contained in:
Generated
+45
-14701
File diff suppressed because it is too large
Load Diff
@@ -19,12 +19,14 @@
|
|||||||
"@tsconfig/svelte": "^1.0.0",
|
"@tsconfig/svelte": "^1.0.0",
|
||||||
"@types/marked": "^1.2.1",
|
"@types/marked": "^1.2.1",
|
||||||
"@types/node": "^14.14.17",
|
"@types/node": "^14.14.17",
|
||||||
|
"@types/pluralize": "0.0.29",
|
||||||
"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",
|
"insane": "^2.6.2",
|
||||||
"lodash-es": "^4.17.20",
|
"lodash-es": "^4.17.20",
|
||||||
"marked": "^2.0.1",
|
"marked": "^2.0.1",
|
||||||
|
"pluralize": "^8.0.0",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
"rollup-plugin-dev": "^1.1.3",
|
"rollup-plugin-dev": "^1.1.3",
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="composition">
|
<div class="composition">
|
||||||
{#each list as item (item.name)}
|
{#each list as item}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<span class="amount">{item.amount}x </span>
|
<span class="amount">{item.amount}x </span>
|
||||||
<a href={item.link}>{item.name}</a>
|
<a href={item.link}>{item.name}</a>
|
||||||
|
|||||||
@@ -1,194 +1,54 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from "svelte";
|
import { allOffsets, pastOffsets, customDateRange } from "../utils/date-range";
|
||||||
import { endOfDay, endOfMonth, endOfWeek, endOfYear, formatFormTime, lastMonth, lastYear, monthName, nextMonth, nextYear, startOfDay, startOfMonth, startOfWeek, startOfYear } from "../utils/time";
|
import type { DateRange } from "../utils/date-range";
|
||||||
import EveryMinute from "./EveryMinute.svelte";
|
import { formatFormTime } from "../utils/time";
|
||||||
|
|
||||||
interface DateOption {
|
export let noFuture: boolean = false;
|
||||||
id: string
|
export let value: DateRange;
|
||||||
label: string
|
export let styled: boolean = false;
|
||||||
from: Date
|
export let label: string = "Time";
|
||||||
to: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
export let fromValue: string;
|
|
||||||
export let toValue: string;
|
|
||||||
export let disabled: boolean = false;
|
export let disabled: boolean = false;
|
||||||
export let styled: boolean;
|
|
||||||
export let noFuture: boolean;
|
|
||||||
export let label: string = "Time Period";
|
|
||||||
|
|
||||||
let selected: string = "custom";
|
let rangeOptions: DateRange[];
|
||||||
let options: DateOption[] = [];
|
let selected = value.name;
|
||||||
let now: Date = new Date();
|
let [customMin, customMax] = pastOffsets[0].calculate(new Date()).map(d => formatFormTime(d));
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
for (const option of options) {
|
|
||||||
console.log(formatFormTime(option.from), fromValue, formatFormTime(option.to), toValue);
|
|
||||||
if (formatFormTime(option.from) === fromValue && formatFormTime(option.to) === toValue) {
|
|
||||||
selected = option.id;
|
|
||||||
console.log("-- SELECTED")
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
$: rangeOptions = noFuture ? pastOffsets : allOffsets;
|
||||||
$: {
|
$: {
|
||||||
if (options.length === 0) {
|
if (selected === "Specific Dates") {
|
||||||
let current = startOfMonth(now);
|
value = customDateRange(new Date(customMin), new Date(customMax))
|
||||||
const nextWeek = new Date(now.getTime() + (86400000 * 7));
|
} else {
|
||||||
const lastWeek = new Date(now.getTime() - (86400000 * 7));
|
value = rangeOptions.find(v => v.name === selected);
|
||||||
|
if (value != null) {
|
||||||
options.push({
|
let [from, to] = value.calculate(new Date()).map(d => formatFormTime(d));
|
||||||
id: "this_week",
|
customMin = from;
|
||||||
label: "This Week",
|
customMax = to;
|
||||||
from: startOfWeek(now),
|
} else {
|
||||||
to: endOfWeek(now),
|
selected = rangeOptions[0].name;
|
||||||
});
|
value = rangeOptions[0];
|
||||||
if (!noFuture) {
|
|
||||||
options.push({
|
|
||||||
id: "next_week",
|
|
||||||
label: "Next Week",
|
|
||||||
from: startOfWeek(nextWeek),
|
|
||||||
to: endOfWeek(nextWeek),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "next_7days",
|
|
||||||
label: "Next 7 Days",
|
|
||||||
from: startOfDay(now),
|
|
||||||
to: endOfDay(new Date(now.getTime() + 86400000 * 7)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
options.push({
|
|
||||||
id: "last_week",
|
|
||||||
label: "Last Week",
|
|
||||||
from: startOfWeek(lastWeek),
|
|
||||||
to: endOfWeek(lastWeek),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "last_7days",
|
|
||||||
label: "Last 7 Days",
|
|
||||||
from: startOfDay(new Date(now.getTime() - 86400000 * 7)),
|
|
||||||
to: endOfDay(now),
|
|
||||||
});
|
|
||||||
|
|
||||||
options.push({
|
|
||||||
id: "this_month",
|
|
||||||
label: "This Month",
|
|
||||||
from: current,
|
|
||||||
to: endOfMonth(current),
|
|
||||||
});
|
|
||||||
if (!noFuture) {
|
|
||||||
options.push({
|
|
||||||
id: "next_month",
|
|
||||||
label: "Next Month",
|
|
||||||
from: nextMonth(current),
|
|
||||||
to: endOfMonth(nextMonth(current)),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "next_30days",
|
|
||||||
label: "Next 30 Days",
|
|
||||||
from: startOfDay(now),
|
|
||||||
to: endOfDay(new Date(now.getTime() + 86400000 * 30)),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "next_90days",
|
|
||||||
label: "Next 90 Days",
|
|
||||||
from: startOfDay(now),
|
|
||||||
to: endOfDay(new Date(now.getTime() + 86400000 * 90)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
options.push({
|
|
||||||
id: "last_month",
|
|
||||||
label: "Last Month",
|
|
||||||
from: lastMonth(current),
|
|
||||||
to: endOfMonth(lastMonth(current)),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "last_30days",
|
|
||||||
label: "Last 30 Days",
|
|
||||||
from: startOfDay(new Date(now.getTime() - 86400000 * 30)),
|
|
||||||
to: endOfDay(now),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "last_90days",
|
|
||||||
label: "Last 90 Days",
|
|
||||||
from: startOfDay(new Date(now.getTime() - 86400000 * 90)),
|
|
||||||
to: endOfDay(now),
|
|
||||||
});
|
|
||||||
|
|
||||||
options.push({
|
|
||||||
id: "this_year",
|
|
||||||
label: "This Year",
|
|
||||||
from: startOfYear(now),
|
|
||||||
to: endOfYear(now),
|
|
||||||
});
|
|
||||||
if (!noFuture) {
|
|
||||||
options.push({
|
|
||||||
id: "next_year",
|
|
||||||
label: "Next Year",
|
|
||||||
from: startOfYear(nextYear(now)),
|
|
||||||
to: endOfYear(nextYear(now, 2)),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "next_1year",
|
|
||||||
label: "Next 1 Year",
|
|
||||||
from: startOfDay(now),
|
|
||||||
to: endOfDay(nextYear(now)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
options.push({
|
|
||||||
id: "last_year",
|
|
||||||
label: "Last Year",
|
|
||||||
from: startOfYear(lastYear(now)),
|
|
||||||
to: endOfYear(lastYear(now)),
|
|
||||||
});
|
|
||||||
options.push({
|
|
||||||
id: "last_1year",
|
|
||||||
label: "Last 1 Year",
|
|
||||||
from: startOfDay(lastYear(now)),
|
|
||||||
to: endOfDay(now),
|
|
||||||
});
|
|
||||||
|
|
||||||
options.push({
|
|
||||||
id: "custom",
|
|
||||||
label: "Specific Dates",
|
|
||||||
from: null,
|
|
||||||
to: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (selected !== "custom") {
|
|
||||||
const option = options.find(o => o.id === selected);
|
|
||||||
if (option != null) {
|
|
||||||
fromValue = formatFormTime(option.from);
|
|
||||||
toValue = formatFormTime(option.to);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<EveryMinute bind:now={now} />
|
|
||||||
<div class:styled>
|
<div class:styled>
|
||||||
<div>
|
<div>
|
||||||
<label for="timeRange">{label}</label>
|
<label for="timeRange">{label}</label>
|
||||||
<select name="timeRange" disabled={disabled} bind:value={selected}>
|
<select name="timeRange" disabled={disabled} bind:value={selected}>
|
||||||
{#each options as option (option.id)}
|
{#each rangeOptions as option (option.name)}
|
||||||
<option value={option.id}>{option.label}</option>
|
<option value={option.name}>{option.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
|
<option value="Specific Dates">Specific Dates</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{#if selected === "custom"}
|
{#if value.name === "Specific Dates"}
|
||||||
<div>
|
<div>
|
||||||
<label for="startTime">Start Time</label>
|
<label for="startTime">Start Time</label>
|
||||||
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={fromValue} />
|
<input disabled={disabled} name="startTime" type="datetime-local" bind:value={customMin} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="endTime">End Time</label>
|
<label for="endTime">End Time</label>
|
||||||
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={toValue} />
|
<input disabled={disabled} name="endTime" type="datetime-local" bind:value={customMax} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import OptionRow from "./OptionRow.svelte";
|
import OptionRow from "./OptionRow.svelte";
|
||||||
import ParentEntry from "./ParentEntry.svelte";
|
import ParentEntry from "./ParentEntry.svelte";
|
||||||
import Progress from "./Progress.svelte";
|
import Progress from "./Progress.svelte";
|
||||||
import ProgressNumbers from "./ProgressNumbers.svelte";
|
import ProgressNumbers from "./ProgressNumbers.svelte";
|
||||||
import TimeProgress from "./TimeProgress.svelte";
|
import TimeProgress from "./TimeProgress.svelte";
|
||||||
|
|
||||||
export let goal: GoalResult = null;
|
export let goal: GoalResult = null;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
import type { ProjectResult } from "../models/project";
|
import type { ProjectResult } from "../models/project";
|
||||||
import type { GoalResult } from "../models/goal";
|
import type { GoalResult } from "../models/goal";
|
||||||
|
|
||||||
export let project: ProjectResult | undefined | null;
|
export let project: ProjectResult | undefined | null = void(0);
|
||||||
export let goal: GoalResult | undefined | null;
|
export let goal: GoalResult | undefined | null = void(0);
|
||||||
|
|
||||||
let progressAmount: number = 0;
|
let progressAmount: number = 0;
|
||||||
let progressTarget: number = 0;
|
let progressTarget: number = 0;
|
||||||
|
|||||||
@@ -3,14 +3,15 @@
|
|||||||
import Modal from "../components/Modal.svelte";
|
import Modal from "../components/Modal.svelte";
|
||||||
import modalStore from "../stores/modal";
|
import modalStore from "../stores/modal";
|
||||||
import type { GoalResult } from "../models/goal";
|
import type { GoalResult } from "../models/goal";
|
||||||
import { formatFormTime, nextMonth } from "../utils/time";
|
import { nextMonth } from "../utils/time";
|
||||||
import GroupSelect from "../components/GroupSelect.svelte";
|
import GroupSelect from "../components/GroupSelect.svelte";
|
||||||
import markStale from "../stores/markStale";
|
import markStale from "../stores/markStale";
|
||||||
import Checkbox from "../components/Checkbox.svelte";
|
import Checkbox from "../components/Checkbox.svelte";
|
||||||
import GroupItemSelect from "../components/GroupItemSelect.svelte";
|
import GroupItemSelect from "../components/GroupItemSelect.svelte";
|
||||||
import groupStore from "../stores/group";
|
import groupStore from "../stores/group";
|
||||||
import type { GroupResult } from "../models/group";
|
import type { GroupResult } from "../models/group";
|
||||||
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
||||||
|
import { allOffsets, rangeFromDates } from "../utils/date-range";
|
||||||
|
|
||||||
export let deletion = false;
|
export let deletion = false;
|
||||||
export let creation = false;
|
export let creation = false;
|
||||||
@@ -48,8 +49,7 @@ import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
|||||||
let unweighted = goal.unweighted;
|
let unweighted = goal.unweighted;
|
||||||
let itemId = goal.itemId || "";
|
let itemId = goal.itemId || "";
|
||||||
let compositionMode = goal.compositionMode;
|
let compositionMode = goal.compositionMode;
|
||||||
let startTime = formatFormTime(goal.startTime);
|
let range = rangeFromDates(new Date(goal.startTime), new Date(goal.endTime), allOffsets);
|
||||||
let endTime = formatFormTime(goal.endTime);
|
|
||||||
let taskFilter = goal.taskFilter || "";
|
let taskFilter = goal.taskFilter || "";
|
||||||
let itemFilter = goal.itemFilter || "";
|
let itemFilter = goal.itemFilter || "";
|
||||||
|
|
||||||
@@ -59,11 +59,11 @@ import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
|||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
loading = true;
|
loading = true;
|
||||||
|
const [startTime, endTime] = range.calculate(new Date());
|
||||||
|
|
||||||
if (creation) {
|
if (creation) {
|
||||||
stuffLogClient.createGoal({
|
stuffLogClient.createGoal({
|
||||||
startTime: new Date(startTime),
|
startTime, endTime,
|
||||||
endTime: new Date(endTime),
|
|
||||||
itemId: itemId || null,
|
itemId: itemId || null,
|
||||||
taskFilter: taskFilter.toLowerCase() || null,
|
taskFilter: taskFilter.toLowerCase() || null,
|
||||||
itemFilter: itemFilter.toLowerCase() || null,
|
itemFilter: itemFilter.toLowerCase() || null,
|
||||||
@@ -87,8 +87,7 @@ import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
stuffLogClient.updateGoal(goal.id, {
|
stuffLogClient.updateGoal(goal.id, {
|
||||||
startTime: new Date(startTime),
|
startTime, endTime,
|
||||||
endTime: new Date(endTime),
|
|
||||||
itemId: itemId || null,
|
itemId: itemId || null,
|
||||||
clearItemId: itemId === "",
|
clearItemId: itemId === "",
|
||||||
taskFilter: taskFilter.toLowerCase() || null,
|
taskFilter: taskFilter.toLowerCase() || null,
|
||||||
@@ -134,7 +133,7 @@ import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
|||||||
<option value="task" selected={"task" === compositionMode}>Task</option>
|
<option value="task" selected={"task" === compositionMode}>Task</option>
|
||||||
<option value="project" selected={"project" === compositionMode}>Project</option>
|
<option value="project" selected={"project" === compositionMode}>Project</option>
|
||||||
</select>
|
</select>
|
||||||
<DateRangeSelect disabled={deletion} bind:fromValue={startTime} bind:toValue={endTime} />
|
<DateRangeSelect disabled={deletion} bind:value={range} />
|
||||||
<label for="taskFilter">Task Filter (Optional)</label>
|
<label for="taskFilter">Task Filter (Optional)</label>
|
||||||
<input disabled={deletion} name="taskFilter" type="text" bind:value={taskFilter} />
|
<input disabled={deletion} name="taskFilter" type="text" bind:value={taskFilter} />
|
||||||
<label for="itemFilter">Item Filter (Optional)</label>
|
<label for="itemFilter">Item Filter (Optional)</label>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
||||||
|
|
||||||
import EmptyList from "../components/EmptyList.svelte";
|
import EmptyList from "../components/EmptyList.svelte";
|
||||||
import EmptyParentEntry from "../components/EmptyParentEntry.svelte";
|
import EmptyParentEntry from "../components/EmptyParentEntry.svelte";
|
||||||
|
import EveryMinute from "../components/EveryMinute.svelte";
|
||||||
import GoalEntry from "../components/GoalEntry.svelte";
|
import GoalEntry from "../components/GoalEntry.svelte";
|
||||||
import LogEntry from "../components/LogEntry.svelte";
|
import LogEntry from "../components/LogEntry.svelte";
|
||||||
import ParentEntry from "../components/ParentEntry.svelte";
|
import ParentEntry from "../components/ParentEntry.svelte";
|
||||||
@@ -11,18 +14,19 @@ import ParentEntry from "../components/ParentEntry.svelte";
|
|||||||
import { fpLogStore } from "../stores/logs";
|
import { fpLogStore } from "../stores/logs";
|
||||||
import { fpProjectStore, fpProjectStore2 } from "../stores/project";
|
import { fpProjectStore, fpProjectStore2 } from "../stores/project";
|
||||||
import { fpTaskStore } from "../stores/tasks";
|
import { fpTaskStore } from "../stores/tasks";
|
||||||
|
import { RelativeDateRange } from "../utils/date-range";
|
||||||
import { endOfDay, startOfDay } from "../utils/time";
|
import { endOfDay, startOfDay } from "../utils/time";
|
||||||
|
|
||||||
let fakeMap: {[projectId: string]: boolean} = {}
|
let fakeMap: {[projectId: string]: boolean} = {}
|
||||||
let fakeProjects: ProjectResult[]
|
let fakeProjects: ProjectResult[]
|
||||||
let sortedProjects: ProjectResult[]
|
let sortedProjects: ProjectResult[]
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
if ($fpGoalStore.stale && !$fpGoalStore.loading) {
|
if ($fpGoalStore.stale && !$fpGoalStore.loading) {
|
||||||
fpGoalStore.load({
|
const [minTime, maxTime] = (new RelativeDateRange(3, "day")).calculate(now);
|
||||||
maxTime: new Date(Date.now() + (86400000 * 3)),
|
|
||||||
minTime: new Date(),
|
fpGoalStore.load({minTime, maxTime});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +61,8 @@ import ParentEntry from "../components/ParentEntry.svelte";
|
|||||||
$: {
|
$: {
|
||||||
if ($fpLogStore.stale && !$fpLogStore.loading) {
|
if ($fpLogStore.stale && !$fpLogStore.loading) {
|
||||||
fpLogStore.load({
|
fpLogStore.load({
|
||||||
maxTime: endOfDay(new Date()),
|
maxTime: endOfDay(now),
|
||||||
minTime: startOfDay(new Date()),
|
minTime: startOfDay(now),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +104,7 @@ import ParentEntry from "../components/ParentEntry.svelte";
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
<EveryMinute bind:now={now} />
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{#if !$fpGoalStore.loading || $fpGoalStore.goals.length > 0}
|
{#if !$fpGoalStore.loading || $fpGoalStore.goals.length > 0}
|
||||||
<h1>Active Goals</h1>
|
<h1>Active Goals</h1>
|
||||||
|
|||||||
@@ -4,17 +4,20 @@
|
|||||||
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";
|
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
||||||
|
import { allOffsets, pastOffsets } from "../utils/date-range";
|
||||||
|
import EveryMinute from "../components/EveryMinute.svelte";
|
||||||
|
|
||||||
|
const lsValue = localStorage.getItem("sl2.goalspage.range")
|
||||||
|
|
||||||
const mdGoalAdd: ModalData = {name: "goal.add"};
|
const mdGoalAdd: ModalData = {name: "goal.add"};
|
||||||
|
let range = allOffsets.find(o => o.name === lsValue) || pastOffsets[0];
|
||||||
|
let now = new Date();
|
||||||
|
|
||||||
let minTime = formatFormTime($goalStore.filter.minTime || startOfWeek(new Date()));
|
$: localStorage.setItem("sl2.goalspage.range", range.name);
|
||||||
let maxTime = formatFormTime($goalStore.filter.maxTime || endOfWeek(new Date()));
|
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
const min = new Date(minTime);
|
const [min, max] = range.calculate(now);
|
||||||
const max = new Date(maxTime);
|
|
||||||
|
|
||||||
if (!$goalStore.loading) {
|
if (!$goalStore.loading) {
|
||||||
if ($goalStore.stale || $goalStore.filter.minTime?.getTime() != min.getTime() || $goalStore.filter.maxTime?.getTime() != max.getTime()) {
|
if ($goalStore.stale || $goalStore.filter.minTime?.getTime() != min.getTime() || $goalStore.filter.maxTime?.getTime() != max.getTime()) {
|
||||||
@@ -28,7 +31,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<DateRangeSelect label="Time Filter" styled bind:fromValue={minTime} bind:toValue={maxTime} />
|
<DateRangeSelect label="Time Filter" styled bind:value={range} />
|
||||||
<div class="goal-list" class:loading={$goalStore.loading}>
|
<div class="goal-list" class:loading={$goalStore.loading}>
|
||||||
{#each $goalStore.goals as goal (goal.id)}
|
{#each $goalStore.goals as goal (goal.id)}
|
||||||
<GoalEntry showAllOptions goal={goal} />
|
<GoalEntry showAllOptions goal={goal} />
|
||||||
@@ -37,6 +40,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<RefreshSelection />
|
<RefreshSelection />
|
||||||
|
<EveryMinute now={now} />
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div.page {
|
div.page {
|
||||||
|
|||||||
@@ -7,18 +7,24 @@
|
|||||||
import RefreshSelection from "../components/RefreshSelection.svelte";
|
import RefreshSelection from "../components/RefreshSelection.svelte";
|
||||||
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
import DateRangeSelect from "../components/DateRangeSelect.svelte";
|
||||||
import EveryMinute from "../components/EveryMinute.svelte";
|
import EveryMinute from "../components/EveryMinute.svelte";
|
||||||
|
import { allOffsets, RelativeDateRange } from "../utils/date-range";
|
||||||
|
|
||||||
|
const lsValue = localStorage.getItem("sl2.logspage.range")
|
||||||
|
|
||||||
let groupedLogs: {day: Date, text: string, logs: LogResult[]}[] = [];
|
let groupedLogs: {day: Date, text: string, logs: LogResult[]}[] = [];
|
||||||
let minTime = formatFormTime($logStore.filter.minTime || startOfDay(new Date(Date.now() - 86400000 * 7)));
|
let range = allOffsets.find(o => o.name === lsValue) || new RelativeDateRange(-7, "day");
|
||||||
let maxTime = formatFormTime($logStore.filter.maxTime || endOfDay(new Date()));
|
|
||||||
let emptyMessage = `No logs since ${formatWeekdayDate(minTime)}.`;
|
|
||||||
let error = "";
|
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
|
let error = "";
|
||||||
|
let emptyMessage = `No logs since ${formatWeekdayDate(range.calculate(now)[0])}.`;
|
||||||
|
|
||||||
|
let min: Date;
|
||||||
|
let max: Date;
|
||||||
|
|
||||||
|
$: [min, max] = range.calculate(now);
|
||||||
|
|
||||||
|
$: localStorage.setItem("sl2.logspage.range", range.name);
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
const min = new Date(minTime);
|
|
||||||
const max = new Date(maxTime);
|
|
||||||
|
|
||||||
error = "";
|
error = "";
|
||||||
|
|
||||||
if (!Number.isNaN(min.getTime()) && !Number.isNaN(max.getTime())) {
|
if (!Number.isNaN(min.getTime()) && !Number.isNaN(max.getTime())) {
|
||||||
@@ -41,7 +47,7 @@
|
|||||||
|
|
||||||
$: {
|
$: {
|
||||||
if (!$logStore.loading && !$logStore.stale) {
|
if (!$logStore.loading && !$logStore.stale) {
|
||||||
emptyMessage = `No logs between ${formatWeekdayDate(minTime)} and ${formatWeekdayDate(maxTime)}.`;
|
emptyMessage = `No logs between ${formatWeekdayDate(min)} and ${formatWeekdayDate(max)}.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +100,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<DateRangeSelect label="Time Filter" noFuture styled bind:fromValue={minTime} bind:toValue={maxTime} />
|
<DateRangeSelect label="Time Filter" noFuture styled bind:value={range} />
|
||||||
<div class="error">{error}</div>
|
<div class="error">{error}</div>
|
||||||
<div class="log-list" class:loading={$logStore.loading}>
|
<div class="log-list" class:loading={$logStore.loading}>
|
||||||
{#each groupedLogs as logGroup (logGroup.day.getTime())}
|
{#each groupedLogs as logGroup (logGroup.day.getTime())}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import pluralize from "pluralize";
|
||||||
|
|
||||||
|
import {
|
||||||
|
addDays, addMonths, addYears,
|
||||||
|
endOfDay, endOfMonth, endOfWeek, endOfYear,
|
||||||
|
startOfDay, startOfMonth, startOfWeek, startOfYear
|
||||||
|
} from "./time";
|
||||||
|
|
||||||
|
export interface DateRange {
|
||||||
|
name: string
|
||||||
|
calculate(date: Date): [Date, Date]
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateRangeCallback(name: string, calculate: (date: Date) => [Date, Date]): DateRange {
|
||||||
|
return {name, calculate};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RelativeDateRange {
|
||||||
|
private duration: number
|
||||||
|
private unit: "day" | "month" | "year"
|
||||||
|
public name: string
|
||||||
|
|
||||||
|
constructor(duration: number, unit: "day" | "month" | "year") {
|
||||||
|
this.duration = duration;
|
||||||
|
this.unit = unit;
|
||||||
|
|
||||||
|
if (this.duration < 0) {
|
||||||
|
this.name = `Last ${-this.duration} ${pluralize(this.unit, -this.duration)}`;
|
||||||
|
} else {
|
||||||
|
this.name = `Next ${this.duration} ${pluralize(this.unit, this.duration)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate(date: Date): [Date, Date] {
|
||||||
|
switch (this.unit) {
|
||||||
|
case "day": {
|
||||||
|
return this.duration < 0
|
||||||
|
? [ startOfDay(addDays(date, this.duration)), endOfDay(date) ]
|
||||||
|
: [ startOfDay(date), endOfDay(addDays(date, this.duration)) ];
|
||||||
|
}
|
||||||
|
case "month": {
|
||||||
|
return this.duration < 0
|
||||||
|
? [ startOfDay(addMonths(date, this.duration)), endOfDay(date) ]
|
||||||
|
: [ startOfDay(date), endOfDay(addMonths(date, this.duration)) ];
|
||||||
|
}
|
||||||
|
case "year": {
|
||||||
|
return this.duration < 0
|
||||||
|
? [ startOfDay(addYears(date, this.duration)), endOfDay(date) ]
|
||||||
|
: [ startOfDay(date), endOfDay(addYears(date, this.duration)) ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function customDateRange(from: Date, to: Date): DateRange {
|
||||||
|
return {name: "Specific Dates", calculate: () => [from, to]}
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisWeek = dateRangeCallback("This week", date => [startOfWeek(date), endOfWeek(date)]);
|
||||||
|
const nextWeek = dateRangeCallback("Next week", date => [startOfWeek(addDays(date, 7)), endOfWeek(addDays(date, 7))]);
|
||||||
|
const lastWeek = dateRangeCallback("Last week", date => [startOfWeek(addDays(date, -7)), endOfWeek(addDays(date, -7))]);
|
||||||
|
|
||||||
|
const thisMonth = dateRangeCallback("This month", date => [startOfMonth(date), endOfMonth(date)]);
|
||||||
|
const nextMonth = dateRangeCallback("Next month", date => [startOfMonth(addMonths(date, 1)), endOfMonth(addMonths(date, 1))]);
|
||||||
|
const lastMonth = dateRangeCallback("Last month", date => [startOfMonth(addMonths(date, -1)), endOfMonth(addMonths(date, -1))]);
|
||||||
|
|
||||||
|
const thisYear = dateRangeCallback("This year", date => [startOfYear(date), endOfYear(date)]);
|
||||||
|
const nextYear = dateRangeCallback("Next year", date => [startOfYear(addYears(date, 1)), endOfYear(addYears(date, 1))]);
|
||||||
|
const lastYear = dateRangeCallback("Last year", date => [startOfYear(addYears(date, -1)), endOfYear(addYears(date, -1))]);
|
||||||
|
|
||||||
|
export const allOffsets: DateRange[] = [
|
||||||
|
thisWeek,
|
||||||
|
nextWeek,
|
||||||
|
new RelativeDateRange(7, "day"),
|
||||||
|
lastWeek,
|
||||||
|
new RelativeDateRange(-7, "day"),
|
||||||
|
thisMonth,
|
||||||
|
nextMonth,
|
||||||
|
new RelativeDateRange(30, "day"),
|
||||||
|
new RelativeDateRange(90, "day"),
|
||||||
|
lastMonth,
|
||||||
|
new RelativeDateRange(-30, "day"),
|
||||||
|
new RelativeDateRange(-90, "day"),
|
||||||
|
thisYear,
|
||||||
|
nextYear,
|
||||||
|
new RelativeDateRange(1, "year"),
|
||||||
|
lastYear,
|
||||||
|
new RelativeDateRange(-1, "year"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export function rangeFromDates(from: Date, to: Date, options: DateRange[]): DateRange {
|
||||||
|
for (const option of options) {
|
||||||
|
const [from2, to2] = option.calculate(new Date());
|
||||||
|
if (from2.getTime() == from.getTime() && to2.getTime() === to.getTime()) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return customDateRange(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pastOffsets: DateRange[] = allOffsets.filter(r => !r.name.startsWith("Next"));
|
||||||
@@ -110,4 +110,16 @@ export function addDays(date: Date, days: number): Date {
|
|||||||
const newDate = new Date(date.getTime());
|
const newDate = new Date(date.getTime());
|
||||||
newDate.setDate(newDate.getDate() + days);
|
newDate.setDate(newDate.getDate() + days);
|
||||||
return newDate;
|
return newDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addMonths(date: Date, months: number): Date {
|
||||||
|
const newDate = new Date(date.getTime());
|
||||||
|
newDate.setMonth(newDate.getMonth() + months);
|
||||||
|
return newDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addYears(date: Date, years: number): Date {
|
||||||
|
const newDate = new Date(date.getTime());
|
||||||
|
newDate.setFullYear(newDate.getFullYear() + years);
|
||||||
|
return newDate;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user