Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3215dc8
Create 1.3.0.md
javedh-dev Mar 19, 2026
4a47226
Merge branch 'main' into dev
javedh-dev Mar 19, 2026
e210f4a
Update Finnish translations for 1.3.x
pHamala Mar 22, 2026
71445ef
fix(i18n): fix typos in Finnish translations
pHamala Mar 22, 2026
abdfdf0
Add UK MPG fuel consumption unit
jake-walker Mar 24, 2026
72f24b6
Fix code formatting
jake-walker Mar 24, 2026
3ed9121
Allow zero cost in maintenance logs
rderewianko Mar 26, 2026
57c1930
Fix vehicle list not refreshing after delete
rderewianko Mar 26, 2026
e89db04
Skip pollution notifications when feature is disabled
rderewianko Mar 26, 2026
ed6a551
Merge pull request #196 from pHamala/dev
javedh-dev Mar 26, 2026
bc52575
Merge pull request #198 from rderewianko/dev
javedh-dev Mar 26, 2026
f2a51f3
Merge pull request #199 from rderewianko/fix/vehicle-delete-refresh-v2
javedh-dev Mar 26, 2026
ad5972b
Merge pull request #200 from rderewianko/fix/pollution-notification-f…
javedh-dev Mar 26, 2026
58f5dc3
Add .gitattributes to force LF ending
daunera Mar 27, 2026
c183876
fix: Fix to filter out id key when creating fuel log, insurance, poll…
daunera Mar 27, 2026
f631294
feat: Add missing hungarian translations
daunera Mar 27, 2026
cec57a9
Merge pull request #197 from jake-walker/uk-mileage
javedh-dev Mar 27, 2026
d5cbcf0
fix: Add locale to calendar to handle it based on it (e.g. week start…
daunera Mar 27, 2026
1fb0584
fix: Fix settins date format input validation check
daunera Mar 27, 2026
fcd41ac
Merge branch 'javedh-dev:dev' into dev
daunera Mar 27, 2026
3294a80
Fix vehicle creation failing with null id
rderewianko Mar 28, 2026
83d2304
Let Drizzle generate vehicle UUID via schema defaultFn
rderewianko Mar 30, 2026
02b42d9
Merge pull request #203 from rderewianko/fix/vehicle-null-id
javedh-dev Mar 30, 2026
37272c4
Merge pull request #201 from daunera/dev
javedh-dev Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
115 changes: 115 additions & 0 deletions changelogs/1.3.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Release Notes – v1.3.0 (2026-03-19)

## Overview

v1.3.0 focuses on the settings experience, notification delivery controls, clearer overview charts, and a broad cleanup of shared helpers and notification flows. It also improves demo seed data so mileage and cost trends look more realistic over time.

## Major Features

### Settings UX Improvements

- Expanded all settings accordions by default for quicker access
- Made settings forms more responsive with two- and three-column layouts where space allows
- Added a compact switch-based style for feature flags
- Updated notification provider forms to default new providers to enabled
- Simplified channel subscription controls in the provider dialog
- Extracted reusable settings sections, field helpers, and display blocks
- Reused shared tab shell and form composition patterns in settings

### Notification Delivery Controls

- Added a toggle to enable or disable scheduled notification delivery
- Kept the delivery schedule tied to the notification settings state
- Disabled schedule inputs automatically when scheduling is turned off
- Added webhook and Gotify providers alongside email
- Improved provider toggling, cron scheduling, and notification send templates
- Allowed editing providers without exposing keys/tokens

### Overview Charts

- Added an average reference line to mileage and cost graphs
- Displayed the average as a top-right label with unit-aware formatting
- Formatted tooltip values with the correct units for mileage and currency
- Added a unit-aware average formatter for chart tooltips and labels
- Improved the chart presentation with clearer dashed average lines

### Demo Seed Data

- Updated seeded mileage values to progress more naturally over time
- Added small mileage deviations and realistic fuel cost variation
- Made the overall seed data better reflect real-world usage patterns
- Refined seeded notifications and maintenance history for more natural trends

## Configuration Changes

- Added `notificationProcessingEnabled` to control scheduled notification delivery
- Kept `notificationProcessingSchedule` as the cron expression for delivery timing
- Added default config values for LPG and CNG fuel units
- Preserved mileage unit format settings for distance-per-fuel and fuel-per-distance
- Expanded settings schema and defaults for feature flags and notification delivery
- Updated chart formatting helpers to support unit-aware mileage and currency display
- Added shared config handling and merge helpers for notification provider settings
- Seeded the new config entries automatically for demo setups

## Environment/Runtime Changes

- Demo seeding now generates more realistic mileage and cost trends
- Notification scheduler respects the new enabled/disabled config state
- Overview charts now format tooltip and average values using app units
- The app now keeps chart and settings formatting aligned with the active locale/config

## UI/UX Improvements

- Compact provider channel subscriptions in the add/edit provider dialog
- Better chart labeling and readability in the overview section
- More consistent settings layout across personalization, units, and feature flags
- Better mobile behavior and tighter spacing across settings and dialogs
- Improved loading skeletons and shared record card layouts across the UI

## Architecture & Shared Helpers

- Consolidated route error helpers and standardized backend service responses
- Added typed payload helpers for domain and service layers
- Reduced shared store and form `any` usage
- Extracted reusable table, skeleton, and formatter helpers
- Reused shared resource state and feature card layouts across the UI
- Improved notification provider config merge and service date helpers
- Added helper reuse across vehicle, fuel, maintenance, insurance, and reminders

## Localization & Messaging

- Continued moving hardcoded UI text into i18n message functions
- Added or refined translated messages for settings, notifications, and charts
- Improved localized labels across dashboard, forms, and notifications

## Developer Experience

- Added MCP support for the repo's Svelte workflow
- Upgraded dependencies and fixed follow-up lint/check issues
- Cleaned up formatting, typing, and shared abstractions across the codebase

## Bug Fixes & Improvements

- Fixed reactive binding issues in settings forms so inputs update correctly
- Fixed notification delivery scheduling state so it no longer re-enables unexpectedly after save
- Improved unit display for mileage and cost values in chart tooltips and labels
- Fixed settings accordions to stay expanded by default
- Fixed provider add/edit flow to keep new providers enabled by default
- Fixed fuel and maintenance sorting when records share dates
- Fixed NaN-prone calculations and lint issues carried over from refactors

## Migration Notes

- No breaking changes were introduced
- Existing settings and data remain compatible

## Environment Variables

- No new environment variables were required for this release
- Existing runtime behavior continues to use the current app configuration and demo flags

## Known Issues

- None reported at release time

For detailed commit history, see the [compare view](https://github.com/javedh-dev/tracktor/compare/v1.2.0...v1.3.0).
1 change: 1 addition & 0 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"settings_desc_mileage_format": "Choose how fuel efficiency is displayed",
"settings_mileage_format_distance_per_fuel": "Distance per Fuel (e.g., km/L, mpg)",
"settings_mileage_format_fuel_per_distance": "Fuel per Distance (e.g., L/100km)",
"settings_mileage_format_uk_mpg": "UK MPG (miles per imperial gallon)",
"settings_desc_theme": "Choose your preferred theme",
"settings_desc_custom_css": "CSS Styles for customizing the interface",
"settings_select_language": "Select language",
Expand Down
107 changes: 104 additions & 3 deletions messages/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"hello_world": "Hei, {name} fi:stä!",
"app_name": "Tracktor",
"app_title": "Autotallisi",
"app_new_update_available": "Uusi päivitys saatavilla. Ladataan uudelleen..!",
"app_add_vehicle": "Lisää ajoneuvo",
"app_empty_select_message": "Valitse ajoneuvo nähdäksesi sen yksityiskohdat",
"app_empty_select_hint": "Valitse ajoneuvo nähdäksesi sen kojelaudan",
Expand Down Expand Up @@ -79,7 +80,6 @@
"nav_insurance": "Vakuutus",
"nav_pollution": "Päästöt",
"nav_reminders": "Muistutukset",
"nav_settings": "Asetukset",
"tools_export_data": "Vie data",
"tools_import_data": "Tuo data",
"vehicle_form_make_label": "Valmistaja",
Expand Down Expand Up @@ -163,7 +163,7 @@
"fuel_add_title": "Lisää tankkaus",
"col_date": "Päivämäärä",
"col_odometer": "Matkamittari",
"col_filled": "Täytetty",
"col_filled": "Täysi tankki",
"col_missed_last": "Edellinen ohitettu",
"col_fuel_amount": "Polttoainemäärä",
"col_cost": "Kustannus",
Expand Down Expand Up @@ -204,6 +204,8 @@
"notifications_section_alerts": "Hälytykset",
"notifications_mark_done_title": "Merkitse muistutus valmiiksi",
"notifications_mark_done_aria": "Merkitse {type} muistutus valmiiksi",
"notifications_mark_all_read_title": "Merkitse kaikki luetuiksi",
"notifications_mark_all_read_aria": "Merkitse kaikki ilmoitukset luetuiksi",
"notifications_overdue_days": "{days} päivää myöhässä",
"notifications_due_today": "Määräaika tänään",
"notifications_due_tomorrow": "Määräaika huomenna",
Expand Down Expand Up @@ -449,5 +451,104 @@
"pollution_recurrence_type_fixed": "Kiinteä päättymispäivä",
"pollution_recurrence_type_yearly": "Uusiutuu vuosittain",
"pollution_recurrence_type_monthly": "Uusiutuu kuukausittain",
"pollution_recurrence_type_no_end": "Ei päättymispäivämäärää"
"pollution_recurrence_type_no_end": "Ei päättymispäivämäärää",
"file_drop_existing_note": "Liitetiedosto (Napauta nähdäksesi)",
"fuel_import_step_1_title": "Vaihe 1 : Lataa CSV tiedosto",
"fuel_import_step_1_desc": "Valitse erotinmerkkiä käyttävä tekstitiedosto, joka sisältää polttoaineen kulutustietosi, aloittaaksesi tuontiprosessin",
"fuel_import_drop_placeholder": "Liitä erotinmerkillinen teksti tai napauta selataksesi",
"fuel_import_headers_checkbox": "Ensimmäinen rivi sisältää otsikot",
"fuel_import_delimiter_title": "Erotin",
"fuel_import_delimiter_desc": "Valitse merkki, joka erottaa kentät",
"fuel_import_date_format_title": "Päivämäärän muoto",
"fuel_import_date_format_desc": "Määritä CSV-tiedostosi päivämäärille käytettävä muoto.",
"fuel_import_error_no_headers": "Otsikoita ei havaittu. Päivitä csv.helper.ts palauttaaksesi otsikot.",
"fuel_import_step_2_title": "Vaihe 2 : CSV-sarakkeiden kartoitus",
"fuel_import_step_2_desc": "Määritä CSV-tiedostosi sarakkeet vastaaviin polttoainelokin kenttiin. Pakolliset kentät on merkitty merkinnällä <span class=\"text-destructive\">*</span>.",
"fuel_import_step_3_title": "Vaihe 3 : Esikatsele ja tuo",
"fuel_import_step_3_desc": "Tarkista tuotavat tiedot esikatselusta.",
"fuel_import_no_preview": "Ei esikatseltavaa tietoa vielä. Toteuta jäsentely csv.helper.ts-tiedostossa rivien täyttämiseksi.",
"fuel_import_success": "Tuotu onnistuneesti {count} polttoainelokia.",
"fuel_import_failed_count": "Tuotu {imported}, epäonnistui {failed}",
"fuel_import_error_generic": "Polttoainelokin tuonti epäonnistui.",
"fuel_import_vehicle_label": "Ajoneuvo:",
"fuel_import_delimiter_comma": "Pilkku ( , )",
"fuel_import_delimiter_semicolon": "Puolipiste ( ; )",
"fuel_import_delimiter_tab": "Sarkain ( \\t )",
"fuel_import_delimiter_pipe": "Pystyviiva ( | )",
"fuel_import_delimiter_custom": "Muokattu",
"fuel_import_date_error": "Jotkin rivit sisältävät virheellisesti muotoiltuja päivämääriä \"{format}\"",
"fuel_import_date_format_placeholder": "esim., MM/DD/YYYY",
"fuel_import_date_invalid": "Virheellinen päivämäärä",
"fuel_import_no_vehicle": "Ajoneuvoa ei valittu",
"fuel_import_col_date_hint": "Tankkauspäivämäärä",
"fuel_import_col_odometer_hint": "Mittarilukema tankkaushetkellä",
"fuel_import_col_fuel_hint": "Tankattu määrä tai energia",
"fuel_import_col_cost_hint": "Kokonaiskustannus",
"fuel_import_col_filled_hint": "Onko tämä täysi tankki/lataus?",
"fuel_import_col_missed_hint": "Jäikö edellinen merkintä väliin?",
"fuel_import_col_notes_hint": "Lisätietoja",
"autocomplete_placeholder": "Kirjoita tai valitse...",
"autocomplete_loading": "Ladataan ehdotuksia...",
"autocomplete_no_results": "Ehdotuksia ei löydy. Voit kirjoittaa uuden arvon.",
"input_date_placeholder": "Valitse päivämäärä",
"loading_default_message": "Ladataan...",
"dropzone_placeholder_image": "Napauta tai vedä kuva ladataksesi",
"dropzone_placeholder_attachment": "Pudota kuva tänne tai napauta valitaksesi",
"dropzone_placeholder_default": "Napauta tai vedä tiedostoja ladataksesi",
"dropzone_error_single_file": "Lataa vain yksi tiedosto.",
"dropzone_error_file_size": "Tiedoston koko ylittää sallitun enimmäiskoon {size}.",
"dropzone_unknown_file": "Tuntematon tiedosto",
"dropzone_uploading": "Ladataan...",
"dropzone_supports": "Tuettu: {types}",
"dropzone_max_size": "Enimmäiskoko: {size}",
"dropzone_error_file_type": "Tiedoston tyyppi ei ole tuettu",
"dropzone_hint_accept_limit": "{types} enintään {size}",
"vehicle_details_color_aria": "Väri",
"vehicle_details_close_aria": "Sulje",
"attachment_link_view_title": "Näytä liite",
"file_preview_not_available": "Esikatselu ei ole saatavilla",
"file_preview_download_hint": "Tätä tiedostotyyppiä ei voi esikatsella suoraan. Lataa tiedosto tarkastellaksesi sitä.",
"file_preview_download_button": "Lataa tiedosto",
"file_preview_aria_download": "Lataa",
"file_preview_aria_close": "Sulje",
"theme_toggle_label": "Vaihda teemaa",
"fuel_log_edit": "Muokkaa",
"fuel_log_delete": "Poista",
"fuel_log_menu_open": "Avaa valikko",
"fuel_log_delete_success": "Poistetut polttoainelokimerkinnät",
"fuel_log_menu_sheet_title": "Päivitä polttoainelokimerkintä",
"fuel_log_delete_error": "Polttoainelokimerkinnän poistamisessa tapahtui virhe",
"color_picker_label": "Valitse väri",
"reminder_type_maintenance": "Huolto",
"reminder_type_insurance": "Vakuutuksen uusiminen",
"reminder_type_pollution": "Päästömittaus / katsastus",
"reminder_type_registration": "Rekisteröinti / ajoneuvovero",
"reminder_type_inspection": "Katsastus",
"reminder_type_custom": "Custom",
"alert_type_insurance": "Vakuutus",
"alert_type_pucc": "Päästötodistus",
"alert_status_expired_ago": "{label} vanheni {days} päivää sitten",
"alert_status_expires_in": "{label} vanhenee {days} päivässä",
"alert_status_valid_for": "{label} voimassa {days} päivää",
"alert_insurance_active_no_end": "Vakuutus on voimassa toistaiseksi",
"alert_pucc_active_no_end": "Päästötodistus on voimassa toistaiseksi",
"alert_record_not_found": "{label}-tietoja ei löydy. Lisää tiedot pysyäksesi vaatimusten mukaisena.",
"settings_error_format_not_valid": "Virheellinen muoto",
"common_kilogram_unit": "Kilogrammaa (kg)",
"common_pound_unit": "Paunaa (lb)",
"settings_section_fuel_types": "Polttoaineen tyyppi",
"settings_section_fuel_types_desc": "Valitse mittayksikkö kullekin polttoaineelle.",
"fuel_type_petrol_diesel": "Bensiini/Diesel",
"theme_slate": "Liuskekivi",
"theme_stone": "Kivi",
"theme_red": "Punainen",
"theme_rose": "Ruusu",
"theme_blue": "Sininen",
"theme_green": "Vihreä",
"theme_purple": "Violetti",
"theme_orange": "Oranssi",
"theme_yellow": "Keltainen",
"theme_teal": "Sinivihreä",
"theme_indigo": "Indigo",
"theme_pink": "Vaaleanpunainen"
}
2 changes: 2 additions & 0 deletions messages/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@
"notifications_section_alerts": "Riasztások",
"notifications_mark_done_title": "Emlékeztető megjelölése készként",
"notifications_mark_done_aria": "{type} emlékeztető megjelölése készként",
"notifications_mark_all_read_title": "Az összes megjelölése olvasottként",
"notifications_mark_all_read_aria": "Összes értesítés megjelölése olvasottként",
"notifications_overdue_days": "{days} napja lejárt",
"notifications_due_today": "Ma esedékes",
"notifications_due_tomorrow": "Holnap esedékes",
Expand Down
6 changes: 5 additions & 1 deletion src/lib/components/feature/settings/SettingsModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
unitOfLpg: z.enum(['liter', 'gallon', 'kilogram', 'pound']).default('liter'),
unitOfCng: z.enum(['liter', 'gallon', 'kilogram', 'pound']).default('kilogram'),
mileageUnitFormat: z
.enum(['distance-per-fuel', 'fuel-per-distance'])
.enum(['distance-per-fuel', 'fuel-per-distance', 'uk-mpg'])
.default('distance-per-fuel'),
theme: z.string().default('light'),
customCss: z.string().optional(),
Expand Down Expand Up @@ -177,6 +177,10 @@
{
value: 'fuel-per-distance',
label: m.settings_mileage_format_fuel_per_distance()
},
{
value: 'uk-mpg',
label: m.settings_mileage_format_uk_mpg()
}
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
/>
<Form.Description>
{m.common_example_prefix()}
{isValidFormat(formData.dateFormat).ex || m.common_invalid_format()}
{isValidFormat($formData.dateFormat).ex || m.common_invalid_format()}
</Form.Description>
{/snippet}
</Form.Control>
Expand Down
10 changes: 1 addition & 9 deletions src/lib/components/feature/vehicle/VehicleCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import BellRing from '@lucide/svelte/icons/bell-ring';
import Info from '@lucide/svelte/icons/info';
import { vehicleStore } from '$stores/vehicle.svelte';
import { browser } from '$app/environment';
import IconButton from '$appui/IconButton.svelte';
import DeleteConfirmation from '$appui/DeleteConfirmation.svelte';
import * as Card from '$ui/card';
Expand All @@ -36,21 +35,14 @@
const performDelete = async (vehicleId: string) => {
deleteVehicle(vehicleId).then((res) => {
if (res.status == 'OK') {
fetchVehicles();
vehicleStore.refreshVehicles();
toast.success(m.vehicle_delete_success());
} else {
toast.error(res.error || m.vehicle_delete_error());
}
});
};

const fetchVehicles = () => {
if (browser) {
const pin = localStorage.getItem('userPin') || undefined;
if (pin) vehicleStore.refreshVehicles();
}
};

// Dynamic image URL - fallback to default if vehicle doesn't have image
const imageUrl = $derived(
vehicle.image ? withBase(`/api/files/${vehicle.image}`) : '/default-vehicle.png'
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/ui/calendar/calendar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import type { ButtonVariant } from '../button/button.svelte';
import { isEqualMonth, type DateValue } from '@internationalized/date';
import type { Snippet } from 'svelte';
import { getLocale } from '$lib/paraglide/runtime.js';

let {
ref = $bindable(null),
Expand All @@ -14,7 +15,7 @@
weekdayFormat = 'short',
buttonVariant = 'ghost',
captionLayout = 'label',
locale = 'en-US',
locale = getLocale(),
months: monthsProp,
years,
monthFormat: monthFormatProp,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/domain/maintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const maintenanceSchema = z.object({
.string()
.min(2, 'It must be more than 1 character.')
.max(50, 'It must be less than 50 characters.'),
cost: z.float32().positive(),
cost: z.float32().nonnegative(),
notes: z.string().nullable(),
attachment: z.string().nullable()
});
Expand Down
18 changes: 18 additions & 0 deletions src/lib/helper/format.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@ const getMileageUnit = (vehicleType: string): string => {
return `${fuelLabel}/100${distanceUnit}`;
}

// only show uk mpg if miles and liters are used
if (
configs.mileageUnitFormat === 'uk-mpg' &&
configs.unitOfDistance === 'mile' &&
fuelUnit === 'liter'
) {
return 'mpg';
}

// Default: distance-per-fuel (e.g., km/L, mpg)
const mileageUnit = `${configs.unitOfDistance}-per-${fuelUnit}`;
const label = safeUnitLabel(mileageUnit);
Expand All @@ -230,6 +239,15 @@ const formatMileage = (mileage: number, vehicleType: string): string => {
return `${mileage.toFixed(2)} ${fuelLabel}/100${distanceUnit}`;
}

// only show uk mpg if miles and liters are used
if (
configs.mileageUnitFormat === 'uk-mpg' &&
configs.unitOfDistance === 'mile' &&
fuelUnit === 'liter'
) {
return `${mileage.toFixed(2)} mpg`;
}

// Default: distance-per-fuel (e.g., km/L, mpg)
const mileageUnit = `${configs.unitOfDistance}-per-${fuelUnit}`;
return (
Expand Down
Loading
Loading