From 2ec1278b19aee00c79b05d16352bcb531a9f6d18 Mon Sep 17 00:00:00 2001 From: mARTin Date: Sun, 5 Apr 2026 12:50:46 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20quote=20sources=20=E2=80=94=20preset=20?= =?UTF-8?q?buttons,=20universal=20parser,=20expanded=20local=20collection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Goal API (route.ts): - Now actually uses profile.quoteSourceUrls — tries each user-configured URL in order before falling back to ZenQuotes - Universal JSON parser handles ZenQuotes [{q,a}], Quotable {content,author}, Forismatic {quoteText,quoteAuthor}, generic {text/quote/body, author/by}, nested {data:{...}}, and advice-slip {slip:{advice}} formats - ZenQuotes remains the built-in EN fallback when no user URLs are configured SettingsSidebar: - Adds clickable preset-source pill buttons below the "Add source" button: ZenQuotes, Stoic Quotes, Quotable, Forismatic (EN), Advice Slip - Pills show ✓ and are disabled when the URL is already in the list - Note explains DE/FR/IT/ES are served from the curated local collection quotes.ts: - Updated PRESET_QUOTE_SOURCES to match the 5 working English APIs - +14 French quotes (Marcus Aurelius, Lombardi, Ali, MLK, Picasso, etc.) - +13 Spanish quotes (Collier, Eliot, Darwin, Voltaire, Ashe, etc.) - +13 Italian quotes (Collier, Eliot, Ali, MLK, Aurelius, da Vinci, etc.) v1.84.0 --- package.json | 2 +- src/app/api/goal/route.ts | 46 +++++- src/components/SettingsSidebar.tsx | 48 ++++++ src/lib/quotes.ts | 251 ++++++++++++++++++++++++++--- 4 files changed, 321 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index e2d075d..ae82050 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "my-weekly-todo-list", - "version": "1.83.4", + "version": "1.84.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/src/app/api/goal/route.ts b/src/app/api/goal/route.ts index 440dfe9..62211c0 100644 --- a/src/app/api/goal/route.ts +++ b/src/app/api/goal/route.ts @@ -33,6 +33,7 @@ export async function GET(req: Request) { goalDefaultSentence: true, language: true, quoteLanguages: true, + quoteSourceUrls: true, } }); @@ -92,10 +93,53 @@ export async function GET(req: Request) { return NextResponse.json({ goal: holidayHint, isHoliday: true }); } - // 4. Fallback to ZenQuotes motivational quote (if English is in quote languages) + // 4. Try user-configured quote source URLs, then ZenQuotes const quoteLangs: string[] = (user as any)?.quoteLanguages?.length > 0 ? (user as any).quoteLanguages : [user?.language || 'en']; + + // Universal JSON quote parser — handles the most common free API response shapes + const parseQuoteResponse = (data: any): string | null => { + try { + // Array responses: pick a random element + if (Array.isArray(data)) { + if (data.length === 0) return null; + data = data[Math.floor(Math.random() * data.length)]; + } + // ZenQuotes: [{q, a}] + if (data.q && data.a) return `${data.q} — ${data.a}`; + // Quotable: {content, author} + if (data.content && data.author) return `${data.content} — ${data.author}`; + // Forismatic: {quoteText, quoteAuthor} + if (data.quoteText) return data.quoteAuthor ? `${data.quoteText.trim()} — ${data.quoteAuthor.trim()}` : data.quoteText.trim(); + // Common: {text, author} or {quote, author} + const text = data.text || data.quote || data.body || data.sentence; + const author = data.author || data.by || data.from || data.speaker; + if (text) return author ? `${text} — ${author}` : text; + // Nested: {data: {quote, author}} + if (data.data) return parseQuoteResponse(data.data); + // Nested: {slip: {advice}} + if (data.slip?.advice) return data.slip.advice; + } catch { /* ignore */ } + return null; + }; + + // Try each user-configured URL first + const userUrls: string[] = (user as any)?.quoteSourceUrls?.filter(Boolean) || []; + for (const url of userUrls) { + try { + const res = await fetch(url, { signal: AbortSignal.timeout(4000) }); + if (res.ok) { + const data = await res.json(); + const parsed = parseQuoteResponse(data); + if (parsed) return NextResponse.json({ goal: parsed, isQuote: true }); + } + } catch (e) { + console.error(`Failed to fetch from ${url}:`, e); + } + } + + // ZenQuotes as built-in English fallback if (quoteLangs.includes('en')) { try { const res = await fetch('https://zenquotes.io/api/random', { signal: AbortSignal.timeout(3000) }); diff --git a/src/components/SettingsSidebar.tsx b/src/components/SettingsSidebar.tsx index 6ceb66d..d1f6174 100644 --- a/src/components/SettingsSidebar.tsx +++ b/src/components/SettingsSidebar.tsx @@ -3771,6 +3771,54 @@ function SettingsSidebar({ > {t.addSource} + + {/* Preset sources */} +
+

+ {profile.language === "de" ? "Bekannte Quellen (klicken zum Hinzufügen):" : "Known sources (click to add):"} +

+
+ {[ + { label: "ZenQuotes (EN)", url: "https://zenquotes.io/api/random" }, + { label: "Stoic Quotes (EN)", url: "https://stoic.tekloon.net/stoic-quote" }, + { label: "Quotable (EN)", url: "https://api.quotable.io/quotes/random" }, + { label: "Forismatic (EN)", url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en" }, + { label: "Advice Slip (EN)", url: "https://api.adviceslip.com/advice" }, + ].map(({ label, url }) => { + const current: string[] = profile.quoteSourceUrls || []; + const already = current.includes(url); + return ( + + ); + })} +
+

+ {profile.language === "de" + ? "Für DE, FR, IT, ES werden kuratierte lokale Zitate verwendet." + : "For DE, FR, IT, ES, curated local quotes are used automatically."} +

+

{t.urlFormatHelp} diff --git a/src/lib/quotes.ts b/src/lib/quotes.ts index a69ea98..33e9fc3 100644 --- a/src/lib/quotes.ts +++ b/src/lib/quotes.ts @@ -8,30 +8,11 @@ export interface LocalQuote { } export const PRESET_QUOTE_SOURCES = [ - { - name: "ZenQuotes", - url: "https://zenquotes.io/api/random", - language: "en", - tags: ["motivation", "wisdom", "life"], - }, - { - name: "Quotable", - url: "https://api.quotable.io/random", - language: "en", - tags: ["motivation", "wisdom", "success", "life"], - }, - { - name: "Forismatic", - url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=de", - language: "de", - tags: ["motivation", "wisdom", "life"], - }, - { - name: "Type.fit", - url: "https://type.fit/api/quotes", - language: "en", - tags: ["motivation", "wisdom"], - }, + { name: "ZenQuotes", url: "https://zenquotes.io/api/random", language: "en" }, + { name: "Stoic Quotes", url: "https://stoic.tekloon.net/stoic-quote", language: "en" }, + { name: "Quotable", url: "https://api.quotable.io/quotes/random", language: "en" }, + { name: "Forismatic (EN)", url: "https://api.forismatic.com/api/1.0/?method=getQuote&format=json&lang=en", language: "en" }, + { name: "Advice Slip", url: "https://api.adviceslip.com/advice", language: "en" }, ] as const; export const LOCAL_QUOTES: LocalQuote[] = [ @@ -616,6 +597,84 @@ export const LOCAL_QUOTES: LocalQuote[] = [ language: "fr", tags: ["creativity", "humor"], }, + { + text: "Vous avez du pouvoir sur votre esprit, pas sur les événements extérieurs. Réalisez cela, et vous trouverez la force.", + author: "Marc Aurèle", + language: "fr", + tags: ["wisdom", "motivation", "perseverance"], + }, + { + text: "Le succès c'est tomber sept fois et se relever huit.", + author: "Proverbe japonais", + language: "fr", + tags: ["perseverance", "motivation", "success"], + }, + { + text: "Votre temps est limité, ne le gâchez pas en vivant la vie de quelqu'un d'autre.", + author: "Steve Jobs", + language: "fr", + tags: ["life", "wisdom", "motivation"], + }, + { + text: "La perfection n'est pas atteignable. Mais en visant la perfection, nous pouvons atteindre l'excellence.", + author: "Vince Lombardi", + language: "fr", + tags: ["success", "motivation", "work"], + }, + { + text: "Commencez là où vous êtes. Utilisez ce que vous avez. Faites ce que vous pouvez.", + author: "Arthur Ashe", + language: "fr", + tags: ["motivation", "work", "perseverance"], + }, + { + text: "Les grandes choses ne viennent jamais de la zone de confort.", + author: "Inconnu", + language: "fr", + tags: ["motivation", "perseverance", "success"], + }, + { + text: "Ne comptez pas les jours, faites compter les jours.", + author: "Muhammad Ali", + language: "fr", + tags: ["motivation", "work", "life"], + }, + { + text: "Ce n'est pas parce que c'est difficile que nous n'osons pas, c'est parce que nous n'osons pas que c'est difficile.", + author: "Sénèque", + language: "fr", + tags: ["motivation", "wisdom", "perseverance"], + }, + { + text: "Tout ce que vous pouvez imaginer est réel.", + author: "Pablo Picasso", + language: "fr", + tags: ["creativity", "motivation"], + }, + { + text: "La vie n'est pas d'attendre que la tempête passe, c'est d'apprendre à danser sous la pluie.", + author: "Vivian Greene", + language: "fr", + tags: ["life", "perseverance", "wisdom"], + }, + { + text: "Si vous ne pouvez pas voler, courez. Si vous ne pouvez pas courir, marchez. Si vous ne pouvez pas marcher, rampez. Mais continuez d'avancer.", + author: "Martin Luther King Jr.", + language: "fr", + tags: ["perseverance", "motivation"], + }, + { + text: "La seule façon de faire du bon travail est d'aimer ce que vous faites.", + author: "Steve Jobs", + language: "fr", + tags: ["work", "motivation", "success"], + }, + { + text: "Chaque expert a d'abord été un débutant.", + author: "Helen Hayes", + language: "fr", + tags: ["perseverance", "motivation", "work"], + }, // --- Spanish Quotes --- { text: "No es la especie más fuerte la que sobrevive, sino la que mejor se adapta al cambio.", @@ -707,6 +766,78 @@ export const LOCAL_QUOTES: LocalQuote[] = [ language: "es", tags: ["motivation", "perseverance", "success"], }, + { + text: "El éxito es la suma de pequeños esfuerzos repetidos día tras día.", + author: "Robert Collier", + language: "es", + tags: ["success", "perseverance", "work"], + }, + { + text: "Nunca es tarde para ser lo que podrías haber sido.", + author: "George Eliot", + language: "es", + tags: ["motivation", "life", "perseverance"], + }, + { + text: "No cuentes los días, haz que los días cuenten.", + author: "Muhammad Ali", + language: "es", + tags: ["motivation", "work", "life"], + }, + { + text: "Si no puedes volar, corre. Si no puedes correr, camina. Si no puedes caminar, arrástrate. Pero sigue adelante.", + author: "Martin Luther King Jr.", + language: "es", + tags: ["perseverance", "motivation"], + }, + { + text: "Tienes poder sobre tu mente, no sobre los eventos externos. Date cuenta de esto y encontrarás fuerza.", + author: "Marco Aurelio", + language: "es", + tags: ["wisdom", "motivation", "perseverance"], + }, + { + text: "El único modo de hacer un gran trabajo es amar lo que haces.", + author: "Steve Jobs", + language: "es", + tags: ["work", "motivation", "success"], + }, + { + text: "No importa cuán lento vayas, siempre y cuando no te detengas.", + author: "Confucio", + language: "es", + tags: ["perseverance", "motivation"], + }, + { + text: "Todo experto fue alguna vez un principiante.", + author: "Helen Hayes", + language: "es", + tags: ["perseverance", "motivation", "work"], + }, + { + text: "El futuro pertenece a quienes creen en la belleza de sus sueños.", + author: "Eleanor Roosevelt", + language: "es", + tags: ["motivation", "life", "creativity"], + }, + { + text: "No es la especie más fuerte la que sobrevive, sino la más adaptable al cambio.", + author: "Charles Darwin", + language: "es", + tags: ["wisdom", "perseverance", "life"], + }, + { + text: "La perseverancia es el padre del éxito.", + author: "Voltaire", + language: "es", + tags: ["perseverance", "success", "work"], + }, + { + text: "Comienza donde estás. Usa lo que tienes. Haz lo que puedes.", + author: "Arthur Ashe", + language: "es", + tags: ["motivation", "work", "perseverance"], + }, // --- Italian Quotes --- { text: "La semplicità è la raffinatezza suprema.", @@ -798,6 +929,78 @@ export const LOCAL_QUOTES: LocalQuote[] = [ language: "it", tags: ["motivation", "perseverance", "success"], }, + { + text: "Il successo è la somma di piccoli sforzi ripetuti giorno dopo giorno.", + author: "Robert Collier", + language: "it", + tags: ["success", "perseverance", "work"], + }, + { + text: "Non è mai troppo tardi per essere ciò che avresti potuto essere.", + author: "George Eliot", + language: "it", + tags: ["motivation", "life", "perseverance"], + }, + { + text: "Non contare i giorni, fai sì che i giorni contino.", + author: "Muhammad Ali", + language: "it", + tags: ["motivation", "work", "life"], + }, + { + text: "Se non puoi volare, corri. Se non puoi correre, cammina. Se non puoi camminare, striscia. Ma continua ad andare avanti.", + author: "Martin Luther King Jr.", + language: "it", + tags: ["perseverance", "motivation"], + }, + { + text: "Hai potere sulla tua mente, non sugli eventi esterni. Renditi conto di questo e troverai la forza.", + author: "Marco Aurelio", + language: "it", + tags: ["wisdom", "motivation", "perseverance"], + }, + { + text: "Ogni esperto è stato prima un principiante.", + author: "Helen Hayes", + language: "it", + tags: ["perseverance", "motivation", "work"], + }, + { + text: "Il futuro appartiene a coloro che credono nella bellezza dei propri sogni.", + author: "Eleanor Roosevelt", + language: "it", + tags: ["motivation", "life", "creativity"], + }, + { + text: "Inizia dove sei. Usa quello che hai. Fai quello che puoi.", + author: "Arthur Ashe", + language: "it", + tags: ["motivation", "work", "perseverance"], + }, + { + text: "La semplicità è la massima sofisticazione.", + author: "Leonardo da Vinci", + language: "it", + tags: ["wisdom", "creativity"], + }, + { + text: "Il coraggio non è l'assenza di paura, ma il giudizio che qualcos'altro è più importante della paura.", + author: "Ambrose Redmoon", + language: "it", + tags: ["motivation", "perseverance", "wisdom"], + }, + { + text: "Le grandi menti discutono di idee; le menti medie discutono di eventi; le piccole menti discutono di persone.", + author: "Eleanor Roosevelt", + language: "it", + tags: ["wisdom", "creativity"], + }, + { + text: "Sii tu il cambiamento che vuoi vedere nel mondo.", + author: "Mahatma Gandhi", + language: "it", + tags: ["wisdom", "life", "motivation"], + }, ]; /**