fix(calendar): en tom kalender skal sige hvorfor, ikke bare være tom

At vælge en kalender er nogen, der siger "vis mig den her". At gemme valget og
lade gitteret stå tomt, til fejeren kommer forbi et kvarter senere, er en
kalender, der påstår, at fjorten dage er fri — og personen står på siden med
knappen under hånden. Gemningen henter nu selv.

Og er der forbundet en konto uden at der er valgt nogen kalendere, siger
visningen dét. "Der er ikke valgt en kalender" og "der er ikke booket noget"
ser ens ud, og kun den ene er noget at gøre noget ved.
This commit is contained in:
Kristian Wind
2026-08-20 07:27:42 +02:00
parent 1b1e57df29
commit 031bb18067
5 changed files with 39 additions and 3 deletions
+9 -1
View File
@@ -1072,7 +1072,9 @@ test('kalenderen viser Googles begivenheder over egne opgaver, og kun opgaven ka
read_only: true,
has_client: true,
redirect_uri: 'http://localhost/oauth/calendar/callback',
calendars: []
calendars: [
{ id: 'c1', remote_id: 'fam', name: 'Arbejde', colour: '#0b8043', shown: true }
]
}
})
);
@@ -1109,6 +1111,12 @@ test('kalenderen viser Googles begivenheder over egne opgaver, og kun opgaven ka
}
await expect(cell, `uge-visningen nåede aldrig frem til ${day}`).toBeVisible();
// Connected, with a calendar ticked — so neither of the two "there is nothing
// here" notices belongs on the screen. They exist so an empty grid is never
// unexplained, and an explanation for a state you are not in is noise.
await expect(page.getByText('Ingen kalender er forbundet endnu.')).toBeHidden();
await expect(page.getByText('Ingen af kontoens kalendere er valgt endnu.')).toBeHidden();
// Both kinds are in the same cell, which is the whole point of the view.
const event = cell.locator('.event').filter({ hasText: 'Bestyrelsesmøde' });
const allDay = cell.locator('.event').filter({ hasText: 'Feriedag' });
+1
View File
@@ -630,6 +630,7 @@ export const da = {
'cal.notConnected':
'Ingen kalender er forbundet endnu. Det gøres under Indstillinger → Integrationer.',
'cal.settings': 'Indstillinger',
'cal.noneChosen': 'Ingen af kontoens kalendere er valgt endnu.',
'cal.allDay': 'Hele dagen',
'cal.openInGoogle': 'Åbn i Google Kalender',
'cal.beyondWindow':
+1
View File
@@ -623,6 +623,7 @@ export const en = {
'cal.title': 'Calendar',
'cal.notConnected': 'No calendar is connected yet. That is done under Settings → Integrations.',
'cal.settings': 'Settings',
'cal.noneChosen': 'None of the account’s calendars are chosen yet.',
'cal.allDay': 'All day',
'cal.openInGoogle': 'Open in Google Calendar',
'cal.beyondWindow':
@@ -197,8 +197,19 @@
calendarsSaved = false;
try {
await api.setCalendars(shownCalendars);
await loadCalendar();
calendarsSaved = true;
// And fetch, without being asked again.
//
// Ticking a calendar is somebody saying "show me this one". Saving the
// choice and leaving the grid empty until the sweep comes round a quarter
// of an hour later is a calendar that says a fortnight is clear — and the
// person is on this page, watching, with the button under their hand.
// Also what refreshes the panel: syncCalendarNow reloads it when it is done.
if (shownCalendars.length) {
await syncCalendarNow();
} else {
await loadCalendar();
}
} catch (e) {
app.toast(humanMessage(e));
} finally {
+16 -1
View File
@@ -22,13 +22,18 @@
/** Null until the first answer, so nothing flashes before it is known. */
let window_ = $state(null);
let connected = $state(null);
/** Whether any calendar in the connected account is ticked. */
let anyShown = $state(false);
/** The dates the grid is showing, so the notice below can compare against them. */
let showing = $state(null);
$effect(() => {
api
.getCalendar()
.then((status) => (connected = status.connected))
.then((status) => {
connected = status.connected;
anyShown = (status.calendars ?? []).some((c) => c.shown);
})
.catch(() => {
// A calendar that could not be asked about is not a reason to withhold
// the grid: the tasks in it are verdande's own and do not depend on
@@ -101,6 +106,16 @@
</p>
{/if}
<!-- Connected, and nothing ticked. Worth its own sentence rather than an empty
grid: "no calendar is chosen" and "nothing is booked" look identical, and
only one of them is something to do about. -->
{#if connected && !anyShown}
<p class="hint">
{t('cal.noneChosen')}
<a href="/indstillinger/integrationer">{t('cal.settings')}</a>
</p>
{/if}
{#if beyond}
<p class="hint">{t('cal.beyondWindow', { from: window_.from, to: window_.to })}</p>
{/if}