feat(upcoming): look up to 30 days ahead, and choose how far
Kommendes liste has always been the next seven days and nothing else. Seven days is the right walk to take in the morning, and it is still what the view opens on — but "when does this month get busy" is a different question, and the month grid answers it with shape rather than in order. So the list now takes a horizon: 7, 14 or 30 days, chosen with a switcher beside the view buttons. Only the list gets one. A week grid is a week and a month grid is a month; there is no fortnight for them to show, so the switcher is hidden in those modes. The choice lives in localStorage next to the mode, under its own key. It is the same kind of choice as list-or-grid — how you want to look, not a fact about the work — and it should survive a reload without following you to a phone where thirty day headings is a very long scroll. Once the strip is longer than a week the day headings carry the week number. Thirty days repeat every weekday four times, and "mandag 6. okt" alone does not say which mandag. Server side the endpoint already took `?days=` and capped it at 31, so the window needed nothing — but its row limit was a constant 1000 written for a week. A month asked for with a week's ceiling would truncate the last days silently, and a day cut off the end of the answer looks exactly like a day with nothing due, so the limit now follows the window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SA1TDmgQZw3AufJqkbwezx
This commit is contained in:
co-authored by
Claude Opus 5
parent
f60758b770
commit
094ea5cdd8
@@ -1,6 +1,6 @@
|
||||
<script>
|
||||
/**
|
||||
* Kommende: the next seven days as a list, one week as a grid, or the month.
|
||||
* Kommende: a run of days as a list, one week as a grid, or the month.
|
||||
*
|
||||
* The views ask for different things and none is a subset of the others — seven
|
||||
* days from today, against a chosen week, against six whole weeks around a
|
||||
@@ -15,7 +15,7 @@
|
||||
* A week that straddles the 31st and the 1st has both days in the same row.
|
||||
*/
|
||||
import { api } from '$lib/api.js';
|
||||
import { app, upcomingView } from '$lib/stores.svelte.js';
|
||||
import { app, upcomingView, UPCOMING_DAYS } from '$lib/stores.svelte.js';
|
||||
import { TASK, startDrag, carries, dragged, accept } from '$lib/dnd.js';
|
||||
import TaskRow from '$lib/components/TaskRow.svelte';
|
||||
import QuickAdd from '$lib/components/QuickAdd.svelte';
|
||||
@@ -24,9 +24,12 @@
|
||||
|
||||
let days = $state([]);
|
||||
|
||||
// Reads `upcomingView.days` inside the effect on purpose: that is what makes
|
||||
// choosing a longer horizon reload, rather than extending the strip with days
|
||||
// nobody asked the server about.
|
||||
$effect(() => {
|
||||
if (upcomingView.mode !== 'list') return;
|
||||
api.upcoming().then((data) => {
|
||||
api.upcoming(upcomingView.days).then((data) => {
|
||||
days = data.days;
|
||||
app.tasks = data.days.flatMap((d) => d.tasks);
|
||||
});
|
||||
@@ -53,6 +56,21 @@
|
||||
return day.toLocaleDateString(tag(), { weekday: 'long', day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
||||
// A month of headings repeats every weekday four times, and "mandag 6. okt"
|
||||
// alone does not say which mandag. The week number is the label that does, and
|
||||
// it only earns its place once the strip is longer than a week.
|
||||
function weekOf(date) {
|
||||
if (upcomingView.days <= 7) return null;
|
||||
const day = new Date(date + 'T00:00:00');
|
||||
// ISO weeks: Thursday decides which year and which week a row belongs to.
|
||||
const thursday = new Date(day);
|
||||
thursday.setDate(day.getDate() - ((day.getDay() + 6) % 7) + 3);
|
||||
const firstThursday = new Date(thursday.getFullYear(), 0, 4);
|
||||
firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3);
|
||||
const n = 1 + Math.round((thursday - firstThursday) / (7 * 86400000));
|
||||
return t('view.weekNumber', { n });
|
||||
}
|
||||
|
||||
// Reading tasks back out of the store keeps a completed row disappearing
|
||||
// immediately rather than on the next load. It is also what makes a task
|
||||
// dragged from one day to another land in its new section straight away: the
|
||||
@@ -82,6 +100,19 @@
|
||||
<div class="view" class:wide={upcomingView.mode !== 'list'}>
|
||||
<header>
|
||||
<h1>{t('view.upcoming')}</h1>
|
||||
{#if upcomingView.mode === 'list'}
|
||||
<!-- Only the list takes a horizon. A week grid is a week and a month grid
|
||||
is a month; there is no fortnight for them to show. -->
|
||||
<div class="views" role="group" aria-label={t('view.horizon')}>
|
||||
{#each UPCOMING_DAYS as n}
|
||||
<button
|
||||
class:active={upcomingView.days === n}
|
||||
onclick={() => upcomingView.setDays(n)}
|
||||
aria-pressed={upcomingView.days === n}>{t('view.days', { n })}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="views" role="group" aria-label={t('view.mode')}>
|
||||
{#each [['list', t('view.list')], ['week', t('view.week')], ['calendar', t('view.month')]] as [value, label]}
|
||||
<button
|
||||
@@ -102,6 +133,7 @@
|
||||
{:else}
|
||||
{#each days as day (day.date)}
|
||||
{@const tasks = live(day.date)}
|
||||
{@const week = weekOf(day.date)}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<section
|
||||
class:over={over === day.date}
|
||||
@@ -109,7 +141,10 @@
|
||||
ondragleave={() => (over = null)}
|
||||
ondrop={(e) => onDrop(e, day.date)}
|
||||
>
|
||||
<h2>{heading(day.date)}</h2>
|
||||
<h2>
|
||||
{heading(day.date)}
|
||||
{#if week}<span class="week">{week}</span>{/if}
|
||||
</h2>
|
||||
{#if tasks.length}
|
||||
{#each tasks as task (task.id)}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
@@ -210,6 +245,10 @@
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: var(--s2);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 560;
|
||||
text-transform: uppercase;
|
||||
@@ -220,6 +259,14 @@
|
||||
margin-bottom: var(--s2);
|
||||
}
|
||||
|
||||
/* Lighter than the day it sits beside: it is there to be found when you are
|
||||
lost in the fourth week, not read on every row. */
|
||||
.week {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* An em dash rather than "ingen opgaver": seven repetitions of a sentence is
|
||||
noise, and the reader only needs to know the day is clear. */
|
||||
.empty {
|
||||
|
||||
Reference in New Issue
Block a user