feat(workspace): journal with one note per day and a calendar
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m6s

- journal_days (migration 8) maps a person's day to an ordinary note;
  GET /journal lists the days, POST /journal/{date} opens or creates one
- the journal sits on top of the notes sidebar: days below each other
  (today first, older ones load while scrolling), a calendar with month
  and year picker follows the day in view and jumps to any day
- the header shares the day in view; journal notes stay out of the folder
  tree; can be switched off per device under Darstellung

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-30 19:20:58 +01:00
1 parent 261579f0f2
commit 9008d7c915
19 files changed
+864 -19

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
// Journal helpers: dates are local calendar days as YYYY-MM-DD (the server stores them as given).
export const JOURNAL_ENABLED_KEY = 'mindsift_journal_enabled';
export function isJournalEnabled(): boolean {
try {
return localStorage.getItem(JOURNAL_ENABLED_KEY) !== 'false';
} catch {
return true;
}
}
export function setJournalEnabled(on: boolean) {
try {
localStorage.setItem(JOURNAL_ENABLED_KEY, on ? 'true' : 'false');
} catch {
// storage blocked: the setting simply does not stick
}
}
export function toISODate(d: Date): string {
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${d.getFullYear()}-${m}-${day}`;
}
export function todayISO(): string {
return toISODate(new Date());
}
export function parseISODate(date: string): Date {
const [y, m, d] = date.split('-').map(Number);
return new Date(y, (m || 1) - 1, d || 1);
}
export const MONTHS = [
'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember',
];
/** "Fr, 30. Okt. 2026" for compact labels */
export function shortDateLabel(date: string): string {
return parseISODate(date).toLocaleDateString('de-DE', {
weekday: 'short',
day: 'numeric',
month: 'short',
year: 'numeric',
});
}
/** Weeks of a month for a Monday-first grid; days outside the month are null. */
export function monthGrid(year: number, month: number): (string | null)[][] {
const first = new Date(year, month, 1);
const offset = (first.getDay() + 6) % 7; // Monday = 0
const daysInMonth = new Date(year, month + 1, 0).getDate();
const cells: (string | null)[] = Array(offset).fill(null);
for (let d = 1; d <= daysInMonth; d++) cells.push(toISODate(new Date(year, month, d)));
while (cells.length % 7 !== 0) cells.push(null);
const weeks: (string | null)[][] = [];
for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
return weeks;
}