hellth-hub/apps/admin/lib/hellth/storage.ts
Sebastian Mayer 8ed7c083c4
Some checks failed
CI / Lint, Typecheck, Test, Build (push) Has been cancelled
Refactor: God-Router + Frontend-Lib in Domain-Module aufgeteilt
Router-Split (packages/api/src/trpc/routers/health/):
- health.ts (2061 Zeilen Monolith) entfernt und in 6 Sub-Router aufgeteilt:
  - dashboard.ts (overview, profile, goals, point settings)
  - foods.ts (foods, nutrition, OpenFoodFacts, food preferences)
  - recipes.ts (recipes, submissions, reviews, activity feed)
  - stores.ts (stores, change-requests, shopping lists)
  - tracking.ts (meals, drinks, weight, mealPlans, activities, leaderboard)
  - community.ts (club, wearables, coach, stats)
- index.ts mergt alle Sub-Router via mergeRouters, sodass Frontend-Aufrufe
  (trpc.health.listFoods etc.) unveraendert weiterfunktionieren.
- t.mergeRouters in init.ts exportiert.

Frontend-Lib-Split (apps/admin/lib/hellth/):
- hellth-data.ts (1149 Zeilen Monolith) in 5 Domain-Module aufgeteilt:
  - types.ts (alle TypeScript-Types)
  - constants.ts (Seeds, Defaults, todayKey, uid)
  - nutrition.ts (calcRecipe, classifyRecipe, parseNutritionLabel, ...)
  - storage.ts (load/save HellthData, ClubData, hasCompletedHealthProfile)
  - stats.ts (Budget, Stats, Coach, Shopping, Weight-Projection)
- hellth-data.ts wird zum reinen Re-Export-Index, damit bestehende
  Imports `from "@/lib/hellth-data"` unveraendert weiterfunktionieren.

Funktional identisch zum Original - reine Code-Reorganisation.
Verifiziert: 28 API-Tests + 10 Admin-Tests gruen, Typecheck (3 Packages)
gruen, Admin-Build (26 Pages) gruen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 01:09:53 +02:00

161 lines
5.2 KiB
TypeScript

import {
HELLTH_CLUB_KEY,
HELLTH_DATA_KEY,
defaultClubProfile,
defaultGoals,
defaultReminderSettings,
seedActivityTemplates,
seedFoods,
seedPlannedMeals,
seedRecipes,
seedStores,
} from "./constants";
import type { ClubData, HellthData } from "./types";
export function defaultHellthData(): HellthData {
return {
stores: seedStores,
foods: seedFoods,
recipes: seedRecipes,
recipeRatings: [],
plannedMeals: seedPlannedMeals,
shoppingItems: [],
goals: defaultGoals,
coachRecommendations: [],
reminders: defaultReminderSettings,
activityTemplates: seedActivityTemplates,
activityLogs: [],
weeklyCaloriePlans: [],
};
}
function scopedStorageKey(baseKey: string, userKey?: string | null) {
const normalized = userKey?.trim().toLowerCase();
return normalized ? `${baseKey}:${normalized}` : baseKey;
}
export function hellthDataStorageKey(userKey?: string | null) {
return scopedStorageKey(HELLTH_DATA_KEY, userKey);
}
function migrateGlobalDataToUserKey(userKey: string) {
if (typeof window === "undefined") return;
const scopedKey = hellthDataStorageKey(userKey);
if (window.localStorage.getItem(scopedKey)) return;
const globalData = window.localStorage.getItem(HELLTH_DATA_KEY);
if (globalData) {
window.localStorage.setItem(scopedKey, globalData);
}
}
export function loadHellthData(userKey?: string | null): HellthData {
if (typeof window === "undefined") return defaultHellthData();
try {
if (userKey) migrateGlobalDataToUserKey(userKey);
const raw = window.localStorage.getItem(hellthDataStorageKey(userKey));
if (!raw) return defaultHellthData();
const parsed = JSON.parse(raw) as Partial<HellthData>;
return {
stores: parsed.stores?.length ? parsed.stores : seedStores,
foods: parsed.foods?.length ? parsed.foods : seedFoods,
recipes: parsed.recipes?.length ? parsed.recipes : seedRecipes,
recipeRatings: parsed.recipeRatings ?? [],
plannedMeals: parsed.plannedMeals ?? seedPlannedMeals,
shoppingItems: parsed.shoppingItems ?? [],
goals: { ...defaultGoals, ...parsed.goals },
healthProfile: parsed.healthProfile,
coachRecommendations: parsed.coachRecommendations ?? [],
reminders: {
...defaultReminderSettings,
...parsed.reminders,
rules: {
water: { ...defaultReminderSettings.rules.water, ...parsed.reminders?.rules?.water },
snack: { ...defaultReminderSettings.rules.snack, ...parsed.reminders?.rules?.snack },
movement: { ...defaultReminderSettings.rules.movement, ...parsed.reminders?.rules?.movement },
},
},
activityTemplates: parsed.activityTemplates?.length ? parsed.activityTemplates : seedActivityTemplates,
activityLogs: parsed.activityLogs ?? [],
weeklyCaloriePlans: parsed.weeklyCaloriePlans ?? [],
};
} catch {
return defaultHellthData();
}
}
export function saveHellthData(data: HellthData, userKey?: string | null) {
if (typeof window === "undefined") return;
window.localStorage.setItem(hellthDataStorageKey(userKey), JSON.stringify(data));
}
export function hasCompletedHealthProfile(userKey?: string | null) {
return Boolean(loadHellthData(userKey).healthProfile?.completedAt);
}
import { todayKey } from "./constants";
export function defaultClubData(): ClubData {
const today = todayKey();
return {
profile: defaultClubProfile,
activities: [
{
id: "club-sebastian-walk",
title: "Eine Stunde spazieren gehen",
hostUserKey: "seed-sebastian",
hostName: "Sebastian",
category: "walk",
scope: "state",
state: "Bayern",
city: "Augsburg",
locationLabel: "Bayern",
startsAt: `${today}T18:00:00`,
durationMinutes: 60,
points: 100,
note: "Lockerer Spaziergang nach Feierabend. Jeder geht für sich, aber wir starten gemeinsam.",
participants: [
{
userKey: "seed-sebastian",
displayName: "Sebastian",
joinedAt: `${today}T10:00:00`,
},
],
},
{
id: "club-mobility",
title: "5 Minuten Dehnübungen",
hostUserKey: "seed-hellth",
hostName: "Hellth Coach",
category: "mobility",
scope: "nationwide",
state: "Bayern",
locationLabel: "Deutschlandweit",
startsAt: `${today}T15:00:00`,
durationMinutes: 5,
points: 20,
note: "Kurze Schreibtischpause. Matte nicht nötig.",
participants: [],
},
],
};
}
export function loadClubData(): ClubData {
if (typeof window === "undefined") return defaultClubData();
try {
const raw = window.localStorage.getItem(HELLTH_CLUB_KEY);
if (!raw) return defaultClubData();
const parsed = JSON.parse(raw) as Partial<ClubData>;
return {
profile: { ...defaultClubProfile, ...parsed.profile },
activities: parsed.activities?.length ? parsed.activities : defaultClubData().activities,
};
} catch {
return defaultClubData();
}
}
export function saveClubData(data: ClubData) {
if (typeof window === "undefined") return;
window.localStorage.setItem(HELLTH_CLUB_KEY, JSON.stringify(data));
}