diff --git a/public/pages/calendar.js b/public/pages/calendar.js index 5341722..12d1fb3 100644 --- a/public/pages/calendar.js +++ b/public/pages/calendar.js @@ -59,27 +59,113 @@ const EVENT_COLOR_NAMES = () => ({ '#30B0C7': t('calendar.colorCyan'), }); +const EVENT_ICON_ALIASES = { + tooth: 'drill', +}; + const EVENT_ICONS = [ { value: 'calendar', label: 'Calendar' }, - { value: 'tooth', label: 'Dentist' }, - { value: 'stethoscope', label: 'Doctor' }, - { value: 'heart-pulse', label: 'Health' }, - { value: 'briefcase', label: 'Work' }, - { value: 'plane', label: 'Travel' }, - { value: 'utensils', label: 'Meal' }, - { value: 'cake', label: 'Birthday' }, - { value: 'car', label: 'Car' }, - { value: 'graduation-cap', label: 'School' }, - { value: 'dumbbell', label: 'Sports' }, - { value: 'home', label: 'Home' }, - { value: 'shopping-bag', label: 'Shopping' }, - { value: 'music', label: 'Music' }, - { value: 'party-popper', label: 'Party' }, - { value: 'paw-print', label: 'Pet' }, - { value: 'scissors', label: 'Haircut' }, - { value: 'book-open', label: 'Reading' }, - { value: 'users', label: 'Family' }, + { value: 'drill', label: 'Dentist' }, + { value: 'alarm-clock', label: 'Alarm' }, + { value: 'clock', label: 'Time' }, { value: 'bell', label: 'Reminder' }, + { value: 'map-pin', label: 'Location' }, + { value: 'home', label: 'Home' }, + { value: 'house', label: 'House' }, + { value: 'building', label: 'Building' }, + { value: 'hospital', label: 'Hospital' }, + { value: 'stethoscope', label: 'Doctor' }, + { value: 'syringe', label: 'Vaccine' }, + { value: 'pill', label: 'Medicine' }, + { value: 'tablets', label: 'Tablets' }, + { value: 'bandage', label: 'Bandage' }, + { value: 'ambulance', label: 'Ambulance' }, + { value: 'heart-pulse', label: 'Health' }, + { value: 'activity', label: 'Activity' }, + { value: 'cross', label: 'Care' }, + { value: 'scissors', label: 'Haircut' }, + { value: 'shower-head', label: 'Personal care' }, + { value: 'dumbbell', label: 'Sports' }, + { value: 'trophy', label: 'Competition' }, + { value: 'car', label: 'Car' }, + { value: 'bus', label: 'Bus' }, + { value: 'train', label: 'Train' }, + { value: 'tram-front', label: 'Transit' }, + { value: 'fuel', label: 'Fuel' }, + { value: 'parking-meter', label: 'Parking' }, + { value: 'traffic-cone', label: 'Traffic' }, + { value: 'navigation', label: 'Navigation' }, + { value: 'route', label: 'Route' }, + { value: 'briefcase', label: 'Work' }, + { value: 'laptop', label: 'Laptop' }, + { value: 'monitor', label: 'Computer' }, + { value: 'presentation', label: 'Presentation' }, + { value: 'plane', label: 'Travel' }, + { value: 'plane-takeoff', label: 'Flight' }, + { value: 'school', label: 'School' }, + { value: 'graduation-cap', label: 'Education' }, + { value: 'book-open', label: 'Reading' }, + { value: 'library', label: 'Library' }, + { value: 'pencil', label: 'Study' }, + { value: 'notebook-pen', label: 'Notes' }, + { value: 'calculator', label: 'Calculator' }, + { value: 'utensils', label: 'Meal' }, + { value: 'cooking-pot', label: 'Cooking' }, + { value: 'coffee', label: 'Coffee' }, + { value: 'cake', label: 'Birthday' }, + { value: 'croissant', label: 'Bakery' }, + { value: 'pizza', label: 'Pizza' }, + { value: 'ice-cream', label: 'Dessert' }, + { value: 'beer', label: 'Bar' }, + { value: 'wine', label: 'Wine' }, + { value: 'popcorn', label: 'Cinema' }, + { value: 'sandwich', label: 'Snack' }, + { value: 'salad', label: 'Salad' }, + { value: 'shopping-bag', label: 'Shopping' }, + { value: 'shopping-cart', label: 'Groceries' }, + { value: 'gift', label: 'Gift' }, + { value: 'package', label: 'Package' }, + { value: 'shirt', label: 'Clothing' }, + { value: 'tag', label: 'Tag' }, + { value: 'credit-card', label: 'Card' }, + { value: 'wallet', label: 'Wallet' }, + { value: 'banknote', label: 'Cash' }, + { value: 'coins', label: 'Coins' }, + { value: 'piggy-bank', label: 'Savings' }, + { value: 'receipt', label: 'Receipt' }, + { value: 'landmark', label: 'Bank' }, + { value: 'music', label: 'Music' }, + { value: 'guitar', label: 'Guitar' }, + { value: 'film', label: 'Movie' }, + { value: 'theater', label: 'Theater' }, + { value: 'ticket', label: 'Ticket' }, + { value: 'gamepad-2', label: 'Game' }, + { value: 'camera', label: 'Photo' }, + { value: 'party-popper', label: 'Party' }, + { value: 'users', label: 'Family' }, + { value: 'baby', label: 'Baby' }, + { value: 'dog', label: 'Dog' }, + { value: 'cat', label: 'Cat' }, + { value: 'paw-print', label: 'Pet' }, + { value: 'wrench', label: 'Repair' }, + { value: 'hammer', label: 'Maintenance' }, + { value: 'paintbrush', label: 'Decoration' }, + { value: 'lightbulb', label: 'Idea' }, + { value: 'sofa', label: 'Furniture' }, + { value: 'bed', label: 'Bed' }, + { value: 'bath', label: 'Bath' }, + { value: 'washing-machine', label: 'Laundry' }, + { value: 'refrigerator', label: 'Fridge' }, + { value: 'star', label: 'Favorite' }, + { value: 'flag', label: 'Flag' }, + { value: 'target', label: 'Goal' }, + { value: 'flame', label: 'Important' }, + { value: 'leaf', label: 'Nature' }, + { value: 'tree-pine', label: 'Outdoors' }, + { value: 'flower', label: 'Flower' }, + { value: 'sun', label: 'Day' }, + { value: 'moon', label: 'Night' }, + { value: 'cloud-sun', label: 'Weather' }, ]; const HOUR_HEIGHT = 56; // px pro Stunde in Wochen-/Tagesansicht @@ -176,7 +262,8 @@ function formatDateTime(datetimeStr) { } function eventIconName(icon) { - return EVENT_ICONS.some((item) => item.value === icon) ? icon : 'calendar'; + const normalized = EVENT_ICON_ALIASES[icon] || icon; + return EVENT_ICONS.some((item) => item.value === normalized) ? normalized : 'calendar'; } function eventIconHtml(icon, className = 'event-icon') { diff --git a/public/sw.js b/public/sw.js index 34c0021..8c845b1 100644 --- a/public/sw.js +++ b/public/sw.js @@ -13,10 +13,10 @@ * → bypassCacheUntil (in-memory + Cache API für SW-Restart-Robustheit) */ -const SHELL_CACHE = 'oikos-shell-v58'; -const PAGES_CACHE = 'oikos-pages-v53'; +const SHELL_CACHE = 'oikos-shell-v59'; +const PAGES_CACHE = 'oikos-pages-v54'; const LOCALES_CACHE = 'oikos-locales-v5'; -const ASSETS_CACHE = 'oikos-assets-v53'; +const ASSETS_CACHE = 'oikos-assets-v54'; const BYPASS_CACHE = 'oikos-bypass-flag'; const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, LOCALES_CACHE, ASSETS_CACHE]; diff --git a/server/db-schema-test.js b/server/db-schema-test.js index 35fbf6c..3c21764 100644 --- a/server/db-schema-test.js +++ b/server/db-schema-test.js @@ -327,6 +327,9 @@ const MIGRATIONS_SQL = { 14: ` ALTER TABLE calendar_events ADD COLUMN icon TEXT NOT NULL DEFAULT 'calendar'; `, + 15: ` + UPDATE calendar_events SET icon = 'drill' WHERE icon = 'tooth'; + `, }; export { MIGRATIONS_SQL }; diff --git a/server/db.js b/server/db.js index 8d71504..215dd53 100644 --- a/server/db.js +++ b/server/db.js @@ -741,6 +741,13 @@ const MIGRATIONS = [ ALTER TABLE calendar_events ADD COLUMN icon TEXT NOT NULL DEFAULT 'calendar'; `, }, + { + version: 22, + description: 'Normalize calendar dentist icon', + up: ` + UPDATE calendar_events SET icon = 'drill' WHERE icon = 'tooth'; + `, + }, ]; /** diff --git a/server/routes/calendar.js b/server/routes/calendar.js index 2c8f0ad..7a5c9a6 100644 --- a/server/routes/calendar.js +++ b/server/routes/calendar.js @@ -22,10 +22,22 @@ const router = express.Router(); const VALID_SOURCES = ['local', 'google', 'apple', 'ics']; const ICS_COLOR_RE = /^#[0-9a-fA-F]{6}$/; const VALID_EVENT_ICONS = new Set([ - 'calendar', 'tooth', 'stethoscope', 'heart-pulse', 'briefcase', 'plane', - 'utensils', 'cake', 'car', 'graduation-cap', 'dumbbell', 'home', - 'shopping-bag', 'music', 'party-popper', 'paw-print', 'scissors', - 'book-open', 'users', 'bell', + 'calendar', 'drill', 'alarm-clock', 'clock', 'bell', 'map-pin', 'home', + 'house', 'building', 'hospital', 'stethoscope', 'syringe', 'pill', + 'tablets', 'bandage', 'ambulance', 'heart-pulse', 'activity', 'cross', + 'scissors', 'shower-head', 'dumbbell', 'trophy', 'car', 'bus', 'train', + 'tram-front', 'plane', 'plane-takeoff', 'fuel', 'parking-meter', + 'traffic-cone', 'navigation', 'route', 'briefcase', 'laptop', 'monitor', + 'presentation', 'school', 'graduation-cap', 'book-open', 'library', + 'pencil', 'notebook-pen', 'calculator', 'utensils', 'cooking-pot', + 'coffee', 'cake', 'croissant', 'pizza', 'ice-cream', 'beer', 'wine', + 'popcorn', 'sandwich', 'salad', 'shopping-bag', 'shopping-cart', 'gift', + 'package', 'shirt', 'tag', 'credit-card', 'wallet', 'banknote', 'coins', + 'piggy-bank', 'receipt', 'landmark', 'music', 'guitar', 'film', 'theater', + 'ticket', 'gamepad-2', 'camera', 'party-popper', 'users', 'baby', 'dog', + 'cat', 'paw-print', 'wrench', 'hammer', 'paintbrush', 'lightbulb', 'sofa', + 'bed', 'bath', 'washing-machine', 'refrigerator', 'star', 'flag', 'target', + 'flame', 'leaf', 'tree-pine', 'flower', 'sun', 'moon', 'cloud-sun', ]); function getUserId(req) { @@ -42,7 +54,8 @@ function isAdminUser(req) { } function eventIcon(value) { - const icon = typeof value === 'string' && value.trim() ? value.trim() : 'calendar'; + const raw = typeof value === 'string' && value.trim() ? value.trim() : 'calendar'; + const icon = raw === 'tooth' ? 'drill' : raw; return VALID_EVENT_ICONS.has(icon) ? icon : null; }