diff --git a/.claude/settings.json b/.claude/settings.json index 1a86153..1f5b896 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,4 +1,18 @@ { + "permissions": { + "allow": [ + "Bash(git *)", + "Bash(gh *)", + "Bash(npm *)", + "Bash(node *)", + "Bash(ls *)", + "Bash(find *)", + "Bash(grep *)", + "Bash(cat *)", + "Bash(jq *)", + "Bash(which *)" + ] + }, "hooks": { "PostToolUse": [ { diff --git a/BACKLOG.md b/BACKLOG.md index 159cf9c..43f910c 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -63,3 +63,26 @@ New suggestion? → [Open an issue](https://github.com/ulsklyc/oikos/issues/new? | - | Calendar event location display with RFC 5545 backslash-escape normalization (`fmtLocation()`) | v0.23.0 | | - | Tasks: filter defaults to `status: open`; effective due date sort; due chip shows time component | v0.23.0 | | - | Dashboard: FAB shortcuts open new-item modal directly after navigation | v0.23.0 | +| - | Budget: DB-backed expense categories (stable slug keys), subcategories (35 predefined + custom), CSV export with subcategory column | v0.24.0 | +| - | i18n: all 14 non-German locales extended with budget category & subcategory keys | v0.24.0 | +| - | Server-side log messages and API error strings translated to English — contributed by @rafaelfoster | v0.24.0 | +| - | UX/Accessibility: skip-to-content link, modal focus fix, swipe-to-close, 44 px touch targets, unique SVG gradient IDs | v0.24.1 | +| - | API tokens: named Bearer / X-API-Key tokens for external integrations; SHA-256-hashed, expiry, revocation, last-used tracking | v0.25.0 | +| - | OpenAPI 3.0 specification at `/api/v1/openapi.json` | v0.25.0 | +| - | SPA router: dynamic imports, module cache, directional transitions, per-module accent theming | v0.25.x | +| - | innerHTML → replaceChildren / insertAdjacentHTML migration across all modules (PR #88) | v0.25.x | +| - | Birthdays module: name, birth date, photo, notes; auto-synced to calendar (yearly recurring event) and reminders (1 day before) | v0.26.0 | +| - | Dashboard: birthdays widget, family participants widget, budget overview widget; customisable widget order | v0.26.0 | +| - | Settings › General: custom application name | v0.26.0 | +| - | Birthday image upload limit: 5 MB | v0.26.5 | +| - | Family management: family roles (Dad, Mom, Parent, Child, etc.) separate from system access role | v0.27.0 | +| - | Profile pictures: upload own avatar (PNG/JPEG/WebP, ≤ 5 MB, auto-resized to 512 px) | v0.27.0 | +| - | Admin: edit any family member's profile (name, role, picture) | v0.27.0 | +| - | API: `GET /api/v1/family/members`, `PATCH /api/v1/auth/users/:id`, `PATCH /api/v1/auth/me/profile` | v0.27.0 | +| - | Google Calendar null guard: initial OAuth sync no longer silently fails on empty item arrays | v0.27.1 | +| - | Navigation: sidebar tooltips in icon-only breakpoint; global keyboard shortcuts (`/`, `n`, `?`, `g d/t/c/s/n`) | v0.28.0 | +| - | PWA: offline connectivity banner | v0.28.0 | +| - | UX: `deleteWithUndo` for birthdays; contextual onboarding hints in all empty states | v0.28.0 | +| - | Calendar: custom event icons (102 Lucide options via visual picker); birthday events auto-assigned `cake` icon | v0.29.0 | +| - | Calendar: extended reminder presets (2 days, 1 week, 2 weeks) + fully custom reminder (number + unit) | v0.29.0 | +| - | Calendar: locale-aware date text inputs (MDY / DMY / YMD) in all date fields across Calendar, Tasks, Meals, Birthdays, Budget | v0.29.0 | diff --git a/CHANGELOG.md b/CHANGELOG.md index 408d44b..110b0eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.29.2] - 2026-04-28 + +### Changed +- Docs: SPEC updated with Reminders, Birthdays, and Family Management tables and module sections; Users table reflects `family_role` and `avatar_data` columns +- Docs: README lists Reminders and Birthdays in the feature tagline and Highlights section +- Docs: BACKLOG completed-features table brought up to date through v0.29.1 + ## [0.29.1] - 2026-04-28 ### Changed diff --git a/README.md b/README.md index 077eb10..21117dd 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Oikos

Oikos

Self-hosted family planner for small households

-

Tasks · Shopping Lists · Meal Planning · Recipes · Calendar Sync · Budget · Notes · Contacts

+

Tasks · Shopping Lists · Meal Planning · Recipes · Calendar Sync · Budget · Notes · Contacts · Birthdays · Reminders

MIT License Latest Release @@ -57,6 +57,12 @@ **Notes & Contacts:** Colored sticky notes with Markdown, contact directory with vCard import/export +**Birthdays:** Personal birthday tracker with automatic annual calendar events, age calculation, profile photos (≤ 5 MB), and 1-day-before reminders — auto-synced, no manual setup needed. + +**Reminders:** Time-based reminders on any task or calendar event, with an in-app notification badge. Birthday reminders are created automatically. + +**Family Management:** Assign family roles (Dad, Mom, Child, etc.) and upload profile pictures per family member. + **API Tokens:** Admins can create named Bearer / X-API-Key tokens for external integrations; tokens are SHA-256-hashed at rest, support optional expiry and revocation. OpenAPI 3.0 specification available at `/api/v1/openapi.json`. **Zero Build Step:** Pure ES modules, no bundler, no transpiler, no framework. Ships what you write. diff --git a/docs/SPEC.md b/docs/SPEC.md index 71fd5f1..4a1bbc4 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -15,7 +15,9 @@ Every table: `id INTEGER PRIMARY KEY`, `created_at TEXT`, `updated_at TEXT` (ISO | display_name | TEXT | | | password_hash | TEXT | bcrypt | | avatar_color | TEXT | HEX color code | +| avatar_data | TEXT | Base64 data URL of profile picture (nullable) | | role | TEXT | 'admin' or 'member' | +| family_role | TEXT | 'dad', 'mom', 'parent', 'child', 'grandparent', 'relative', 'other' (default 'other') | ### Tasks | Column | Type | Constraint | @@ -190,6 +192,31 @@ Stores instances of a recurring entry deleted by the user so they are not re-gen | month | TEXT | YYYY-MM, NOT NULL | | PRIMARY KEY | | (parent_id, month) | +### Reminders + +Per-user reminders attached to tasks or calendar events. + +| Column | Type | Constraint | +|--------|------|-----------| +| entity_type | TEXT | 'task' or 'event', NOT NULL | +| entity_id | INTEGER | FK → tasks or calendar_events, NOT NULL | +| remind_at | TEXT | ISO 8601 datetime, NOT NULL | +| dismissed | INTEGER | 0/1, default 0 | +| created_by | INTEGER | FK → Users (CASCADE delete), NOT NULL | + +### Birthdays + +Birthday records with optional profile photo and automatic calendar event + reminder. + +| Column | Type | Constraint | +|--------|------|-----------| +| name | TEXT | NOT NULL | +| birth_date | TEXT | DATE (YYYY-MM-DD), NOT NULL | +| notes | TEXT | nullable | +| photo_data | TEXT | Base64 data URL (≤ 5 MB), nullable | +| calendar_event_id | INTEGER | FK → calendar_events (SET NULL on delete), nullable | +| created_by | INTEGER | FK → Users (CASCADE delete), NOT NULL | + ### API Tokens Named Bearer / X-API-Key tokens for non-interactive external integrations. Admin-only creation and revocation. Token values are SHA-256-hashed at rest; the plaintext is shown only once after creation. @@ -349,6 +376,8 @@ User management and app configuration. Logged-in users only. - **Language:** System (follows `navigator.language`), German, English, Spanish, French, Italian, Swedish, Greek, Russian, Turkish, Chinese, Japanese, Arabic, Hindi, Portuguese - via `oikos-locale-picker` web component; switch without page reload - **API Tokens (admin):** create named Bearer / X-API-Key tokens for external integrations; the full token value is shown only once immediately after creation; tokens can be revoked at any time; support optional expiry and track last-used timestamp - **Tab navigation:** Settings is organized in seven tabs (General, Meals, Budget, Shopping, Calendar, API Tokens, Account). Sticky tab bar, active tab persists in sessionStorage, Calendar tab auto-activates after OAuth callbacks. +- **Family management (admin):** assign a `family_role` (Dad, Mom, Parent, Child, Grandparent, Relative, Other) to each user. Displayed in the family member list and profile views. +- **Profile picture:** users can upload a personal avatar (PNG/JPEG/WebP/GIF, ≤ 5 MB), stored as a Base64 data URL in `avatar_data`. Displayed alongside display name across the app. - **App info:** version, license ### Budget (`/budget`) @@ -365,6 +394,29 @@ User management and app configuration. Logged-in users only. - CSV export includes a subcategory column and English column headers - API: `GET /api/v1/budget/categories`, `GET /api/v1/budget/categories/:key/subcategories` (optional `?lang=` localisation), `POST /api/v1/budget/categories`, `POST /api/v1/budget/categories/:key/subcategories` +### Birthdays (`/birthdays`) + +Personal birthday tracker with automatic calendar integration. + +- CRUD: name, birth_date (day/month/year or day/month only for age-unknown entries), notes, photo +- Profile photo upload (PNG/JPEG/WebP/GIF, ≤ 5 MB, stored as Base64 data URL) +- **Upcoming view:** birthdays sorted by days until next occurrence; shows age when year is known +- **Calendar integration:** creating or updating a birthday automatically creates/updates a recurring annual all-day calendar event (title: "🎂 {Name}"); deleting a birthday removes the linked event +- **Automatic reminder:** a birthday reminder is synced 1 day before each occurrence (auto-dismissed when the birthday passes) +- Search filter by name +- API: `GET /api/v1/birthdays`, `GET /api/v1/birthdays/upcoming`, `GET /api/v1/birthdays/:id`, `POST /api/v1/birthdays`, `PUT /api/v1/birthdays/:id`, `DELETE /api/v1/birthdays/:id` + +### Reminders (`/reminders`) + +Time-based reminders attached to tasks or calendar events. + +- One reminder per entity (upsert — creating a new reminder replaces the previous one) +- Reminder time set via datetime picker in the task or event modal +- **Pending reminders:** polled on page load and at a fixed interval; displayed as an in-app notification badge/toast +- **Birthday reminders** auto-synced from the Birthdays module (1 day before each occurrence) +- Dismissing a reminder marks it `dismissed = 1`; dismissed reminders are not shown again +- API: `GET /api/v1/reminders/pending`, `GET /api/v1/reminders?entity_type=&entity_id=`, `POST /api/v1/reminders`, `DELETE /api/v1/reminders/:id`, `POST /api/v1/reminders/:id/dismiss` + --- ## API Documentation diff --git a/package-lock.json b/package-lock.json index e4c30e7..5f08386 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "oikos", - "version": "0.28.1", + "version": "0.29.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "oikos", - "version": "0.28.1", + "version": "0.29.2", "license": "MIT", "dependencies": { "bcrypt": "^6.0.0", diff --git a/package.json b/package.json index 1fff0e3..f5e15b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.29.1", + "version": "0.29.2", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module",