diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte
index b4851a62..083cdaad 100644
--- a/frontend/src/routes/profile/+page.svelte
+++ b/frontend/src/routes/profile/+page.svelte
@@ -37,6 +37,17 @@
let themeMode = $state(getThemeMode());
let customTheme = $state(getCustomTheme() ?? 'dracula');
let themeMessage = $state<{ type: 'success' | 'error'; text: string } | null>(null);
+ let goalPagesPerDayEnabled = $state(false);
+ let goalPagesPerDay = $state(20);
+ let goalPagesPerMonthEnabled = $state(false);
+ let goalPagesPerMonth = $state(300);
+ let goalBooksPerMonthEnabled = $state(false);
+ let goalBooksPerMonth = $state(2);
+ let goalBooksPerYearEnabled = $state(false);
+ let goalBooksPerYear = $state(25);
+ let gamificationEnabled = $state(true);
+ let goalsMessage = $state<{ type: 'success' | 'error'; text: string } | null>(null);
+ let goalsSaving = $state(false);
let resetDataConfirmation = $state('');
let resetDataMessage = $state<{ type: 'success' | 'error'; text: string } | null>(null);
let deleteAccountConfirmation = $state('');
@@ -124,6 +135,15 @@
customTheme = settings.custom_theme;
setCustomTheme(customTheme);
}
+ goalPagesPerDayEnabled = settings.goal_pages_per_day_enabled;
+ goalPagesPerDay = settings.goal_pages_per_day;
+ goalPagesPerMonthEnabled = settings.goal_pages_per_month_enabled;
+ goalPagesPerMonth = settings.goal_pages_per_month;
+ goalBooksPerMonthEnabled = settings.goal_books_per_month_enabled;
+ goalBooksPerMonth = settings.goal_books_per_month;
+ goalBooksPerYearEnabled = settings.goal_books_per_year_enabled;
+ goalBooksPerYear = settings.goal_books_per_year;
+ gamificationEnabled = settings.gamification_enabled;
applyThemeToDocument();
saveThemeToStorage();
saveRestorePoint();
@@ -227,6 +247,40 @@
}
}
+ async function saveGoals() {
+ goalsMessage = null;
+ const targets = [
+ { enabled: goalPagesPerDayEnabled, value: goalPagesPerDay, key: $_('profile.goals.pagesPerDay') },
+ { enabled: goalPagesPerMonthEnabled, value: goalPagesPerMonth, key: $_('profile.goals.pagesPerMonth') },
+ { enabled: goalBooksPerMonthEnabled, value: goalBooksPerMonth, key: $_('profile.goals.booksPerMonth') },
+ { enabled: goalBooksPerYearEnabled, value: goalBooksPerYear, key: $_('profile.goals.booksPerYear') }
+ ];
+ const invalid = targets.find((t) => t.enabled && (!Number.isInteger(t.value) || t.value < 1));
+ if (invalid) {
+ goalsMessage = { type: 'error', text: $_('profile.goals.invalidTarget', { values: { name: invalid.key } }) };
+ return;
+ }
+ goalsSaving = true;
+ try {
+ await api.profile.updateSettings({
+ goal_pages_per_day_enabled: goalPagesPerDayEnabled,
+ goal_pages_per_day: goalPagesPerDay,
+ goal_pages_per_month_enabled: goalPagesPerMonthEnabled,
+ goal_pages_per_month: goalPagesPerMonth,
+ goal_books_per_month_enabled: goalBooksPerMonthEnabled,
+ goal_books_per_month: goalBooksPerMonth,
+ goal_books_per_year_enabled: goalBooksPerYearEnabled,
+ goal_books_per_year: goalBooksPerYear,
+ gamification_enabled: gamificationEnabled
+ });
+ goalsMessage = { type: 'success', text: $_('profile.goals.saveSuccess') };
+ } catch (e: unknown) {
+ goalsMessage = { type: 'error', text: e instanceof Error ? e.message : $_('common.saveFailed') };
+ } finally {
+ goalsSaving = false;
+ }
+ }
+
async function createKey() {
const result = await api.profile.createApiKey({ description: description || null });
createdKey = result.key;
@@ -565,6 +619,104 @@