diff --git a/CHANGELOG.md b/CHANGELOG.md index 95ac2e0..0c24a51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.0] - 2026-04-04 + +### Added +- Optional task priority: new "None" level allows tasks without urgency, reducing visual noise for routine tasks (#15) +- "None" is now the default priority for new tasks +- Tasks with no priority hide the priority badge entirely in list and dashboard views +- DB migration v4 extends priority CHECK constraint to include 'none' +- i18n keys for "None" priority in de, en, it locales + ## [0.8.2] - 2026-04-04 ### Fixed diff --git a/package.json b/package.json index dd6944b..09b4f6e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "oikos", - "version": "0.8.2", + "version": "0.9.0", "description": "Self-hosted family planner - calendar, tasks, shopping, meal planning, budget and more. Private, open-source, no subscription.", "main": "server/index.js", "type": "module", diff --git a/public/locales/de.json b/public/locales/de.json index a6f7341..9a15f32 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -100,6 +100,7 @@ "priorityHigh": "Hoch", "priorityMedium": "Mittel", "priorityLow": "Niedrig", + "priorityNone": "Keine", "statusOpen": "Offen", "statusInProgress": "In Bearbeitung", "statusDone": "Erledigt", diff --git a/public/locales/en.json b/public/locales/en.json index be1d695..1a4d49b 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -100,6 +100,7 @@ "priorityHigh": "High", "priorityMedium": "Medium", "priorityLow": "Low", + "priorityNone": "None", "statusOpen": "Open", "statusInProgress": "In Progress", "statusDone": "Done", diff --git a/public/locales/it.json b/public/locales/it.json index 283e5b2..a7d8c17 100644 --- a/public/locales/it.json +++ b/public/locales/it.json @@ -100,6 +100,7 @@ "priorityHigh": "Alta", "priorityMedium": "Media", "priorityLow": "Bassa", + "priorityNone": "Nessuna", "statusOpen": "Aperto", "statusInProgress": "In corso", "statusDone": "Completato", diff --git a/public/pages/dashboard.js b/public/pages/dashboard.js index d5a2cdd..90ce761 100644 --- a/public/pages/dashboard.js +++ b/public/pages/dashboard.js @@ -167,7 +167,7 @@ function renderUrgentTasks(tasks) { const due = formatDueDate(t.due_date); return `
- + ${t.priority !== 'none' ? `` : ''} ${PRIORITY_LABELS()[t.priority] ?? t.priority}
${esc(t.title)}
diff --git a/public/pages/tasks.js b/public/pages/tasks.js index 54427a3..29f6237 100644 --- a/public/pages/tasks.js +++ b/public/pages/tasks.js @@ -20,6 +20,7 @@ const PRIORITIES = () => [ { value: 'high', label: t('tasks.priorityHigh'), color: 'var(--color-priority-high)' }, { value: 'medium', label: t('tasks.priorityMedium'), color: 'var(--color-priority-medium)' }, { value: 'low', label: t('tasks.priorityLow'), color: 'var(--color-priority-low)' }, + { value: 'none', label: t('tasks.priorityNone'), color: 'var(--color-priority-none)' }, ]; const STATUSES = () => [ @@ -110,6 +111,7 @@ function groupBy(tasks, mode) { // -------------------------------------------------------- function renderPriorityBadge(priority) { + if (priority === 'none') return ''; return ` ${PRIORITY_LABELS()[priority] ?? priority} @@ -254,7 +256,7 @@ function renderModalContent({ task = null, users = [] } = {}) { ).join(''); const priorityOptions = PRIORITIES().map((p) => - `` + `` ).join(''); return ` diff --git a/public/styles/tokens.css b/public/styles/tokens.css index 5eb8ef6..05420f2 100644 --- a/public/styles/tokens.css +++ b/public/styles/tokens.css @@ -114,12 +114,14 @@ /* -------------------------------------------------------- * 6. Farben - Prioritäten * -------------------------------------------------------- */ + --color-priority-none: var(--neutral-400); --color-priority-low: var(--neutral-500); --color-priority-medium: #B45309; --color-priority-high: #D4511E; --color-priority-urgent: #DC2626; /* Hintergrundfarben für Priority-Badges */ + --color-priority-none-bg: rgba(142, 141, 137, 0.08); --color-priority-low-bg: rgba(142, 141, 137, 0.12); --color-priority-medium-bg: rgba(180, 83, 9, 0.12); --color-priority-high-bg: rgba(212, 81, 30, 0.12); @@ -328,6 +330,7 @@ --meal-snack-light: #3D2010; /* Priority-Badge Hintergründe */ + --color-priority-none-bg: rgba(142, 141, 137, 0.12); --color-priority-low-bg: rgba(142, 141, 137, 0.18); --color-priority-medium-bg: rgba(230, 147, 10, 0.18); --color-priority-high-bg: rgba(212, 81, 30, 0.18); @@ -408,6 +411,7 @@ --meal-dinner-light: #1A2D4D; --meal-snack-light: #3D2010; + --color-priority-none-bg: rgba(142, 141, 137, 0.12); --color-priority-low-bg: rgba(142, 141, 137, 0.18); --color-priority-medium-bg: rgba(230, 147, 10, 0.18); --color-priority-high-bg: rgba(212, 81, 30, 0.18); diff --git a/public/sw.js b/public/sw.js index 67b618b..8ce6352 100644 --- a/public/sw.js +++ b/public/sw.js @@ -12,9 +12,9 @@ * API: Immer Netzwerk (kein Caching von Nutzerdaten) */ -const SHELL_CACHE = 'oikos-shell-v23'; -const PAGES_CACHE = 'oikos-pages-v23'; -const ASSETS_CACHE = 'oikos-assets-v23'; +const SHELL_CACHE = 'oikos-shell-v24'; +const PAGES_CACHE = 'oikos-pages-v24'; +const ASSETS_CACHE = 'oikos-assets-v24'; const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, ASSETS_CACHE]; // App-Shell: sofort benötigt für ersten Render diff --git a/server/db.js b/server/db.js index b72399c..1a1269c 100644 --- a/server/db.js +++ b/server/db.js @@ -88,8 +88,8 @@ const MIGRATIONS = [ title TEXT NOT NULL, description TEXT, category TEXT NOT NULL DEFAULT 'Sonstiges', - priority TEXT NOT NULL DEFAULT 'medium' - CHECK(priority IN ('low', 'medium', 'high', 'urgent')), + priority TEXT NOT NULL DEFAULT 'none' + CHECK(priority IN ('none', 'low', 'medium', 'high', 'urgent')), status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open', 'in_progress', 'done')), due_date TEXT, @@ -296,6 +296,41 @@ const MIGRATIONS = [ CREATE INDEX IF NOT EXISTS idx_budget_parent ON budget_entries(recurrence_parent_id); `, }, + { + version: 4, + description: 'Priorität "none" erlauben und als Default setzen', + up: ` + -- SQLite erlaubt kein ALTER CHECK, daher Tabelle neu erstellen + CREATE TABLE tasks_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + description TEXT, + category TEXT NOT NULL DEFAULT 'Sonstiges', + priority TEXT NOT NULL DEFAULT 'none' + CHECK(priority IN ('none', 'low', 'medium', 'high', 'urgent')), + status TEXT NOT NULL DEFAULT 'open' + CHECK(status IN ('open', 'in_progress', 'done')), + due_date TEXT, + due_time TEXT, + assigned_to INTEGER REFERENCES users(id) ON DELETE SET NULL, + created_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + is_recurring INTEGER NOT NULL DEFAULT 0, + recurrence_rule TEXT, + parent_task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE, + created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) + ); + + INSERT INTO tasks_new SELECT * FROM tasks; + DROP TABLE tasks; + ALTER TABLE tasks_new RENAME TO tasks; + + CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status); + CREATE INDEX IF NOT EXISTS idx_tasks_assigned ON tasks(assigned_to); + CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_id); + CREATE INDEX IF NOT EXISTS idx_tasks_due ON tasks(due_date); + `, + }, ]; /** diff --git a/server/routes/tasks.js b/server/routes/tasks.js index 6492e87..bbd156e 100644 --- a/server/routes/tasks.js +++ b/server/routes/tasks.js @@ -18,7 +18,7 @@ const router = express.Router(); // Konstanten // -------------------------------------------------------- -const VALID_PRIORITIES = ['low', 'medium', 'high', 'urgent']; +const VALID_PRIORITIES = ['none', 'low', 'medium', 'high', 'urgent']; const VALID_STATUSES = ['open', 'in_progress', 'done']; const VALID_CATEGORIES = ['Haushalt', 'Schule', 'Einkauf', 'Reparatur', 'Gesundheit', 'Finanzen', 'Freizeit', 'Sonstiges']; @@ -96,7 +96,7 @@ router.get('/', (req, res) => { ORDER BY CASE t.status WHEN 'done' THEN 1 ELSE 0 END, CASE t.priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 - WHEN 'medium' THEN 2 ELSE 3 END, + WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END, t.due_date ASC NULLS LAST, t.created_at DESC `; @@ -148,7 +148,7 @@ router.post('/', (req, res) => { title, description = null, category = 'Sonstiges', - priority = 'medium', + priority = 'none', due_date = null, due_time = null, assigned_to = null,