feat: complete Svelte 5 migration across entire application
All checks were successful
CI / update (push) Successful in 2m8s

Migrated all components and routes from Svelte 4 to Svelte 5 syntax:

- Converted export let → $props() with generic type syntax
- Replaced createEventDispatcher → callback props
- Migrated $: reactive statements → $derived() and $effect()
- Updated two-way bindings with $bindable()
- Fixed TypeScript syntax: added lang="ts" to script tags
- Converted inline type annotations to generic parameter syntax

- Updated deprecated event directives to Svelte 5 syntax:
  - on:click → onclick
  - on:submit → onsubmit
  - on:change → onchange

- Converted deprecated <slot> elements → {@render children()}
- Updated slot props to Snippet types
- Fixed season/icon selector components with {#snippet} blocks

- Fixed non-reactive state by converting let → $state()
- Fixed infinite loop in EnhancedBalance by converting $effect → $derived
- Fixed Chart.js integration by converting $state proxies to plain arrays
- Updated cospend dashboard and payment pages with proper reactivity

- Migrated 20+ route files from export let data → $props()
- Fixed TypeScript type annotations in page components
- Updated reactive statements in error and cospend routes

- Removed invalid onchange attribute from Toggle component
- Fixed modal ID isolation in CreateIngredientList/CreateStepList
- Fixed dark mode button visibility in TranslationApproval
- Build now succeeds with zero deprecation warnings

All functionality tested and working. No breaking changes to user experience.
This commit is contained in:
2026-01-10 16:20:43 +01:00
parent 8eee15d901
commit 5c8605c690
72 changed files with 1011 additions and 1043 deletions

View File

@@ -1,25 +1,37 @@
<script>
export let imagePreview = '';
export let imageFile = null;
export let uploading = false;
export let currentImage = null; // For edit mode
export let title = 'Receipt Image';
// Events
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
<script lang="ts">
let {
imagePreview = $bindable(''),
imageFile = $bindable(null),
uploading = $bindable(false),
currentImage = $bindable(null),
title = 'Receipt Image',
onerror,
onimageSelected,
onimageRemoved,
oncurrentImageRemoved
} = $props<{
imagePreview?: string,
imageFile?: File | null,
uploading?: boolean,
currentImage?: string | null,
title?: string,
onerror?: (message: string) => void,
onimageSelected?: (file: File) => void,
onimageRemoved?: () => void,
oncurrentImageRemoved?: () => void
}>();
function handleImageChange(event) {
const file = event.target.files[0];
if (file) {
if (file.size > 5 * 1024 * 1024) {
dispatch('error', 'File size must be less than 5MB');
onerror?.('File size must be less than 5MB');
return;
}
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
dispatch('error', 'Please select a valid image file (JPEG, PNG, WebP)');
onerror?.('Please select a valid image file (JPEG, PNG, WebP)');
return;
}
@@ -29,8 +41,8 @@
imagePreview = e.target.result;
};
reader.readAsDataURL(file);
dispatch('imageSelected', file);
onimageSelected?.(file);
}
}
@@ -38,12 +50,12 @@
imageFile = null;
imagePreview = '';
currentImage = null;
dispatch('imageRemoved');
onimageRemoved?.();
}
function removeCurrentImage() {
currentImage = null;
dispatch('currentImageRemoved');
oncurrentImageRemoved?.();
}
</script>
@@ -54,7 +66,7 @@
<div class="current-image">
<img src={currentImage} alt="Receipt" class="receipt-preview" />
<div class="image-actions">
<button type="button" class="btn-remove" on:click={removeCurrentImage}>
<button type="button" class="btn-remove" onclick={removeCurrentImage}>
Remove Image
</button>
</div>
@@ -64,7 +76,7 @@
{#if imagePreview}
<div class="image-preview">
<img src={imagePreview} alt="Receipt preview" />
<button type="button" class="remove-image" on:click={removeImage}>
<button type="button" class="remove-image" onclick={removeImage}>
Remove Image
</button>
</div>
@@ -85,7 +97,7 @@
type="file"
id="image"
accept="image/jpeg,image/jpg,image/png,image/webp"
on:change={handleImageChange}
onchange={handleImageChange}
disabled={uploading}
hidden
/>