fix: eliminate all 167 svelte-check warnings
Refactor page components to use $derived + invalidateAll() where data is read-only or re-fetched after mutations. Suppress state_referenced_locally for intentional patterns (form state, optimistic updates, pagination). Fix a11y issues with role="presentation", add standard line-clamp properties, remove unused CSS selectors and empty rulesets.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "1.11.1",
|
"version": "1.11.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
children
|
children
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let isVisible = $state(eager); // If eager=true, render immediately
|
let isVisible = $state(eager); // If eager=true, render immediately
|
||||||
/** @type {HTMLDivElement | null} */
|
/** @type {HTMLDivElement | null} */
|
||||||
let containerRef = $state(null);
|
let containerRef = $state(null);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let shouldLoad = $state(eager);
|
let shouldLoad = $state(eager);
|
||||||
/** @type {HTMLImageElement | null} */
|
/** @type {HTMLImageElement | null} */
|
||||||
let imgElement = $state(null);
|
let imgElement = $state(null);
|
||||||
|
|||||||
@@ -5,16 +5,19 @@
|
|||||||
|
|
||||||
let { initialBalance = null, initialDebtData = null } = $props<{ initialBalance?: any, initialDebtData?: any }>();
|
let { initialBalance = null, initialDebtData = null } = $props<{ initialBalance?: any, initialDebtData?: any }>();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let balance = $state(initialBalance || {
|
let balance = $state(initialBalance || {
|
||||||
netBalance: 0,
|
netBalance: 0,
|
||||||
recentSplits: []
|
recentSplits: []
|
||||||
});
|
});
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let debtData = $state(initialDebtData || {
|
let debtData = $state(initialDebtData || {
|
||||||
whoOwesMe: [],
|
whoOwesMe: [],
|
||||||
whoIOwe: [],
|
whoIOwe: [],
|
||||||
totalOwedToMe: 0,
|
totalOwedToMe: 0,
|
||||||
totalIOwe: 0
|
totalIOwe: 0
|
||||||
});
|
});
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let loading = $state(!initialBalance || !initialDebtData);
|
let loading = $state(!initialBalance || !initialDebtData);
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,14 @@
|
|||||||
|
|
||||||
const isEnglish = $derived(lang === 'en');
|
const isEnglish = $derived(lang === 'en');
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let book: string = $state(verseData?.book || '');
|
let book: string = $state(verseData?.book || '');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let chapter: number = $state(verseData?.chapter || 0);
|
let chapter: number = $state(verseData?.chapter || 0);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let verses: Array<{ verse: number; text: string }> = $state(verseData?.verses || []);
|
let verses: Array<{ verse: number; text: string }> = $state(verseData?.verses || []);
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let error = $state(verseData ? '' : (lang === 'en' ? 'No verse data available' : 'Keine Versdaten verfügbar'));
|
let error = $state(verseData ? '' : (lang === 'en' ? 'No verse data available' : 'Keine Versdaten verfügbar'));
|
||||||
|
|
||||||
function handleBackdropClick(event: MouseEvent) {
|
function handleBackdropClick(event: MouseEvent) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
let { periods: initialPeriods = [], lang = 'en', sharedWith: initialSharedWith = [], readOnly = false, ownerName = '' } = $props();
|
let { periods: initialPeriods = [], lang = 'en', sharedWith: initialSharedWith = [], readOnly = false, ownerName = '' } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let periods = $state([...initialPeriods]);
|
let periods = $state([...initialPeriods]);
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
let showAddForm = $state(false);
|
let showAddForm = $state(false);
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
let showHistory = $state(false);
|
let showHistory = $state(false);
|
||||||
|
|
||||||
// Sharing state
|
// Sharing state
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let shareList = $state([...initialSharedWith]);
|
let shareList = $state([...initialSharedWith]);
|
||||||
let showShare = $state(false);
|
let showShare = $state(false);
|
||||||
let shareInput = $state('');
|
let shareInput = $state('');
|
||||||
@@ -855,10 +857,10 @@
|
|||||||
|
|
||||||
<!-- Share modal -->
|
<!-- Share modal -->
|
||||||
{#if showShare}
|
{#if showShare}
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
||||||
<div class="share-overlay" onclick={() => showShare = false} onkeydown={(e) => e.key === 'Escape' && (showShare = false)}>
|
<div class="share-overlay" onclick={() => showShare = false} onkeydown={(e) => e.key === 'Escape' && (showShare = false)}>
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions a11y_click_events_have_key_events -->
|
||||||
<div class="share-modal" onclick={(e) => e.stopPropagation()}>
|
<div class="share-modal" role="presentation" onclick={(e) => e.stopPropagation()}>
|
||||||
<div class="share-modal-header">
|
<div class="share-modal-header">
|
||||||
<h3>{t('share', lang)}</h3>
|
<h3>{t('share', lang)}</h3>
|
||||||
<button class="share-modal-close" onclick={() => showShare = false}>
|
<button class="share-modal-close" onclick={() => showShare = false}>
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
<svelte:window onkeydown={handleKeydown} />
|
<svelte:window onkeydown={handleKeydown} />
|
||||||
|
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<div class="video-overlay" onclick={handleBackdrop}>
|
<div class="video-overlay" role="presentation" onclick={handleBackdrop}>
|
||||||
<button class="close-btn" onclick={onClose} aria-label="Close video">
|
<button class="close-btn" onclick={onClose} aria-label="Close video">
|
||||||
<X size={24} />
|
<X size={24} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ let {
|
|||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
// Unique dialog ID based on type to prevent conflicts when both are on the same page
|
// Unique dialog ID based on type to prevent conflicts when both are on the same page
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const dialogId = `base-recipe-selector-modal-${type}`;
|
const dialogId = `base-recipe-selector-modal-${type}`;
|
||||||
|
|
||||||
let baseRecipes: any[] = $state([]);
|
let baseRecipes: any[] = $state([]);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
const isEnglish = $derived(lang === 'en');
|
const isEnglish = $derived(lang === 'en');
|
||||||
const label = $derived(isEnglish ? 'Favorites' : 'Favoriten');
|
const label = $derived(isEnglish ? 'Favorites' : 'Favoriten');
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let checked = $state(enabled);
|
let checked = $state(enabled);
|
||||||
|
|
||||||
// Watch for changes to checked and call onToggle
|
// Watch for changes to checked and call onToggle
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ const flattenedIngredients = $derived.by(() => {
|
|||||||
const lang = data.lang || 'de';
|
const lang = data.lang || 'de';
|
||||||
return flattenIngredientReferences(data.ingredients, lang);
|
return flattenIngredientReferences(data.ingredients, lang);
|
||||||
});
|
});
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let multiplier = $state(data.multiplier || 1);
|
let multiplier = $state(data.multiplier || 1);
|
||||||
|
|
||||||
const isEnglish = $derived(data.lang === 'en');
|
const isEnglish = $derived(data.lang === 'en');
|
||||||
@@ -138,10 +139,15 @@ const labels = $derived({
|
|||||||
|
|
||||||
// Cake form scaling
|
// Cake form scaling
|
||||||
const hasDefaultForm = $derived(!!data.defaultForm?.shape);
|
const hasDefaultForm = $derived(!!data.defaultForm?.shape);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let userFormShape = $state(data.defaultForm?.shape || 'round');
|
let userFormShape = $state(data.defaultForm?.shape || 'round');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let userFormDiameter = $state(data.defaultForm?.diameter || 26);
|
let userFormDiameter = $state(data.defaultForm?.diameter || 26);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let userFormWidth = $state(data.defaultForm?.width || 20);
|
let userFormWidth = $state(data.defaultForm?.width || 20);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let userFormLength = $state(data.defaultForm?.length || 30);
|
let userFormLength = $state(data.defaultForm?.length || 30);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let userFormInnerDiameter = $state(data.defaultForm?.innerDiameter || 8);
|
let userFormInnerDiameter = $state(data.defaultForm?.innerDiameter || 8);
|
||||||
|
|
||||||
/** @param {string} shape @param {number} diameter @param {number} width @param {number} length @param {number} innerDiameter */
|
/** @param {string} shape @param {number} diameter @param {number} width @param {number} length @param {number} innerDiameter */
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let multiplier = $state(data.multiplier || 1);
|
let multiplier = $state(data.multiplier || 1);
|
||||||
|
|
||||||
// Recursively flatten nested instruction references
|
// Recursively flatten nested instruction references
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
const andLabel = $derived(isEnglish ? 'AND' : 'UND');
|
const andLabel = $derived(isEnglish ? 'AND' : 'UND');
|
||||||
const orLabel = $derived(isEnglish ? 'OR' : 'ODER');
|
const orLabel = $derived(isEnglish ? 'OR' : 'ODER');
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let checked = $state(useAndLogic);
|
let checked = $state(useAndLogic);
|
||||||
|
|
||||||
// Watch for changes to checked and call onToggle
|
// Watch for changes to checked and call onToggle
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
type TranslationState = 'idle' | 'translating' | 'preview' | 'approved' | 'error';
|
type TranslationState = 'idle' | 'translating' | 'preview' | 'approved' | 'error';
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let translationState = $state<TranslationState>(englishData ? 'preview' : 'idle');
|
let translationState = $state<TranslationState>(englishData ? 'preview' : 'idle');
|
||||||
let errorMessage = $state('');
|
let errorMessage = $state('');
|
||||||
let validationErrors = $state<string[]>([]);
|
let validationErrors = $state<string[]>([]);
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly initialize editableEnglish from germanData if no English translation exists
|
// Eagerly initialize editableEnglish from germanData if no English translation exists
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let editableEnglish = $state<any>(
|
let editableEnglish = $state<any>(
|
||||||
englishData ? {
|
englishData ? {
|
||||||
...englishData,
|
...englishData,
|
||||||
|
|||||||
@@ -31,17 +31,27 @@
|
|||||||
{ tag: 'müll', icon: Trash2 },
|
{ tag: 'müll', icon: Trash2 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let title = $state(task?.title || '');
|
let title = $state(task?.title || '');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let description = $state(task?.description || '');
|
let description = $state(task?.description || '');
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let selectedAssignees = $state(task?.assignees ? [...task.assignees] : []);
|
let selectedAssignees = $state(task?.assignees ? [...task.assignees] : []);
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let selectedTags = $state(task?.tags ? [...task.tags] : []);
|
let selectedTags = $state(task?.tags ? [...task.tags] : []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let difficulty = $state(task?.difficulty || '');
|
let difficulty = $state(task?.difficulty || '');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let refreshMode = $state(task?.refreshMode || 'completion');
|
let refreshMode = $state(task?.refreshMode || 'completion');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let isRecurring = $state(task?.isRecurring || false);
|
let isRecurring = $state(task?.isRecurring || false);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let frequencyType = $state(task?.frequency?.type || 'weekly');
|
let frequencyType = $state(task?.frequency?.type || 'weekly');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let customDays = $state(task?.frequency?.customDays || 7);
|
let customDays = $state(task?.frequency?.customDays || 7);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let nextDueDate = $state(
|
let nextDueDate = $state(
|
||||||
task?.nextDueDate
|
task?.nextDueDate
|
||||||
? new Date(task.nextDueDate).toISOString().split('T')[0]
|
? new Date(task.nextDueDate).toISOString().split('T')[0]
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
// Create language context for prayer components
|
// Create language context for prayer components
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const langContext = createLanguageContext({ urlLang: /** @type {'de' | 'en'} */(data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
const langContext = createLanguageContext({ urlLang: /** @type {'de' | 'en'} */(data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
||||||
|
|
||||||
// Update lang store when data.lang changes (e.g., after navigation)
|
// Update lang store when data.lang changes (e.g., after navigation)
|
||||||
@@ -113,6 +114,7 @@
|
|||||||
postcommunio: ['eucharistic'],
|
postcommunio: ['eucharistic'],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let selectedCategory = $state(data.initialCategory);
|
let selectedCategory = $state(data.initialCategory);
|
||||||
|
|
||||||
// JS-only search (hidden without JS)
|
// JS-only search (hidden without JS)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const langContext = createLanguageContext({ urlLang: /** @type {'de' | 'en'} */(data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
const langContext = createLanguageContext({ urlLang: /** @type {'de' | 'en'} */(data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ import { setupScrollSync } from "./rosaryScrollSync.js";
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
// Toggle for including Luminous mysteries (initialized from URL param or default)
|
// Toggle for including Luminous mysteries (initialized from URL param or default)
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let includeLuminous = $state(data.initialLuminous);
|
let includeLuminous = $state(data.initialLuminous);
|
||||||
|
|
||||||
// Toggle for showing mystery images (initialized from URL param or default)
|
// Toggle for showing mystery images (initialized from URL param or default)
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let showImages = $state(data.initialShowImages);
|
let showImages = $state(data.initialShowImages);
|
||||||
|
|
||||||
// Flag to prevent saving before we've loaded from localStorage
|
// Flag to prevent saving before we've loaded from localStorage
|
||||||
@@ -40,6 +42,7 @@ let hasLoadedFromStorage = $state(false);
|
|||||||
|
|
||||||
// Create language context for prayer components (LanguageToggle will use this)
|
// Create language context for prayer components (LanguageToggle will use this)
|
||||||
// For Latin route, force showLatin on so only Latin prayers render
|
// For Latin route, force showLatin on so only Latin prayers render
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const langContext = createLanguageContext({ urlLang: /** @type {'en'|'de'} */ (data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
const langContext = createLanguageContext({ urlLang: /** @type {'en'|'de'} */ (data.lang), initialLatin: data.lang === 'la' ? true : data.initialLatin });
|
||||||
|
|
||||||
// Update lang store when data.lang changes (e.g., after navigation)
|
// Update lang store when data.lang changes (e.g., after navigation)
|
||||||
@@ -71,8 +74,9 @@ $effect(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Use server-computed initial values (supports no-JS via URL params)
|
// Use server-computed initial values (supports no-JS via URL params)
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let selectedMystery = $state(/** @type {MysteryType} */ (data.initialMystery));
|
let selectedMystery = $state(/** @type {MysteryType} */ (data.initialMystery));
|
||||||
let todaysMystery = $state(/** @type {MysteryType} */ (data.todaysMystery));
|
let todaysMystery = $derived(/** @type {MysteryType} */ (data.todaysMystery));
|
||||||
|
|
||||||
// Derive these values from selectedMystery so they update automatically
|
// Derive these values from selectedMystery so they update automatically
|
||||||
let currentMysteries = $derived(mysteries[selectedMystery]);
|
let currentMysteries = $derived(mysteries[selectedMystery]);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
const hasHashedImage = (r: any) => r.images?.length > 0 && /\.\w+\.\w+$/.test(r.images[0].mediapath);
|
const hasHashedImage = (r: any) => r.images?.length > 0 && /\.\w+\.\w+$/.test(r.images[0].mediapath);
|
||||||
|
|
||||||
// Server-generated random index ensures SSR and client pick the same hero
|
// Server-generated random index ensures SSR and client pick the same hero
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const heroIndex = data.heroIndex;
|
const heroIndex = data.heroIndex;
|
||||||
const heroRecipe = $derived.by(() => {
|
const heroRecipe = $derived.by(() => {
|
||||||
const seasonPool = data.season.filter(hasHashedImage);
|
const seasonPool = data.season.filter(hasHashedImage);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<script>
|
<script>
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const isEnglish = data.lang === 'en';
|
const isEnglish = data.lang === 'en';
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const recipeLang = data.recipeLang;
|
const recipeLang = data.recipeLang;
|
||||||
|
|
||||||
let processing = $state(false);
|
let processing = $state(false);
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
let { data } = $props<{ data: PageData }>();
|
let { data } = $props<{ data: PageData }>();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const isEnglish = data.lang === 'en';
|
const isEnglish = data.lang === 'en';
|
||||||
const pageTitle = isEnglish ? 'Administration' : 'Administration';
|
const pageTitle = isEnglish ? 'Administration' : 'Administration';
|
||||||
const pageDescription = isEnglish
|
const pageDescription = isEnglish
|
||||||
? 'Manage recipes and content'
|
? 'Manage recipes and content'
|
||||||
: 'Rezepte und Inhalte verwalten';
|
: 'Rezepte und Inhalte verwalten';
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const links = [
|
const links = [
|
||||||
{
|
{
|
||||||
title: isEnglish ? 'Untranslated Recipes' : 'Unübersetzte Rezepte',
|
title: isEnglish ? 'Untranslated Recipes' : 'Unübersetzte Rezepte',
|
||||||
|
|||||||
@@ -19,10 +19,14 @@
|
|||||||
|
|
||||||
let { data, form }: { data: PageData; form: ActionData } = $props();
|
let { data, form }: { data: PageData; form: ActionData } = $props();
|
||||||
|
|
||||||
// Recipe data state
|
// Recipe data state — all form fields initialized from server data (intentionally local mutable state)
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let preamble = $state(data.recipe.preamble || "");
|
let preamble = $state(data.recipe.preamble || "");
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let addendum = $state(data.recipe.addendum || "");
|
let addendum = $state(data.recipe.addendum || "");
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let note = $state(data.recipe.note || "");
|
let note = $state(data.recipe.note || "");
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let image_preview_url = $state(
|
let image_preview_url = $state(
|
||||||
"https://bocken.org/static/rezepte/thumb/" +
|
"https://bocken.org/static/rezepte/thumb/" +
|
||||||
(data.recipe.images?.[0]?.mediapath || `${data.recipe.short_name}.webp`)
|
(data.recipe.images?.[0]?.mediapath || `${data.recipe.short_name}.webp`)
|
||||||
@@ -31,15 +35,20 @@
|
|||||||
|
|
||||||
// Translation workflow state
|
// Translation workflow state
|
||||||
let showTranslationWorkflow = $state(false);
|
let showTranslationWorkflow = $state(false);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let translationData = $state<any>(data.recipe.translations?.en || null);
|
let translationData = $state<any>(data.recipe.translations?.en || null);
|
||||||
let changedFields = $state<string[]>([]);
|
let changedFields = $state<string[]>([]);
|
||||||
|
|
||||||
// Store original recipe data for change detection
|
// Store original recipe data for change detection
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const originalRecipe = JSON.parse(JSON.stringify(data.recipe));
|
const originalRecipe = JSON.parse(JSON.stringify(data.recipe));
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const old_short_name = data.recipe.short_name;
|
const old_short_name = data.recipe.short_name;
|
||||||
|
|
||||||
// Season and portions stores
|
// Season and portions stores
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
portions.update(() => data.recipe.portions || "");
|
portions.update(() => data.recipe.portions || "");
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let portions_local = $state<string>(data.recipe.portions || "");
|
let portions_local = $state<string>(data.recipe.portions || "");
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
portions.subscribe((p) => {
|
portions.subscribe((p) => {
|
||||||
@@ -47,7 +56,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
season.update(() => data.recipe.season || []);
|
season.update(() => data.recipe.season || []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let season_local = $state<number[]>(data.recipe.season || []);
|
let season_local = $state<number[]>(data.recipe.season || []);
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
season.subscribe((s) => {
|
season.subscribe((s) => {
|
||||||
@@ -55,6 +66,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let card_data = $state({
|
let card_data = $state({
|
||||||
icon: data.recipe.icon || "",
|
icon: data.recipe.icon || "",
|
||||||
category: data.recipe.category || "",
|
category: data.recipe.category || "",
|
||||||
@@ -63,6 +75,7 @@
|
|||||||
tags: data.recipe.tags || [],
|
tags: data.recipe.tags || [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let add_info = $state({
|
let add_info = $state({
|
||||||
preparation: data.recipe.preparation || "",
|
preparation: data.recipe.preparation || "",
|
||||||
fermentation: {
|
fermentation: {
|
||||||
@@ -78,13 +91,20 @@
|
|||||||
cooking: data.recipe.cooking || "",
|
cooking: data.recipe.cooking || "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let images = $state(data.recipe.images || []);
|
let images = $state(data.recipe.images || []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let short_name = $state(data.recipe.short_name || "");
|
let short_name = $state(data.recipe.short_name || "");
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let datecreated = $state(data.recipe.datecreated);
|
let datecreated = $state(data.recipe.datecreated);
|
||||||
let datemodified = $state(new Date());
|
let datemodified = $state(new Date());
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let isBaseRecipe = $state(data.recipe.isBaseRecipe || false);
|
let isBaseRecipe = $state(data.recipe.isBaseRecipe || false);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let defaultForm = $state(data.recipe.defaultForm ? { ...data.recipe.defaultForm } : null);
|
let defaultForm = $state(data.recipe.defaultForm ? { ...data.recipe.defaultForm } : null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let ingredients = $state(data.recipe.ingredients || []);
|
let ingredients = $state(data.recipe.ingredients || []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let instructions = $state(data.recipe.instructions || []);
|
let instructions = $state(data.recipe.instructions || []);
|
||||||
|
|
||||||
// Form submission state
|
// Form submission state
|
||||||
@@ -245,6 +265,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nutrition state — all edits are local until form save
|
// Nutrition state — all edits are local until form save
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let nutritionMappings = $state<any[]>(data.recipe.nutritionMappings || []);
|
let nutritionMappings = $state<any[]>(data.recipe.nutritionMappings || []);
|
||||||
let generatingNutrition = $state(false);
|
let generatingNutrition = $state(false);
|
||||||
let searchQueries = $state<Record<string, string>>({});
|
let searchQueries = $state<Record<string, string>>({});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let items = $state(data.items ?? []);
|
let items = $state(data.items ?? []);
|
||||||
|
|
||||||
const isEnglish = $derived(data.lang === 'en');
|
const isEnglish = $derived(data.lang === 'en');
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
let showModal = $state(false);
|
let showModal = $state(false);
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
let paymentId = $state(null);
|
let paymentId = $state(null);
|
||||||
let user = $state(data.session?.user);
|
let user = $derived(data.session?.user);
|
||||||
let isGuest = $derived(!data.session?.user);
|
let isGuest = $derived(!data.session?.user);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { pushState } from '$app/navigation';
|
import { pushState } from '$app/navigation';
|
||||||
import ProfilePicture from '$lib/components/cospend/ProfilePicture.svelte';
|
import ProfilePicture from '$lib/components/cospend/ProfilePicture.svelte';
|
||||||
import EnhancedBalance from '$lib/components/cospend/EnhancedBalance.svelte';
|
import EnhancedBalance from '$lib/components/cospend/EnhancedBalance.svelte';
|
||||||
@@ -16,14 +17,14 @@
|
|||||||
let { data } = $props(); // Contains session data and balance from server
|
let { data } = $props(); // Contains session data and balance from server
|
||||||
|
|
||||||
// Use server-side data, with fallback for progressive enhancement
|
// Use server-side data, with fallback for progressive enhancement
|
||||||
let balance = $state(data.balance || {
|
let balance = $derived(data.balance || {
|
||||||
netBalance: 0,
|
netBalance: 0,
|
||||||
recentSplits: []
|
recentSplits: []
|
||||||
});
|
});
|
||||||
let loading = $state(false); // Start as false since we have server data
|
let loading = $state(false);
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
let monthlyExpensesData = $state(/** @type {any} */ (data).monthlyExpensesData || { labels: [], datasets: [] });
|
let monthlyExpensesData = $state(/** @type {any} */ ({ labels: [], datasets: [] }));
|
||||||
let expensesLoading = $state(false);
|
let expensesLoading = $state(false);
|
||||||
/** @type {string[] | null} */
|
/** @type {string[] | null} */
|
||||||
let categoryFilter = $state(null);
|
let categoryFilter = $state(null);
|
||||||
@@ -40,71 +41,31 @@
|
|||||||
/** @type {any} */
|
/** @type {any} */
|
||||||
let debtBreakdownComponent;
|
let debtBreakdownComponent;
|
||||||
|
|
||||||
// Progressive enhancement: refresh data if JavaScript is available
|
|
||||||
onMount(() => {
|
|
||||||
// Mark that JavaScript is loaded for progressive enhancement
|
|
||||||
document.body.classList.add('js-loaded');
|
|
||||||
|
|
||||||
// Only fetch if we don't have server-side data
|
|
||||||
if (!balance.recentSplits || balance.recentSplits.length === 0) {
|
|
||||||
fetchBalance();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!monthlyExpensesData.datasets || monthlyExpensesData.datasets.length === 0) {
|
|
||||||
fetchMonthlyExpenses();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for dashboard refresh events from the layout
|
|
||||||
const handleDashboardRefresh = () => {
|
|
||||||
refreshAllComponents();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('dashboardRefresh', handleDashboardRefresh);
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('dashboardRefresh', handleDashboardRefresh);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
async function fetchBalance() {
|
|
||||||
try {
|
|
||||||
loading = true;
|
|
||||||
const response = await fetch('/api/cospend/balance');
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to fetch balance');
|
|
||||||
}
|
|
||||||
balance = await response.json();
|
|
||||||
} catch (err) {
|
|
||||||
error = err instanceof Error ? err.message : String(err);
|
|
||||||
} finally {
|
|
||||||
loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchMonthlyExpenses() {
|
async function fetchMonthlyExpenses() {
|
||||||
try {
|
try {
|
||||||
expensesLoading = true;
|
expensesLoading = true;
|
||||||
const response = await fetch('/api/cospend/monthly-expenses');
|
const response = await fetch('/api/cospend/monthly-expenses');
|
||||||
if (!response.ok) {
|
if (response.ok) monthlyExpensesData = await response.json();
|
||||||
throw new Error('Failed to fetch monthly expenses');
|
|
||||||
}
|
|
||||||
monthlyExpensesData = await response.json();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error fetching monthly expenses:', err);
|
console.error('Error fetching monthly expenses:', err);
|
||||||
// Don't show this error in the main error state
|
|
||||||
} finally {
|
} finally {
|
||||||
expensesLoading = false;
|
expensesLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Progressive enhancement: refresh data if JavaScript is available
|
||||||
|
onMount(() => {
|
||||||
|
document.body.classList.add('js-loaded');
|
||||||
|
fetchMonthlyExpenses();
|
||||||
|
|
||||||
|
const handleDashboardRefresh = () => { refreshAllComponents(); };
|
||||||
|
window.addEventListener('dashboardRefresh', handleDashboardRefresh);
|
||||||
|
return () => { window.removeEventListener('dashboardRefresh', handleDashboardRefresh); };
|
||||||
|
});
|
||||||
|
|
||||||
// Function to refresh all dashboard components after payment deletion
|
// Function to refresh all dashboard components after payment deletion
|
||||||
async function refreshAllComponents() {
|
async function refreshAllComponents() {
|
||||||
// Refresh the main balance and recent activity
|
await Promise.all([invalidateAll(), fetchMonthlyExpenses()]);
|
||||||
await Promise.all([
|
|
||||||
fetchBalance(),
|
|
||||||
fetchMonthlyExpenses()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Refresh the enhanced balance component if it exists and has a refresh method
|
// Refresh the enhanced balance component if it exists and has a refresh method
|
||||||
if (enhancedBalanceComponent && enhancedBalanceComponent.refresh) {
|
if (enhancedBalanceComponent && enhancedBalanceComponent.refresh) {
|
||||||
|
|||||||
@@ -475,6 +475,7 @@
|
|||||||
<div class="edit-modal" onclick={(e) => e.stopPropagation()}>
|
<div class="edit-modal" onclick={(e) => e.stopPropagation()}>
|
||||||
<h3>{parseQuantity(editingItem.name).name}</h3>
|
<h3>{parseQuantity(editingItem.name).name}</h3>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y_label_has_associated_control -->
|
||||||
<label class="edit-label">Kategorie</label>
|
<label class="edit-label">Kategorie</label>
|
||||||
<div class="category-picker">
|
<div class="category-picker">
|
||||||
{#each SHOPPING_CATEGORIES as cat}
|
{#each SHOPPING_CATEGORIES as cat}
|
||||||
@@ -492,6 +493,7 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y_label_has_associated_control -->
|
||||||
<label class="edit-label">Icon</label>
|
<label class="edit-label">Icon</label>
|
||||||
<div class="icon-search">
|
<div class="icon-search">
|
||||||
<Search size={14} />
|
<Search size={14} />
|
||||||
@@ -838,6 +840,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,16 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
// Use server-side data with progressive enhancement
|
// Use server-side data with progressive enhancement
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let payments = $state(data.payments || []);
|
let payments = $state(data.payments || []);
|
||||||
let loading = $state(false); // Start as false since we have server data
|
let loading = $state(false); // Start as false since we have server data
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let currentPage = $state(Math.floor(data.currentOffset / data.limit));
|
let currentPage = $state(Math.floor(data.currentOffset / data.limit));
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let limit = $state(data.limit || 20);
|
let limit = $state(data.limit || 20);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let hasMore = $state(data.hasMore || false);
|
let hasMore = $state(data.hasMore || false);
|
||||||
|
|
||||||
// Re-sync local state when server data changes (e.g. URL param navigation)
|
// Re-sync local state when server data changes (e.g. URL param navigation)
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
// Initialize form data with server values if available (for error handling)
|
// Initialize form data with server values if available (for error handling)
|
||||||
/** @type {Record<string, any>} */
|
/** @type {Record<string, any>} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const formValues = form?.values || {};
|
const formValues = form?.values || {};
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let formData = $state({
|
let formData = $state({
|
||||||
title: /** @type {string} */ (formValues.title || ''),
|
title: /** @type {string} */ (formValues.title || ''),
|
||||||
description: /** @type {string} */ (formValues.description || ''),
|
description: /** @type {string} */ (formValues.description || ''),
|
||||||
@@ -31,6 +33,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Recurring payment settings
|
// Recurring payment settings
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let recurringData = $state({
|
let recurringData = $state({
|
||||||
frequency: /** @type {string} */ (formValues.recurringFrequency || 'monthly'),
|
frequency: /** @type {string} */ (formValues.recurringFrequency || 'monthly'),
|
||||||
cronExpression: /** @type {string} */ (formValues.recurringCronExpression || ''),
|
cronExpression: /** @type {string} */ (formValues.recurringCronExpression || ''),
|
||||||
@@ -49,7 +52,9 @@
|
|||||||
let personalAmounts = $state({});
|
let personalAmounts = $state({});
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let error = $state(form?.error || null);
|
let error = $state(form?.error || null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let predefinedMode = $state(data.predefinedUsers.length > 0);
|
let predefinedMode = $state(data.predefinedUsers.length > 0);
|
||||||
let jsEnhanced = $state(false);
|
let jsEnhanced = $state(false);
|
||||||
let cronError = $state(false);
|
let cronError = $state(false);
|
||||||
@@ -67,6 +72,7 @@
|
|||||||
let exchangeRateTimeout = $state();
|
let exchangeRateTimeout = $state();
|
||||||
|
|
||||||
// Initialize users from server data for no-JS support (use data directly to avoid reactivity warning)
|
// Initialize users from server data for no-JS support (use data directly to avoid reactivity warning)
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const initialUsers = data.predefinedUsers.length > 0 ? [...data.predefinedUsers] : (data.currentUser ? [data.currentUser] : []);
|
const initialUsers = data.predefinedUsers.length > 0 ? [...data.predefinedUsers] : (data.currentUser ? [data.currentUser] : []);
|
||||||
let users = $state(initialUsers);
|
let users = $state(initialUsers);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { goto } from '$app/navigation';
|
import { goto, invalidateAll } from '$app/navigation';
|
||||||
import ProfilePicture from '$lib/components/cospend/ProfilePicture.svelte';
|
import ProfilePicture from '$lib/components/cospend/ProfilePicture.svelte';
|
||||||
import { getCategoryEmoji, getCategoryName } from '$lib/utils/categories';
|
import { getCategoryEmoji, getCategoryName } from '$lib/utils/categories';
|
||||||
import EditButton from '$lib/components/EditButton.svelte';
|
import EditButton from '$lib/components/EditButton.svelte';
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
// Use server-side data with progressive enhancement
|
// Use server-side data with progressive enhancement
|
||||||
/** @type {any | null} */
|
/** @type {any | null} */
|
||||||
let payment = $state(data.payment || null);
|
let payment = $derived(data.payment || null);
|
||||||
let loading = $state(false); // Start as false since we have server data
|
let loading = $state(false);
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
|
|
||||||
@@ -24,26 +24,10 @@
|
|||||||
|
|
||||||
// Only refresh if we don't have server data
|
// Only refresh if we don't have server data
|
||||||
if (!payment) {
|
if (!payment) {
|
||||||
await loadPayment();
|
await invalidateAll();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadPayment() {
|
|
||||||
try {
|
|
||||||
loading = true;
|
|
||||||
const response = await fetch(`/api/cospend/payments/${data.paymentId}`);
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to load payment');
|
|
||||||
}
|
|
||||||
const result = await response.json();
|
|
||||||
payment = result.payment;
|
|
||||||
} catch (err) {
|
|
||||||
error = err instanceof Error ? err.message : String(err);
|
|
||||||
} finally {
|
|
||||||
loading = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatAmountWithCurrency(/** @type {any} */ payment) {
|
function formatAmountWithCurrency(/** @type {any} */ payment) {
|
||||||
if (payment.currency === 'CHF' || !payment.originalAmount) {
|
if (payment.currency === 'CHF' || !payment.originalAmount) {
|
||||||
return formatCurrency(payment.amount, 'CHF', 'de-CH');
|
return formatCurrency(payment.amount, 'CHF', 'de-CH');
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let formData = $state({
|
let formData = $state({
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
let { data, form } = $props();
|
let { data, form } = $props();
|
||||||
|
|
||||||
// Use server-side data with progressive enhancement
|
// Use server-side data with progressive enhancement
|
||||||
let debtData = $state(data.debtData || {
|
let debtData = $derived(data.debtData || {
|
||||||
whoOwesMe: [],
|
whoOwesMe: [],
|
||||||
whoIOwe: [],
|
whoIOwe: [],
|
||||||
totalOwedToMe: 0,
|
totalOwedToMe: 0,
|
||||||
@@ -18,9 +18,11 @@
|
|||||||
});
|
});
|
||||||
let loading = $state(false); // Start as false since we have server data
|
let loading = $state(false); // Start as false since we have server data
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let error = $state(data.error || form?.error || null);
|
let error = $state(data.error || form?.error || null);
|
||||||
/** @type {{ type: string; from: string; to: string; amount: number; description: string } | null} */
|
/** @type {{ type: string; from: string; to: string; amount: number; description: string } | null} */
|
||||||
let selectedSettlement = $state(null);
|
let selectedSettlement = $state(null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let settlementAmount = $state(form?.values?.amount || '');
|
let settlementAmount = $state(form?.values?.amount || '');
|
||||||
let submitting = $state(false);
|
let submitting = $state(false);
|
||||||
let predefinedMode = isPredefinedUsersMode();
|
let predefinedMode = isPredefinedUsersMode();
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let sessions = $state(data.sessions?.sessions ? [...data.sessions.sessions] : []);
|
let sessions = $state(data.sessions?.sessions ? [...data.sessions.sessions] : []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let total = $state(data.sessions?.total ? data.sessions.total : 0);
|
let total = $state(data.sessions?.total ? data.sessions.total : 0);
|
||||||
let loading = $state(false);
|
let loading = $state(false);
|
||||||
let page = $state(1);
|
let page = $state(1);
|
||||||
|
|||||||
@@ -13,14 +13,20 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
const workout = getWorkout();
|
const workout = getWorkout();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let latest = $state(data.latest ? { ...data.latest } : {});
|
let latest = $state(data.latest ? { ...data.latest } : {});
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let measurements = $state(data.measurements?.measurements ? [...data.measurements.measurements] : []);
|
let measurements = $state(data.measurements?.measurements ? [...data.measurements.measurements] : []);
|
||||||
let showWeightHistory = $state(false);
|
let showWeightHistory = $state(false);
|
||||||
|
|
||||||
// Profile fields (sex, height, birth year) — stored in FitnessGoal
|
// Profile fields (sex, height, birth year) — stored in FitnessGoal
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let savedSex = $state(data.profile?.sex ?? 'male');
|
let savedSex = $state(data.profile?.sex ?? 'male');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let profileSex = $state(data.profile?.sex ?? 'male');
|
let profileSex = $state(data.profile?.sex ?? 'male');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let profileHeight = $state(data.profile?.heightCm != null ? String(data.profile.heightCm) : '');
|
let profileHeight = $state(data.profile?.heightCm != null ? String(data.profile.heightCm) : '');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let profileBirthYear = $state(data.profile?.birthYear != null ? String(data.profile.birthYear) : '');
|
let profileBirthYear = $state(data.profile?.birthYear != null ? String(data.profile.birthYear) : '');
|
||||||
let profileSaving = $state(false);
|
let profileSaving = $state(false);
|
||||||
let profileEditing = $state(false);
|
let profileEditing = $state(false);
|
||||||
@@ -146,7 +152,8 @@
|
|||||||
{#if profileEditing}
|
{#if profileEditing}
|
||||||
<div class="profile-fields">
|
<div class="profile-fields">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{t('sex', lang)}</label>
|
<!-- svelte-ignore a11y_label_has_associated_control -->
|
||||||
|
<label>{t('sex', lang)}</label>
|
||||||
<div class="sex-pills">
|
<div class="sex-pills">
|
||||||
<button class="sex-pill" class:active={profileSex === 'male'} onclick={() => profileSex = 'male'}>
|
<button class="sex-pill" class:active={profileSex === 'male'} onclick={() => profileSex = 'male'}>
|
||||||
<Mars size={14} /> {t('male', lang)}
|
<Mars size={14} /> {t('male', lang)}
|
||||||
@@ -357,7 +364,7 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
.form-group input, .form-group select {
|
.form-group input {
|
||||||
padding: 0.4rem 0.5rem;
|
padding: 0.4rem 0.5rem;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
const measureSlug = $derived(lang === 'en' ? 'measure' : 'messen');
|
const measureSlug = $derived(lang === 'en' ? 'measure' : 'messen');
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const m = data.measurement?.measurement;
|
const m = data.measurement?.measurement;
|
||||||
|
|
||||||
let saving = $state(false);
|
let saving = $state(false);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
// --- Date navigation ---
|
// --- Date navigation ---
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let currentDate = $state(data.date);
|
let currentDate = $state(data.date);
|
||||||
const todayStr = new Date().toISOString().slice(0, 10);
|
const todayStr = new Date().toISOString().slice(0, 10);
|
||||||
const isToday = $derived(currentDate === todayStr);
|
const isToday = $derived(currentDate === todayStr);
|
||||||
@@ -37,7 +38,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Entries ---
|
// --- Entries ---
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let entries = $state(data.foodLog?.entries ?? []);
|
let entries = $state(data.foodLog?.entries ?? []);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let recipeImages = $state(data.recipeImages ?? {});
|
let recipeImages = $state(data.recipeImages ?? {});
|
||||||
|
|
||||||
async function loadEntries() {
|
async function loadEntries() {
|
||||||
@@ -53,13 +56,21 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// --- Goals ---
|
// --- Goals ---
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalCalories = $state(data.goal?.dailyCalories ?? null);
|
let goalCalories = $state(data.goal?.dailyCalories ?? null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalProteinMode = $state(data.goal?.proteinMode ?? 'fixed');
|
let goalProteinMode = $state(data.goal?.proteinMode ?? 'fixed');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalProteinTarget = $state(data.goal?.proteinTarget ?? null);
|
let goalProteinTarget = $state(data.goal?.proteinTarget ?? null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalFatPercent = $state(data.goal?.fatPercent ?? null);
|
let goalFatPercent = $state(data.goal?.fatPercent ?? null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalCarbPercent = $state(data.goal?.carbPercent ?? null);
|
let goalCarbPercent = $state(data.goal?.carbPercent ?? null);
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let goalSex = $state(data.goal?.sex ?? 'male');
|
let goalSex = $state(data.goal?.sex ?? 'male');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let activityLevel = $state(data.goal?.activityLevel ?? 'light');
|
let activityLevel = $state(data.goal?.activityLevel ?? 'light');
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let latestWeight = $state(data.latestWeight?.weight?.value ?? null);
|
let latestWeight = $state(data.latestWeight?.weight?.value ?? null);
|
||||||
|
|
||||||
let showGoalEditor = $state(false);
|
let showGoalEditor = $state(false);
|
||||||
@@ -395,6 +406,7 @@
|
|||||||
const carbGoalGrams = $derived(goalCalories && goalCarbPercent ? (goalCalories * goalCarbPercent / 100) / 4 : null);
|
const carbGoalGrams = $derived(goalCalories && goalCarbPercent ? (goalCalories * goalCarbPercent / 100) / 4 : null);
|
||||||
|
|
||||||
// --- Burned kcal ---
|
// --- Burned kcal ---
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let exerciseKcal = $state(Number(data.exerciseKcal) || 0);
|
let exerciseKcal = $state(Number(data.exerciseKcal) || 0);
|
||||||
|
|
||||||
// BMR via Mifflin-St Jeor (doi:10.1093/ajcn/51.2.241)
|
// BMR via Mifflin-St Jeor (doi:10.1093/ajcn/51.2.241)
|
||||||
@@ -2417,9 +2429,6 @@
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
.meal-section {
|
|
||||||
|
|
||||||
}
|
|
||||||
.meal-header {
|
.meal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -2505,6 +2514,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
.food-card-link {
|
.food-card-link {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import FitnessChart from '$lib/components/fitness/FitnessChart.svelte';
|
import FitnessChart from '$lib/components/fitness/FitnessChart.svelte';
|
||||||
import MuscleHeatmap from '$lib/components/fitness/MuscleHeatmap.svelte';
|
import MuscleHeatmap from '$lib/components/fitness/MuscleHeatmap.svelte';
|
||||||
@@ -35,8 +36,8 @@
|
|||||||
|
|
||||||
const stats = $derived(data.stats ?? {});
|
const stats = $derived(data.stats ?? {});
|
||||||
|
|
||||||
let goalStreak = $state(data.goal?.streak ?? 0);
|
let goalStreak = $derived(data.goal?.streak ?? 0);
|
||||||
let goalWeekly = $state(data.goal?.weeklyWorkouts ?? null);
|
let goalWeekly = $derived(data.goal?.weeklyWorkouts ?? null);
|
||||||
let goalEditing = $state(false);
|
let goalEditing = $state(false);
|
||||||
let goalInput = $state(4);
|
let goalInput = $state(4);
|
||||||
let goalSaving = $state(false);
|
let goalSaving = $state(false);
|
||||||
@@ -57,9 +58,7 @@
|
|||||||
body: JSON.stringify({ weeklyWorkouts: goalInput })
|
body: JSON.stringify({ weeklyWorkouts: goalInput })
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const d = await res.json();
|
await invalidateAll();
|
||||||
goalWeekly = d.weeklyWorkouts;
|
|
||||||
goalStreak = d.streak;
|
|
||||||
goalEditing = false;
|
goalEditing = false;
|
||||||
} else {
|
} else {
|
||||||
const err = await res.json().catch(() => null);
|
const err = await res.json().catch(() => null);
|
||||||
|
|||||||
@@ -18,13 +18,16 @@
|
|||||||
|
|
||||||
const workout = getWorkout();
|
const workout = getWorkout();
|
||||||
const sync = getWorkoutSync();
|
const sync = getWorkoutSync();
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let templates = $state(data.templates?.templates ? [...data.templates.templates] : []);
|
let templates = $state(data.templates?.templates ? [...data.templates.templates] : []);
|
||||||
let seeded = $state(false);
|
let seeded = $state(false);
|
||||||
|
|
||||||
// Schedule state
|
// Schedule state
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let scheduleOrder = $state(data.schedule?.schedule?.templateOrder ?? []);
|
let scheduleOrder = $state(data.schedule?.schedule?.templateOrder ?? []);
|
||||||
/** @type {string | null} */
|
/** @type {string | null} */
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
let nextTemplateId = $state(data.schedule?.nextTemplateId ?? null);
|
let nextTemplateId = $state(data.schedule?.nextTemplateId ?? null);
|
||||||
let showScheduleEditor = $state(false);
|
let showScheduleEditor = $state(false);
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
let tasks = $state(data.tasks || []);
|
let tasks = $derived(data.tasks || []);
|
||||||
let stats = $state(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
let stats = $derived(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
||||||
let currentUser = $derived(data.session?.user?.nickname || '');
|
let currentUser = $derived(data.session?.user?.nickname || '');
|
||||||
let myStat = $derived(stats.userStats.find((/** @type {any} */ s) => s._id === currentUser));
|
let myStat = $derived(stats.userStats.find((/** @type {any} */ s) => s._id === currentUser));
|
||||||
let showForm = $state(false);
|
let showForm = $state(false);
|
||||||
@@ -130,12 +130,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function refreshTasks() {
|
async function refreshTasks() {
|
||||||
const [tasksRes, statsRes] = await Promise.all([
|
await invalidateAll();
|
||||||
fetch('/api/tasks'),
|
|
||||||
fetch('/api/tasks/stats')
|
|
||||||
]);
|
|
||||||
if (tasksRes.ok) tasks = (await tasksRes.json()).tasks;
|
|
||||||
if (statsRes.ok) stats = await statsRes.json();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleTaskSaved() {
|
async function handleTaskSaved() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { STICKERS, getStickerById, getRarityColor } from '$lib/utils/stickers';
|
import { STICKERS, getStickerById, getRarityColor } from '$lib/utils/stickers';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { de } from 'date-fns/locale';
|
import { de } from 'date-fns/locale';
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
/** @type {import('$lib/utils/stickers').Sticker | null} */
|
/** @type {import('$lib/utils/stickers').Sticker | null} */
|
||||||
let selectedSticker = $state(null);
|
let selectedSticker = $state(null);
|
||||||
|
|
||||||
let stats = $state(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
let stats = $derived(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
||||||
let currentUser = $derived(data.session?.user?.nickname || '');
|
let currentUser = $derived(data.session?.user?.nickname || '');
|
||||||
|
|
||||||
const rarityLabels = /** @type {Record<string, string>} */ ({
|
const rarityLabels = /** @type {Record<string, string>} */ ({
|
||||||
@@ -69,19 +70,13 @@
|
|||||||
/** @param {string} id */
|
/** @param {string} id */
|
||||||
async function deleteCompletion(id) {
|
async function deleteCompletion(id) {
|
||||||
const res = await fetch(`/api/tasks/completions/${id}`, { method: 'DELETE' });
|
const res = await fetch(`/api/tasks/completions/${id}`, { method: 'DELETE' });
|
||||||
if (res.ok) {
|
if (res.ok) await invalidateAll();
|
||||||
const statsRes = await fetch('/api/tasks/stats');
|
|
||||||
if (statsRes.ok) stats = await statsRes.json();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clearHistory() {
|
async function clearHistory() {
|
||||||
if (!confirm('Deinen gesamten Verlauf und alle Sticker wirklich löschen? Das kann nicht rückgängig gemacht werden.')) return;
|
if (!confirm('Deinen gesamten Verlauf und alle Sticker wirklich löschen? Das kann nicht rückgängig gemacht werden.')) return;
|
||||||
const res = await fetch('/api/tasks/stats', { method: 'DELETE' });
|
const res = await fetch('/api/tasks/stats', { method: 'DELETE' });
|
||||||
if (res.ok) {
|
if (res.ok) await invalidateAll();
|
||||||
const [statsRes] = await Promise.all([fetch('/api/tasks/stats')]);
|
|
||||||
if (statsRes.ok) stats = await statsRes.json();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user