diff --git a/frontend/app/cards/page.tsx b/frontend/app/cards/page.tsx index f79cabc..7ad8ace 100644 --- a/frontend/app/cards/page.tsx +++ b/frontend/app/cards/page.tsx @@ -1,341 +1,341 @@ -'use client' - -import { useState } from 'react' -import { motion } from 'framer-motion' -import Navigation from '@/components/Navigation' -import VocabularyCard from '@/components/VocabularyCard' - -interface SavedCard { - id: string - word: string - language: string - definitions: string[] - examples: string[] - relatedWords: string[] - partOfSpeech: string - notes: string - tags: string[] - createdAt: string -} - -export default function SavedCardsPage() { - const [searchQuery, setSearchQuery] = useState('') - const [selectedLanguage, setSelectedLanguage] = useState('All') - const [selectedCard, setSelectedCard] = useState(null) - const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') - - // Mock data - const savedCards: SavedCard[] = [ - { - id: '1', - word: 'viajar', - language: 'Spanish', - partOfSpeech: 'verb', - definitions: [ - 'To travel or journey', - 'To move from one place to another', - 'To go on a trip' - ], - examples: [ - 'Me gusta viajar por el mundo.', - 'Vamos a viajar a España el próximo verano.', - 'Ella viaja mucho por su trabajo.' - ], - relatedWords: ['viaje', 'viajero', 'travesía', 'recorrer'], - notes: 'Remember: irregular conjugation in present tense. Similar to English "voyage".', - tags: ['travel', 'intermediate', 'common'], - createdAt: '2024-01-15' - }, - { - id: '2', - word: 'épanouir', - language: 'French', - partOfSpeech: 'verb', - definitions: [ - 'To blossom or flourish', - 'To develop fully', - 'To thrive emotionally or personally' - ], - examples: [ - "Les fleurs s'épanouissent au printemps.", - "Elle s'épanouit dans son nouveau travail.", - 'Un enfant a besoin de temps pour s\'épanouir.' - ], - relatedWords: ['épanouissement', 'fleurir', 'développer', 'prospérer'], - notes: 'Beautiful word with no direct English equivalent. Used both literally (flowers) and figuratively (personal growth).', - tags: ['nature', 'personal-growth', 'advanced'], - createdAt: '2024-01-18' - }, - { - id: '3', - word: 'Heimweh', - language: 'German', - partOfSpeech: 'noun', - definitions: [ - 'Homesickness', - 'Longing for home', - 'Nostalgia for one\'s homeland' - ], - examples: [ - 'Nach drei Monaten im Ausland bekam er Heimweh.', - 'Heimweh ist ein Gefühl, das viele Reisende kennen.', - 'Sie litt unter starkem Heimweh.' - ], - relatedWords: ['Fernweh', 'Sehnsucht', 'Nostalgie'], - notes: 'Compound word: Heim (home) + Weh (pain). Contrast with Fernweh (wanderlust).', - tags: ['emotions', 'travel', 'compound-words'], - createdAt: '2024-01-20' - } - ] - - const languages = ['All', 'Spanish', 'French', 'German', 'Italian', 'Portuguese', 'Japanese'] - - const filteredCards = savedCards.filter(card => { - const matchesSearch = card.word.toLowerCase().includes(searchQuery.toLowerCase()) - const matchesLanguage = selectedLanguage === 'All' || card.language === selectedLanguage - return matchesSearch && matchesLanguage - }) - - return ( -
- - -
-
- {/* Header */} - -
-
-

- Your Collection -

-

- {savedCards.length} cards saved • Keep building your knowledge base -

-
- -
- setViewMode('grid')} - className={`p-3 rounded-xl transition-all duration-300 ${ - viewMode === 'grid' ? 'bg-ink text-cream' : 'bg-sand text-sage hover:bg-sage/20' - }`} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - - - - setViewMode('list')} - className={`p-3 rounded-xl transition-all duration-300 ${ - viewMode === 'list' ? 'bg-ink text-cream' : 'bg-sand text-sage hover:bg-sage/20' - }`} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - - - -
-
- - {/* Filters */} -
-
- {/* Search */} -
- setSearchQuery(e.target.value)} - placeholder="Search your cards..." - className="input-field pl-12" - /> -
- - - -
-
- - {/* Language Filter */} -
- {languages.map((lang) => ( - setSelectedLanguage(lang)} - className={`px-4 py-2 rounded-full font-medium text-sm transition-all duration-300 ${ - selectedLanguage === lang - ? 'bg-gradient-to-r from-terracotta to-ochre text-white shadow-md' - : 'bg-sand text-sage hover:bg-sage/20' - }`} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - {lang} - - ))} -
-
-
-
- - {/* Cards Grid/List */} - {filteredCards.length > 0 ? ( -
- {filteredCards.map((card, idx) => ( - setSelectedCard(card)} - whileHover={{ scale: 1.02 }} - > -
- {/* Card Header */} -
-
-

- {card.word} -

-
- - {card.language} - - - {card.partOfSpeech} - -
-
- - - - - -
- - {/* Preview */} -
-
-

Definition

-

- {card.definitions[0]} -

-
- - {card.notes && ( -
-

- ✍️ Your Notes -

-

- {card.notes} -

-
- )} -
- - {/* Tags */} -
- {card.tags.map((tag) => ( - - #{tag} - - ))} -
- - {/* Date */} -
-

- Added {new Date(card.createdAt).toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric' - })} -

-
-
-
- ))} -
- ) : ( - -
- 🔍 -
-

- No cards found -

-

- Try adjusting your filters or search terms -

-
- )} -
-
- - {/* Card Detail Modal */} - {selectedCard && ( - setSelectedCard(null)} - > - e.stopPropagation()} - > -
- -
- -
-
- )} -
- ) -} +'use client' + +import { useState } from 'react' +import { motion } from 'framer-motion' +import Navigation from '@/components/Navigation' +import VocabularyCard from '@/components/VocabularyCard' + +interface SavedCard { + id: string + word: string + language: string + definitions: string[] + examples: { src: string; tgt: string }[] + relatedWords: string[] + partOfSpeech: string + notes: string + tags: string[] + createdAt: string +} + +export default function SavedCardsPage() { + const [searchQuery, setSearchQuery] = useState('') + const [selectedLanguage, setSelectedLanguage] = useState('All') + const [selectedCard, setSelectedCard] = useState(null) + const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') + + // Mock data + const savedCards: SavedCard[] = [ + { + id: '1', + word: 'viajar', + language: 'Spanish', + partOfSpeech: 'verb', + definitions: [ + 'To travel or journey', + 'To move from one place to another', + 'To go on a trip' + ], + examples: [ + { src: 'Me gusta viajar por el mundo.', tgt: 'I like to travel the world.' }, + { src: 'Vamos a viajar a España el próximo verano.', tgt: 'We are going to travel to Spain next summer.' }, + { src: 'Ella viaja mucho por su trabajo.', tgt: 'She travels a lot for work.' } + ], + relatedWords: ['viaje', 'viajero', 'travesía', 'recorrer'], + notes: 'Remember: irregular conjugation in present tense. Similar to English "voyage".', + tags: ['travel', 'intermediate', 'common'], + createdAt: '2024-01-15' + }, + { + id: '2', + word: 'épanouir', + language: 'French', + partOfSpeech: 'verb', + definitions: [ + 'To blossom or flourish', + 'To develop fully', + 'To thrive emotionally or personally' + ], + examples: [ + { src: "Les fleurs s'épanouissent au printemps.", tgt: 'The flowers blossom in spring.' }, + { src: "Elle s'épanouit dans son nouveau travail.", tgt: 'She is flourishing in her new job.' }, + { src: "Un enfant a besoin de temps pour s'épanouir.", tgt: 'A child needs time to develop fully.' } + ], + relatedWords: ['épanouissement', 'fleurir', 'développer', 'prospérer'], + notes: 'Beautiful word with no direct English equivalent. Used both literally (flowers) and figuratively (personal growth).', + tags: ['nature', 'personal-growth', 'advanced'], + createdAt: '2024-01-18' + }, + { + id: '3', + word: 'Heimweh', + language: 'German', + partOfSpeech: 'noun', + definitions: [ + 'Homesickness', + 'Longing for home', + 'Nostalgia for one\'s homeland' + ], + examples: [ + { src: 'Nach drei Monaten im Ausland bekam er Heimweh.', tgt: 'After three months abroad, he got homesick.' }, + { src: 'Heimweh ist ein Gefühl, das viele Reisende kennen.', tgt: 'Homesickness is a feeling that many travelers know.' }, + { src: 'Sie litt unter starkem Heimweh.', tgt: 'She suffered from severe homesickness.' } + ], + relatedWords: ['Fernweh', 'Sehnsucht', 'Nostalgie'], + notes: 'Compound word: Heim (home) + Weh (pain). Contrast with Fernweh (wanderlust).', + tags: ['emotions', 'travel', 'compound-words'], + createdAt: '2024-01-20' + } + ] + + const languages = ['All', 'Spanish', 'French', 'German', 'Italian', 'Portuguese', 'Japanese'] + + const filteredCards = savedCards.filter(card => { + const matchesSearch = card.word.toLowerCase().includes(searchQuery.toLowerCase()) + const matchesLanguage = selectedLanguage === 'All' || card.language === selectedLanguage + return matchesSearch && matchesLanguage + }) + + return ( +
+ + +
+
+ {/* Header */} + +
+
+

+ Your Collection +

+

+ {savedCards.length} cards saved • Keep building your knowledge base +

+
+ +
+ setViewMode('grid')} + className={`p-3 rounded-xl transition-all duration-300 ${ + viewMode === 'grid' ? 'bg-ink text-cream' : 'bg-sand text-sage hover:bg-sage/20' + }`} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + + + + setViewMode('list')} + className={`p-3 rounded-xl transition-all duration-300 ${ + viewMode === 'list' ? 'bg-ink text-cream' : 'bg-sand text-sage hover:bg-sage/20' + }`} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + + + +
+
+ + {/* Filters */} +
+
+ {/* Search */} +
+ setSearchQuery(e.target.value)} + placeholder="Search your cards..." + className="input-field pl-12" + /> +
+ + + +
+
+ + {/* Language Filter */} +
+ {languages.map((lang) => ( + setSelectedLanguage(lang)} + className={`px-4 py-2 rounded-full font-medium text-sm transition-all duration-300 ${ + selectedLanguage === lang + ? 'bg-gradient-to-r from-terracotta to-ochre text-white shadow-md' + : 'bg-sand text-sage hover:bg-sage/20' + }`} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {lang} + + ))} +
+
+
+
+ + {/* Cards Grid/List */} + {filteredCards.length > 0 ? ( +
+ {filteredCards.map((card, idx) => ( + setSelectedCard(card)} + whileHover={{ scale: 1.02 }} + > +
+ {/* Card Header */} +
+
+

+ {card.word} +

+
+ + {card.language} + + + {card.partOfSpeech} + +
+
+ + + + + +
+ + {/* Preview */} +
+
+

Definition

+

+ {card.definitions[0]} +

+
+ + {card.notes && ( +
+

+ ✍️ Your Notes +

+

+ {card.notes} +

+
+ )} +
+ + {/* Tags */} +
+ {card.tags.map((tag) => ( + + #{tag} + + ))} +
+ + {/* Date */} +
+

+ Added {new Date(card.createdAt).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric' + })} +

+
+
+
+ ))} +
+ ) : ( + +
+ 🔍 +
+

+ No cards found +

+

+ Try adjusting your filters or search terms +

+
+ )} +
+
+ + {/* Card Detail Modal */} + {selectedCard && ( + setSelectedCard(null)} + > + e.stopPropagation()} + > +
+ +
+ +
+
+ )} +
+ ) +} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 1495627..957f6eb 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,257 +1,254 @@ -'use client' - -import { useState } from 'react' -import { motion, AnimatePresence } from 'framer-motion' -import Navigation from '@/components/Navigation' -import VocabularyCard from '@/components/VocabularyCard' -import ChatAssistant from '@/components/ChatAssistant' - -export default function LookupPage() { - const [searchQuery, setSearchQuery] = useState('') - const [selectedLanguage, setSelectedLanguage] = useState('Spanish') - const [searchResult, setSearchResult] = useState(null) - const [isSearching, setIsSearching] = useState(false) - const [showChat, setShowChat] = useState(false) - - const languages = ['Spanish', 'French', 'German', 'Italian', 'Portuguese', 'Japanese', 'Korean', 'Mandarin'] - - const handleSearch = async () => { - if (!searchQuery.trim()) return - - setIsSearching(true) - - // Simulate API call - setTimeout(() => { - setSearchResult({ - word: searchQuery, - language: selectedLanguage, - partOfSpeech: 'verb', - definitions: [ - 'To move or go from one place to another', - 'To make a journey, especially of some length or abroad', - 'To move or proceed in a particular direction' - ], - examples: [ - 'We plan to travel to Europe next summer for three weeks.', - 'She travels frequently for work, visiting different cities each month.', - 'Light travels faster than sound through different mediums.' - ], - relatedWords: ['journey', 'voyage', 'trip', 'trek', 'wander', 'roam'] - }) - setIsSearching(false) - }, 1200) - } - - const handleSave = () => { - alert('Card saved to your collection!') - } - - return ( -
- - -
-
- {/* Hero Section */} - - -
- 🔍 -
-
- -

- Master Every Word -

-

- Search any vocabulary word and get instant, comprehensive notes with definitions, examples, and context—auto-populated by AI. -

-
- - {/* Search Section */} - - {/* Language Selector */} -
- -
- {languages.map((lang) => ( - setSelectedLanguage(lang)} - className={`px-5 py-2 rounded-full font-medium transition-all duration-300 ${ - selectedLanguage === lang - ? 'bg-gradient-to-r from-terracotta to-ochre text-white shadow-md' - : 'bg-sand text-sage hover:bg-sage/20' - }`} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - {lang} - - ))} -
-
- - {/* Search Input */} -
-
- setSearchQuery(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && handleSearch()} - placeholder="Enter a word to look up..." - className="input-field pl-12 text-lg" - /> -
- - - -
-
- - - {isSearching ? ( - <> - - Searching... - - ) : ( - <> - Search - - - - - )} - -
- - {/* Quick Tips */} - -

- 💡 - - Tip: After searching, you can ask our AI assistant about nuances, usage patterns, and contextual differences. - -

-
-
- - {/* Search Results */} - - {searchResult && ( - - - - {/* Action Buttons */} - - setShowChat(true)} - className="btn-secondary flex items-center gap-2" - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - - - Ask About Nuances - - - setSearchResult(null)} - className="px-6 py-3 rounded-full font-medium border-2 border-sand text-sage hover:border-sage hover:bg-sage hover:text-white transition-all duration-300" - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - New Search - - - - )} - - - {/* Empty State */} - {!searchResult && !isSearching && ( - -
- 📚 -
-

- Ready to explore? -

-

- Enter a word above to get started with your vocabulary mastery journey. -

-
- )} -
-
- - {/* Chat Assistant Modal */} - - {showChat && searchResult && ( - setShowChat(false)} - /> - )} - -
- ) -} +'use client' +import { apiClient } from '@/lib/api' + +import { useState } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import Navigation from '@/components/Navigation' +import VocabularyCard from '@/components/VocabularyCard' +import ChatAssistant from '@/components/ChatAssistant' + +export default function LookupPage() { + const [searchQuery, setSearchQuery] = useState('') + const [selectedLanguage, setSelectedLanguage] = useState('') + const [searchResult, setSearchResult] = useState(null) + const [isSearching, setIsSearching] = useState(false) + const [showChat, setShowChat] = useState(false) + +const languageCodes: Record = { + 'Spanish': 'es', 'French': 'fr', 'German': 'de', 'Italian': 'it', + 'Portuguese': 'pt', 'Japanese': 'ja', 'Korean': 'ko', 'Mandarin': 'zh' +} + + const handleSearch = async () => { + if (!searchQuery.trim()) return + setIsSearching(true) + try { + const data = await apiClient.lookup({ lang: languageCodes[selectedLanguage], lemma: searchQuery.trim() }) + const card = data.card + setSearchResult({ + word: card.lemma || searchQuery, + language: selectedLanguage, + partOfSpeech: card.partOfSpeech, + definitions: [card.shortDefinition], + examples: card.examples.map((e: { src: string; tgt: string }) => ({ src: e.src, tgt: e.tgt })), + relatedWords: card.relatedWords || [] + }) + } catch { + alert('Word not found. Try another word.') + } finally { + setIsSearching(false) + } + } + const handleSave = () => { + alert('Card saved to your collection!') + } + + return ( +
+ + +
+
+ {/* Hero Section */} + + +
+ 🔍 +
+
+ +

+ Master Every Word +

+

+ Search any vocabulary word and get instant, comprehensive notes with definitions, examples, and context—auto-populated by AI. +

+
+ + {/* Search Section */} + + {/* Language Selector */} +
+ +
+ {Object.keys(languageCodes).map((lang) => ( + setSelectedLanguage(lang)} + className={`px-5 py-2 rounded-full font-medium transition-all duration-300 ${ + selectedLanguage === lang + ? 'bg-gradient-to-r from-terracotta to-ochre text-white shadow-md' + : 'bg-sand text-sage hover:bg-sage/20' + }`} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + {lang} + + ))} +
+
+ + {/* Search Input */} +
+
+ setSearchQuery(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleSearch()} + placeholder="Enter a word to look up..." + className="input-field pl-12 text-lg" + /> +
+ + + +
+
+ + + {isSearching ? ( + <> + + Searching... + + ) : ( + <> + Search + + + + + )} + +
+ + {/* Quick Tips */} + +

+ 💡 + + Tip: After searching, you can ask our AI assistant about nuances, usage patterns, and contextual differences. + +

+
+
+ + {/* Search Results */} + + {searchResult && ( + + + + {/* Action Buttons */} + + setShowChat(true)} + className="btn-secondary flex items-center gap-2" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + + + Ask About Nuances + + + setSearchResult(null)} + className="px-6 py-3 rounded-full font-medium border-2 border-sand text-sage hover:border-sage hover:bg-sage hover:text-white transition-all duration-300" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + New Search + + + + )} + + + {/* Empty State */} + {!searchResult && !isSearching && ( + +
+ 📚 +
+

+ Ready to explore? +

+

+ Enter a word above to get started with your vocabulary mastery journey. +

+
+ )} +
+
+ + {/* Chat Assistant Modal */} + + {showChat && searchResult && ( + setShowChat(false)} + /> + )} + +
+ ) +} diff --git a/frontend/components/VocabularyCard.tsx b/frontend/components/VocabularyCard.tsx index 17f870b..053a709 100644 --- a/frontend/components/VocabularyCard.tsx +++ b/frontend/components/VocabularyCard.tsx @@ -1,211 +1,214 @@ -'use client' - -import { motion } from 'framer-motion' -import { useState } from 'react' - -interface VocabularyCardProps { - word: string - language: string - definitions: string[] - examples: string[] - relatedWords: string[] - partOfSpeech?: string - onSave?: () => void - isPreview?: boolean -} - -export default function VocabularyCard({ - word, - language, - definitions, - examples, - relatedWords, - partOfSpeech, - onSave, - isPreview = false -}: VocabularyCardProps) { - const [notes, setNotes] = useState('') - const [showNotes, setShowNotes] = useState(false) - - return ( - - {/* Header */} -
-
-
- - {word} - - {partOfSpeech && ( - - {partOfSpeech} - - )} -
-

- {language} -

-
- - {!isPreview && onSave && ( - - - - - Save Card - - )} -
- - {/* Definitions */} - -

- - 📖 - - Definitions -

-
    - {definitions.map((def, idx) => ( - - - {idx + 1}. - - {def} - - ))} -
-
- - {/* Examples */} - -

- - 💬 - - Example Sentences -

-
- {examples.map((example, idx) => ( - -

- "{example}" -

-
- ))} -
-
- - {/* Related Words */} - -

- - 🔗 - - Related Words -

-
- {relatedWords.map((word, idx) => ( - - {word} - - ))} -
-
- - {/* Personal Notes Section */} - {!isPreview && ( - - - - {showNotes && ( - -