Some checks failed
CI / Lint, Typecheck, Test, Build (push) Has been cancelled
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>
184 lines
10 KiB
TypeScript
184 lines
10 KiB
TypeScript
import type {
|
|
ActivityTemplate,
|
|
CatalogFood,
|
|
ClubActivityCategory,
|
|
ClubProfile,
|
|
GermanState,
|
|
HellthGoals,
|
|
PlannedMeal,
|
|
Recipe,
|
|
ReminderSettings,
|
|
Store,
|
|
StoreDepartment,
|
|
} from "./types";
|
|
|
|
export const HELLTH_DATA_KEY = "hellth-hub-data-v1";
|
|
export const HELLTH_CLUB_KEY = "hellth-hub-club-v1";
|
|
|
|
export function todayKey() {
|
|
return new Date().toISOString().slice(0, 10);
|
|
}
|
|
|
|
export function uid(prefix = "hh") {
|
|
return `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
|
|
}
|
|
|
|
export const defaultGoals: HellthGoals = {
|
|
drinkMl: 2500,
|
|
calories: 2000,
|
|
carbs: 200,
|
|
fat: 67,
|
|
protein: 150,
|
|
weightKg: 120,
|
|
fastingHours: 12,
|
|
};
|
|
|
|
export const defaultReminderSettings: ReminderSettings = {
|
|
enabled: false,
|
|
rules: {
|
|
water: {
|
|
enabled: true,
|
|
intervalMinutes: 90,
|
|
startTime: "08:00",
|
|
endTime: "20:00",
|
|
message: "Zeit für ein Glas Wasser.",
|
|
},
|
|
snack: {
|
|
enabled: false,
|
|
intervalMinutes: 240,
|
|
startTime: "10:00",
|
|
endTime: "17:00",
|
|
message: "Prüfe kurz: brauchst du einen geplanten Snack oder reicht die nächste Mahlzeit?",
|
|
},
|
|
movement: {
|
|
enabled: true,
|
|
intervalMinutes: 60,
|
|
startTime: "09:00",
|
|
endTime: "17:00",
|
|
message: "Steh kurz auf, streck dich und geh ein paar Schritte.",
|
|
},
|
|
},
|
|
};
|
|
|
|
export const seedActivityTemplates: ActivityTemplate[] = [
|
|
{ id: "activity-stretch-5", name: "5 Minuten Dehnübungen", minutes: 5, points: 20, calories: 20, category: "mobility", icon: "stretch" },
|
|
{ id: "activity-desk-walk", name: "Schreibtischpause mit kurzem Spaziergang", minutes: 10, points: 25, calories: 35, category: "habit", icon: "walk" },
|
|
{ id: "activity-walk-30", name: "30 Minuten Spaziergang", minutes: 30, points: 60, calories: 130, category: "cardio", icon: "walk" },
|
|
{ id: "activity-run-60", name: "1 Stunde Laufen", minutes: 60, points: 100, calories: 550, category: "cardio", icon: "run" },
|
|
{ id: "activity-strength-20", name: "20 Minuten Krafttraining", minutes: 20, points: 70, calories: 140, category: "strength", icon: "strength" },
|
|
];
|
|
|
|
export const clubCategories: Array<{ key: ClubActivityCategory; label: string }> = [
|
|
{ key: "walk", label: "Spazieren" },
|
|
{ key: "run", label: "Laufen" },
|
|
{ key: "mobility", label: "Mobility" },
|
|
{ key: "strength", label: "Kraft" },
|
|
{ key: "bike", label: "Rad" },
|
|
{ key: "other", label: "Sonstiges" },
|
|
];
|
|
|
|
export const germanStates: GermanState[] = [
|
|
"Baden-Württemberg",
|
|
"Bayern",
|
|
"Berlin",
|
|
"Brandenburg",
|
|
"Bremen",
|
|
"Hamburg",
|
|
"Hessen",
|
|
"Mecklenburg-Vorpommern",
|
|
"Niedersachsen",
|
|
"Nordrhein-Westfalen",
|
|
"Rheinland-Pfalz",
|
|
"Saarland",
|
|
"Sachsen",
|
|
"Sachsen-Anhalt",
|
|
"Schleswig-Holstein",
|
|
"Thüringen",
|
|
];
|
|
|
|
export const defaultClubProfile: ClubProfile = {
|
|
state: "Bayern",
|
|
city: "Augsburg",
|
|
privacyMode: "state",
|
|
};
|
|
|
|
const lidlDepartments: StoreDepartment[] = [
|
|
{ id: "lidl-produce", name: "Obst & Gemüse", sortOrder: 1 },
|
|
{ id: "lidl-bakery", name: "Brot & Backwaren", sortOrder: 2 },
|
|
{ id: "lidl-cereals", name: "Cerealien", sortOrder: 3 },
|
|
{ id: "lidl-dairy", name: "Milchprodukte", sortOrder: 4 },
|
|
{ id: "lidl-meat", name: "Fleisch/Wurst/Käse", sortOrder: 5 },
|
|
{ id: "lidl-frozen", name: "Tiefkühlung", sortOrder: 6 },
|
|
{ id: "lidl-drinks", name: "Getränke", sortOrder: 7 },
|
|
{ id: "lidl-checkout", name: "Kasse", sortOrder: 8 },
|
|
];
|
|
|
|
function genericDepartments(storeId: string): StoreDepartment[] {
|
|
return [
|
|
{ id: `${storeId}-produce`, name: "Obst & Gemüse", sortOrder: 1 },
|
|
{ id: `${storeId}-bakery`, name: "Brot & Backwaren", sortOrder: 2 },
|
|
{ id: `${storeId}-dry`, name: "Trockenware", sortOrder: 3 },
|
|
{ id: `${storeId}-dairy`, name: "Milchprodukte", sortOrder: 4 },
|
|
{ id: `${storeId}-protein`, name: "Fleisch/Fisch/Protein", sortOrder: 5 },
|
|
{ id: `${storeId}-frozen`, name: "Tiefkühlung", sortOrder: 6 },
|
|
{ id: `${storeId}-drinks`, name: "Getränke", sortOrder: 7 },
|
|
{ id: `${storeId}-other`, name: "Sonstiges", sortOrder: 99 },
|
|
];
|
|
}
|
|
|
|
export const seedStores: Store[] = [
|
|
{ id: "lidl", name: "Lidl", favorite: true, departments: lidlDepartments },
|
|
{ id: "aldi", name: "Aldi", favorite: true, departments: genericDepartments("aldi") },
|
|
{ id: "edeka", name: "Edeka", favorite: true, departments: genericDepartments("edeka") },
|
|
{ id: "kaufland", name: "Kaufland", favorite: true, departments: genericDepartments("kaufland") },
|
|
{ id: "rewe", name: "Rewe", departments: genericDepartments("rewe") },
|
|
{ id: "dm", name: "DM", departments: genericDepartments("dm") },
|
|
{ id: "biomarkt", name: "Biomarkt", departments: genericDepartments("biomarkt") },
|
|
];
|
|
|
|
export const seedFoods: CatalogFood[] = [
|
|
{ id: "food-apple", name: "Apfel", category: "Obst", kcalPer100g: 52, proteinPer100g: 0.3, carbsPer100g: 14, fatPer100g: 0.2, servingG: 150, preferredUnit: "piece", portions: [{ id: "apple-piece", label: "1 Apfel", unit: "piece", amount: 1, gramsEquivalent: 150 }], favorite: true, source: "seed", availableEverywhere: true, availabilityTags: ["basis", "frisch"] },
|
|
{ id: "food-banana", name: "Banane", category: "Obst", kcalPer100g: 89, proteinPer100g: 1.1, carbsPer100g: 23, fatPer100g: 0.3, servingG: 120, preferredUnit: "piece", portions: [{ id: "banana-piece", label: "1 Banane", unit: "piece", amount: 1, gramsEquivalent: 120 }], favorite: true, source: "seed", availableEverywhere: true, availabilityTags: ["basis", "frisch"] },
|
|
{ id: "food-oats", name: "Haferflocken", category: "Getreide", kcalPer100g: 372, proteinPer100g: 13.5, carbsPer100g: 58.7, fatPer100g: 7, servingG: 50, preferredUnit: "g", favorite: true, source: "seed", availableEverywhere: true, availabilityTags: ["basis", "vorrat"] },
|
|
{ id: "food-rice", name: "Reis, trocken", category: "Getreide", kcalPer100g: 350, proteinPer100g: 7.4, carbsPer100g: 78, fatPer100g: 0.6, servingG: 75, source: "seed", availableEverywhere: true, availabilityTags: ["vorrat"] },
|
|
{ id: "food-potato", name: "Kartoffeln", category: "Gemüse", kcalPer100g: 77, proteinPer100g: 2, carbsPer100g: 17, fatPer100g: 0.1, servingG: 250, source: "seed", availableEverywhere: true, availabilityTags: ["basis", "frisch"] },
|
|
{ id: "food-egg", name: "Ei", category: "Protein", kcalPer100g: 155, proteinPer100g: 13, carbsPer100g: 1.1, fatPer100g: 11, servingG: 60, preferredUnit: "piece", portions: [{ id: "egg-piece", label: "1 Ei", unit: "piece", amount: 1, gramsEquivalent: 60 }], favorite: true, source: "seed", availableEverywhere: true, availabilityTags: ["basis"] },
|
|
{ id: "food-chicken", name: "Hähnchenbrust", category: "Protein", kcalPer100g: 110, proteinPer100g: 23, carbsPer100g: 0, fatPer100g: 1.5, servingG: 180, favorite: true, source: "seed", availableEverywhere: true, availabilityTags: ["frisch"] },
|
|
{ id: "food-quark", name: "Magerquark", category: "Milchprodukte", kcalPer100g: 67, proteinPer100g: 12, carbsPer100g: 4, fatPer100g: 0.2, servingG: 250, preferredUnit: "cup", portions: [{ id: "quark-cup", label: "1 Becher", unit: "cup", amount: 1, gramsEquivalent: 500 }], source: "seed", availableEverywhere: true, availabilityTags: ["protein"] },
|
|
{ id: "food-paprika", name: "Paprika", category: "Gemüse", kcalPer100g: 43, proteinPer100g: 1.3, carbsPer100g: 6.4, fatPer100g: 0.5, servingG: 100, source: "seed", availableEverywhere: true, availabilityTags: ["frisch"] },
|
|
{ id: "food-tuna", name: "Thunfisch", category: "Protein", kcalPer100g: 116, proteinPer100g: 26, carbsPer100g: 0, fatPer100g: 1, servingG: 120, source: "seed", storeIds: ["kaufland", "edeka", "rewe"], availabilityTags: ["konserve", "protein"] },
|
|
{ id: "food-greek-yoghurt-kaufland", name: "Griechischer Joghurt 0,1 % Fett", brand: "K-Classic", category: "Milchprodukte", kcalPer100g: 57, proteinPer100g: 10, carbsPer100g: 3.8, fatPer100g: 0.1, servingG: 250, preferredUnit: "cup", portions: [{ id: "greek-yoghurt-cup", label: "1 Becher", unit: "cup", amount: 1, gramsEquivalent: 500 }], source: "seed", storeIds: ["kaufland"], availabilityTags: ["nur kaufland", "protein"] },
|
|
{ id: "food-gockentoast-gold", name: "Gockentoast Gold", category: "Brot & Backwaren", kcalPer100g: 245, proteinPer100g: 8, carbsPer100g: 45, fatPer100g: 3.5, servingG: 50, preferredUnit: "slice", portions: [{ id: "toast-slice", label: "1 Scheibe", unit: "slice", amount: 1, gramsEquivalent: 25 }, { id: "toast-pack", label: "1 Packung", unit: "pack", amount: 1, gramsEquivalent: 500 }], source: "seed", storeIds: ["edeka", "kaufland", "lidl"], availabilityTags: ["toast", "supermarkt"] },
|
|
{ id: "food-oil", name: "Olivenöl", category: "Öl & Fett", kcalPer100g: 884, proteinPer100g: 0, carbsPer100g: 0, fatPer100g: 100, servingG: 15, preferredUnit: "tbsp", densityGPerMl: 0.91, portions: [{ id: "oil-tsp", label: "1 TL", unit: "tsp", amount: 1, gramsEquivalent: 4.6 }, { id: "oil-tbsp", label: "1 EL", unit: "tbsp", amount: 1, gramsEquivalent: 13.7 }], source: "seed", availableEverywhere: true, availabilityTags: ["basis"] },
|
|
{ id: "food-cream", name: "Sahne", category: "Milchprodukte", kcalPer100g: 292, proteinPer100g: 2.4, carbsPer100g: 3.2, fatPer100g: 30, servingG: 100, preferredUnit: "ml", densityGPerMl: 1, portions: [{ id: "cream-bottle", label: "1 Flasche", unit: "bottle", amount: 1, gramsEquivalent: 250 }], source: "seed", availableEverywhere: true, availabilityTags: ["kühlung"] },
|
|
];
|
|
|
|
export const seedRecipes: Recipe[] = [
|
|
{
|
|
id: "recipe-eggs",
|
|
name: "Eier-Muffins mit Paprika",
|
|
portions: 2,
|
|
minutes: 20,
|
|
ingredients: [
|
|
{ foodId: "food-egg", amountG: 180 },
|
|
{ foodId: "food-paprika", amountG: 150 },
|
|
{ foodId: "food-quark", amountG: 80 },
|
|
],
|
|
notes: "Gut für Meal Prep, hält 2 Tage im Kühlschrank.",
|
|
},
|
|
{
|
|
id: "recipe-pita",
|
|
name: "Mediterrane Thunfisch-Pita",
|
|
portions: 1,
|
|
minutes: 20,
|
|
ingredients: [
|
|
{ foodId: "food-tuna", amountG: 120 },
|
|
{ foodId: "food-paprika", amountG: 80 },
|
|
],
|
|
},
|
|
];
|
|
|
|
export const seedPlannedMeals: PlannedMeal[] = [
|
|
{ id: "plan-eggs", date: todayKey(), mealType: "Frühstück", recipeId: "recipe-eggs", name: "Eier-Muffins mit Paprika", portions: 1 },
|
|
{ id: "plan-pita", date: todayKey(), mealType: "Mittagessen", recipeId: "recipe-pita", name: "Mediterrane Thunfisch-Pita", portions: 1 },
|
|
];
|