diff --git a/package.json b/package.json index 5d9b638..f797ab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.21.0", + "version": "1.22.0", "description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view", "main": "index.js", "scripts": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5d0cc2c..6e37dd6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -98,6 +98,7 @@ model User { customWeekdayNames String? @default("") startDayOffset Int @default(-1) quoteSourceUrls String[] @default([]) + quoteLanguages String[] @default(["en", "de"]) accounts Account[] cachedCalendarEvents CachedCalendarEvent[] calendarConnections CalendarConnection[] diff --git a/src/app/api/goal/route.ts b/src/app/api/goal/route.ts index 0223b7e..5bbfce9 100644 --- a/src/app/api/goal/route.ts +++ b/src/app/api/goal/route.ts @@ -36,6 +36,7 @@ export async function GET(req: Request) { goalFallbackType: true, goalDefaultSentence: true, language: true, + quoteLanguages: true, } }); @@ -92,9 +93,11 @@ export async function GET(req: Request) { return NextResponse.json({ goal: holidayHint, isHoliday: true }); } - // 4. Fallback to ZenQuotes motivational quote (English only) - const userLang = user?.language || 'en'; - if (userLang === 'en') { + // 4. Fallback to ZenQuotes motivational quote (if English is in quote languages) + const quoteLangs: string[] = (user as any)?.quoteLanguages?.length > 0 + ? (user as any).quoteLanguages + : [user?.language || 'en']; + if (quoteLangs.includes('en')) { try { const res = await fetch('https://zenquotes.io/api/random', { signal: AbortSignal.timeout(3000) }); if (res.ok) { @@ -108,8 +111,9 @@ export async function GET(req: Request) { } } - // 5. Fallback to local curated quotes (supports DE and EN) - const localQuote = getRandomLocalQuote(userLang); + // 5. Fallback to local curated quotes (pick random from selected languages) + const randomLang = quoteLangs[Math.floor(Math.random() * quoteLangs.length)]; + const localQuote = getRandomLocalQuote(randomLang); if (localQuote) { return NextResponse.json({ goal: `${localQuote.text} — ${localQuote.author}`, isQuote: true }); } diff --git a/src/app/api/user/profile/route.ts b/src/app/api/user/profile/route.ts index 9c10ebe..cf9e593 100644 --- a/src/app/api/user/profile/route.ts +++ b/src/app/api/user/profile/route.ts @@ -88,6 +88,7 @@ export async function GET(request: NextRequest) { weekdayCase: true, customWeekdayNames: true, quoteSourceUrls: true, + quoteLanguages: true, accountNumber: true, createdAt: true } @@ -130,7 +131,7 @@ export async function PATCH(request: NextRequest) { cwFontFamily, cwFontSize, cwFontWeight, cwColor, yearFontFamily, yearFontSize, yearFontWeight, yearColor, showTaskCheckboxes, dayHeaderGap, - showCompletedTasks, showLines, startDayOffset, weekdayFormat, weekdayCase, customWeekdayNames, quoteSourceUrls + showCompletedTasks, showLines, startDayOffset, weekdayFormat, weekdayCase, customWeekdayNames, quoteSourceUrls, quoteLanguages } = body; const updateData: any = { @@ -208,6 +209,7 @@ export async function PATCH(request: NextRequest) { ...(weekdayCase !== undefined && { weekdayCase }), ...(customWeekdayNames !== undefined && { customWeekdayNames }), ...(quoteSourceUrls !== undefined && { quoteSourceUrls }), + ...(quoteLanguages !== undefined && { quoteLanguages }), }; if (password && password.trim() !== "") { updateData.passwordHash = await bcrypt.hash(password, 10); @@ -293,6 +295,7 @@ export async function PATCH(request: NextRequest) { weekdayCase: true, customWeekdayNames: true, quoteSourceUrls: true, + quoteLanguages: true, accountNumber: true, } }); diff --git a/src/components/WeeklyView.tsx b/src/components/WeeklyView.tsx index 16e033f..c559c2b 100644 --- a/src/components/WeeklyView.tsx +++ b/src/components/WeeklyView.tsx @@ -1126,6 +1126,7 @@ export default function WeeklyView() { dayHeaderGap?: string; showTaskCheckboxes?: boolean; quoteSourceUrls?: string[]; + quoteLanguages?: string[]; startDayOffset?: number; id?: string; accountNumber?: number; @@ -1195,6 +1196,7 @@ export default function WeeklyView() { yearFontWeight: "700", quoteSourceUrl: "", quoteSourceUrls: [], + quoteLanguages: ["en", "de"], }); const [motivationalQuote, setMotivationalQuote] = useState(""); const [showSummary, setShowSummary] = useState(false); @@ -1645,15 +1647,18 @@ export default function WeeklyView() { } } - // Final fallback: use local curated quotes - const lang = profile.language === "de" ? "de" : "en"; - const localQuote = getRandomLocalQuote(lang); + // Final fallback: use local curated quotes in user-selected languages + const quoteLangs = profile.quoteLanguages && profile.quoteLanguages.length > 0 + ? profile.quoteLanguages + : [profile.language || "en"]; + const randomLang = quoteLangs[Math.floor(Math.random() * quoteLangs.length)]; + const localQuote = getRandomLocalQuote(randomLang); if (localQuote) { setMotivationalQuote(`${localQuote.text} — ${localQuote.author}`); } else { - setMotivationalQuote(lang === "de" ? "Bleib fokussiert und produktiv." : "Stay focused and productive."); + setMotivationalQuote(randomLang === "de" ? "Bleib fokussiert und produktiv." : "Stay focused and productive."); } - }, [profile.goalFallbackType, profile.quoteSourceUrl, profile.quoteSourceUrls, profile.language]); + }, [profile.goalFallbackType, profile.quoteSourceUrl, profile.quoteSourceUrls, profile.language, profile.quoteLanguages]); // Fetch tasks on mount useEffect(() => { @@ -8003,9 +8008,11 @@ interface SettingsSidebarProps { showTaskCheckboxes?: boolean; startDayOffset?: number; quoteSourceUrls: string[]; + quoteLanguages: string[]; }) => void; setCurrentWeekStart: (d: Date) => void; quoteSourceUrls?: string[]; + quoteLanguages?: string[]; goal: string; setGoal: (goal: string) => void; saveGoal: (goal: string) => void; @@ -8434,6 +8441,7 @@ function SettingsSidebar({ dayHeaderGap?: string; showTaskCheckboxes?: boolean; quoteSourceUrls?: string[]; + quoteLanguages?: string[]; startDayOffset?: number; id?: string; accountNumber?: number; @@ -11775,8 +11783,82 @@ function SettingsSidebar({

{profile.language === "de" ? "Wenn keine externe Quelle antwortet, werden lokale kuratierte Zitate in Ihrer Sprache verwendet." + : profile.language === "fr" + ? "Si aucune source externe ne répond, des citations locales dans votre langue sont utilisées." + : profile.language === "es" + ? "Si ninguna fuente externa responde, se usan citas locales en tu idioma." + : profile.language === "it" + ? "Se nessuna fonte esterna risponde, vengono usate citazioni locali nella tua lingua." : "If no external source responds, curated local quotes in your language are used as fallback."}

+
+ +
+ {[ + { code: "en", label: "English" }, + { code: "de", label: "Deutsch" }, + { code: "fr", label: "Français" }, + { code: "es", label: "Español" }, + { code: "it", label: "Italiano" }, + ].map((lang) => { + const selected = (profile.quoteLanguages || ["en", "de"]).includes(lang.code); + return ( + + ); + })} +
+

+ {profile.language === "de" + ? "Wählen Sie die Sprachen für Ihre Zitate. Mindestens eine muss ausgewählt sein." + : profile.language === "fr" + ? "Choisissez les langues de vos citations. Au moins une doit être sélectionnée." + : profile.language === "es" + ? "Elige los idiomas de tus citas. Al menos uno debe estar seleccionado." + : profile.language === "it" + ? "Scegli le lingue delle citazioni. Almeno una deve essere selezionata." + : "Choose which languages your quotes appear in. At least one must be selected."} +

+
)} {profile.goalFallbackType === "default" && ( diff --git a/src/lib/quotes.ts b/src/lib/quotes.ts index 9debb09..e5ab37b 100644 --- a/src/lib/quotes.ts +++ b/src/lib/quotes.ts @@ -3,7 +3,7 @@ export interface LocalQuote { text: string; author: string; - language: "de" | "en"; + language: "de" | "en" | "fr" | "es" | "it"; tags: string[]; } @@ -525,6 +525,279 @@ export const LOCAL_QUOTES: LocalQuote[] = [ language: "en", tags: ["work", "perseverance", "success"], }, + // --- French Quotes --- + { + text: "Il n'y a qu'une façon d'échouer, c'est d'abandonner avant d'avoir réussi.", + author: "Olivier Lockert", + language: "fr", + tags: ["perseverance", "motivation"], + }, + { + text: "Le succès n'est pas final, l'échec n'est pas fatal : c'est le courage de continuer qui compte.", + author: "Winston Churchill", + language: "fr", + tags: ["perseverance", "success", "motivation"], + }, + { + text: "La vie, ce n'est pas d'attendre que les orages passent, c'est d'apprendre à danser sous la pluie.", + author: "Sénèque", + language: "fr", + tags: ["life", "wisdom", "perseverance"], + }, + { + text: "Ce n'est pas parce que les choses sont difficiles que nous n'osons pas, c'est parce que nous n'osons pas qu'elles sont difficiles.", + author: "Sénèque", + language: "fr", + tags: ["motivation", "wisdom"], + }, + { + text: "Le meilleur moment pour planter un arbre était il y a vingt ans. Le deuxième meilleur moment est maintenant.", + author: "Proverbe chinois", + language: "fr", + tags: ["motivation", "wisdom", "work"], + }, + { + text: "Celui qui déplace une montagne commence par déplacer de petites pierres.", + author: "Confucius", + language: "fr", + tags: ["perseverance", "motivation", "work"], + }, + { + text: "La simplicité est la sophistication suprême.", + author: "Léonard de Vinci", + language: "fr", + tags: ["wisdom", "creativity"], + }, + { + text: "Rien n'est permanent, sauf le changement.", + author: "Héraclite", + language: "fr", + tags: ["wisdom", "life"], + }, + { + text: "Fais de ta vie un rêve, et d'un rêve, une réalité.", + author: "Antoine de Saint-Exupéry", + language: "fr", + tags: ["motivation", "life", "creativity"], + }, + { + text: "L'imagination est plus importante que le savoir.", + author: "Albert Einstein", + language: "fr", + tags: ["creativity", "wisdom"], + }, + { + text: "On ne voit bien qu'avec le cœur. L'essentiel est invisible pour les yeux.", + author: "Antoine de Saint-Exupéry", + language: "fr", + tags: ["wisdom", "life"], + }, + { + text: "Le bonheur n'est pas quelque chose de prêt à l'emploi. Il vient de vos propres actions.", + author: "Dalaï Lama", + language: "fr", + tags: ["wisdom", "life", "motivation"], + }, + { + text: "Chaque jour est une nouvelle chance de changer ta vie.", + author: "Inconnu", + language: "fr", + tags: ["motivation", "life"], + }, + { + text: "Il faut toujours viser la lune, car même en cas d'échec, on atterrit dans les étoiles.", + author: "Oscar Wilde", + language: "fr", + tags: ["motivation", "success"], + }, + { + text: "La créativité, c'est l'intelligence qui s'amuse.", + author: "Albert Einstein", + language: "fr", + tags: ["creativity", "humor"], + }, + // --- Spanish Quotes --- + { + text: "No es la especie más fuerte la que sobrevive, sino la que mejor se adapta al cambio.", + author: "Charles Darwin", + language: "es", + tags: ["wisdom", "perseverance", "life"], + }, + { + text: "El único modo de hacer un gran trabajo es amar lo que haces.", + author: "Steve Jobs", + language: "es", + tags: ["work", "motivation", "success"], + }, + { + text: "La vida es lo que pasa mientras estás ocupado haciendo otros planes.", + author: "John Lennon", + language: "es", + tags: ["life", "wisdom"], + }, + { + text: "Sé tú mismo; todos los demás ya están ocupados.", + author: "Oscar Wilde", + language: "es", + tags: ["life", "wisdom", "humor"], + }, + { + text: "El mejor momento para plantar un árbol fue hace veinte años. El segundo mejor momento es ahora.", + author: "Proverbio chino", + language: "es", + tags: ["motivation", "wisdom", "work"], + }, + { + text: "La imaginación es más importante que el conocimiento.", + author: "Albert Einstein", + language: "es", + tags: ["creativity", "wisdom"], + }, + { + text: "No importa lo lento que vayas, siempre y cuando no te detengas.", + author: "Confucio", + language: "es", + tags: ["perseverance", "motivation"], + }, + { + text: "El secreto de ir adelante es empezar.", + author: "Mark Twain", + language: "es", + tags: ["motivation", "work", "success"], + }, + { + text: "La felicidad de tu vida depende de la calidad de tus pensamientos.", + author: "Marco Aurelio", + language: "es", + tags: ["wisdom", "life"], + }, + { + text: "Sé el cambio que deseas ver en el mundo.", + author: "Mahatma Gandhi", + language: "es", + tags: ["wisdom", "life", "motivation"], + }, + { + text: "La creatividad es la inteligencia divirtiéndose.", + author: "Albert Einstein", + language: "es", + tags: ["creativity", "humor"], + }, + { + text: "Cada día es una nueva oportunidad para cambiar tu vida.", + author: "Desconocido", + language: "es", + tags: ["motivation", "life"], + }, + { + text: "Lo que no te mata, te hace más fuerte.", + author: "Friedrich Nietzsche", + language: "es", + tags: ["perseverance", "motivation"], + }, + { + text: "Haz lo que puedas, con lo que tengas, donde estés.", + author: "Theodore Roosevelt", + language: "es", + tags: ["motivation", "work", "perseverance"], + }, + { + text: "Grandes cosas nunca vienen de la zona de confort.", + author: "Desconocido", + language: "es", + tags: ["motivation", "perseverance", "success"], + }, + // --- Italian Quotes --- + { + text: "La semplicità è la raffinatezza suprema.", + author: "Leonardo da Vinci", + language: "it", + tags: ["wisdom", "creativity"], + }, + { + text: "Nel mezzo delle difficoltà nascono le opportunità.", + author: "Albert Einstein", + language: "it", + tags: ["perseverance", "motivation", "success"], + }, + { + text: "L'unico modo di fare un ottimo lavoro è amare quello che fai.", + author: "Steve Jobs", + language: "it", + tags: ["work", "motivation", "success"], + }, + { + text: "Chi non osa nulla, non speri nulla.", + author: "Friedrich Schiller", + language: "it", + tags: ["motivation", "perseverance"], + }, + { + text: "La vita è quello che ti accade mentre sei impegnato a fare altri progetti.", + author: "John Lennon", + language: "it", + tags: ["life", "wisdom"], + }, + { + text: "Sii il cambiamento che vuoi vedere nel mondo.", + author: "Mahatma Gandhi", + language: "it", + tags: ["wisdom", "life", "motivation"], + }, + { + text: "Il miglior momento per piantare un albero era vent'anni fa. Il secondo miglior momento è adesso.", + author: "Proverbio cinese", + language: "it", + tags: ["motivation", "wisdom", "work"], + }, + { + text: "Non importa quanto vai piano, l'importante è non fermarsi.", + author: "Confucio", + language: "it", + tags: ["perseverance", "motivation"], + }, + { + text: "Il segreto per andare avanti è iniziare.", + author: "Mark Twain", + language: "it", + tags: ["motivation", "work", "success"], + }, + { + text: "La fantasia è più importante del sapere.", + author: "Albert Einstein", + language: "it", + tags: ["creativity", "wisdom"], + }, + { + text: "Quello che non ti uccide ti rende più forte.", + author: "Friedrich Nietzsche", + language: "it", + tags: ["perseverance", "motivation"], + }, + { + text: "La creatività è l'intelligenza che si diverte.", + author: "Albert Einstein", + language: "it", + tags: ["creativity", "humor"], + }, + { + text: "Ogni giorno è un nuovo inizio.", + author: "T.S. Eliot", + language: "it", + tags: ["motivation", "life"], + }, + { + text: "Fai quel che puoi, con quel che hai, dove sei.", + author: "Theodore Roosevelt", + language: "it", + tags: ["motivation", "work", "perseverance"], + }, + { + text: "Le grandi cose non vengono mai dalla zona di comfort.", + author: "Sconosciuto", + language: "it", + tags: ["motivation", "perseverance", "success"], + }, ]; /**