navbar improvements and removed dear jane

This commit is contained in:
Harivansh Rathi 2024-12-04 12:53:36 -05:00
parent fce42be0b3
commit 9609248b69
6 changed files with 201 additions and 486 deletions

View file

@ -30,7 +30,6 @@ src/
│ ├── Navbar.tsx
│ ├── Pagination.tsx
│ ├── QuoteDisplay.tsx
│ ├── Routes.tsx
│ ├── ShareButtons.tsx
│ └── theme-provider.tsx
@ -38,22 +37,21 @@ src/
│ ├── blog-posts.ts # Character blog content
│ ├── quotes.ts # Austen quotes
│ ├── quiz.ts # Character quiz data
│ ├── dear-jane.ts # Advice column content
│ ├── success-stories.ts
│ ├── literary-analysis.ts # Literary analysis data
│ ├── comparative-analysis.ts # Comparative analysis data
│ └── vendors.ts
├── pages/
│ ├── BlogPost/
│ ├── Advice.tsx
│ ├── Analysis.tsx # Literary analysis page
│ ├── Blogs.tsx
│ ├── DearJane.tsx
│ ├── Home.tsx
│ ├── MarketCalculator.tsx
│ ├── Quiz.tsx
│ ├── Stories.tsx
│ ├── SuccessStories.tsx
│ ├── ComparativeAnalysis.tsx # Comparative analysis page
│ └── Vendors.tsx
```
@ -67,20 +65,22 @@ The application uses React Router for navigation with the following route struct
<Route path="/blogs" element={<Blogs />} />
<Route path="/blogs/:id" element={<BlogPost />} />
<Route path="/quiz" element={<Quiz />} />
<Route path="/advice" element={<Advice />} />
<Route path="/vendors" element={<Vendors />} />
<Route path="/success-stories" element={<SuccessStories />} />
<Route path="/market-calculator" element={<MarketCalculator />} />
<Route path="/analysis" element={<Analysis />} />
<Route path="/comparative" element={<ComparativeAnalysis />} />
<Route path="/network" element={<NetworkVisualization />} />
</Routes>
```
### Navigation Updates
- Main navigation in `Navbar.tsx` includes:
- Primary navigation links
- Literary Analysis link with distinct styling
- Responsive design for all screen sizes
- Primary navigation group with consistent button styling
- Secondary navigation group for analysis features
- Visual separation between groups using border
- All buttons styled with sage color scheme
- Home page features:
- Literary Analysis card in featured sections
- Direct link to analysis content
@ -92,6 +92,8 @@ The application uses React Router for navigation with the following route struct
### Route Integration
- Analysis page is directly accessible via `/analysis`
- Comparative analysis accessible via `/comparative`
- Character network visualization via `/network`
- Integrated into main navigation flow
- Maintains consistent layout and styling
- Proper error handling and loading states
@ -123,6 +125,8 @@ The application uses React Router for navigation with the following route struct
- Pagination for content lists
- Market calculator
- **New: Literary analysis with themed tabs and novel selection**
- **New: Comparative analysis for cross-novel exploration**
- **New: Character network visualization**
### 4. Performance Considerations
@ -297,3 +301,157 @@ const novelAnalyses = {
5. Content management system integration
6. **New: Additional novel analysis features**
7. **New: Comparative analysis tools**
## Character Network Feature
### Interactive Graph Component
The character network visualization is implemented using a force-directed graph with the following features:
```typescript
interface CharacterNode {
id: string;
name: string;
novel: string;
class: string;
type: "protagonist" | "antagonist" | "supporting";
}
interface GraphConfig {
nodeRelSize: number;
nodeVal: number;
width: number;
height: number;
staticGraph: boolean;
enableNodeDrag: boolean;
enableZoom: boolean;
}
```
#### Key Features
1. **Static Node Positioning**
- Nodes are arranged in a circular layout
- Fixed positions prevent unwanted movement
- Calculated using mathematical formulas for even distribution
2. **Interaction Handling**
- Single-click node selection
- Debounced click handling (300ms)
- Click processing state management
- Cleanup of timeouts on unmount
3. **Visual Configuration**
- Node colors based on character type:
- Protagonist: Green (#4CAF50)
- Antagonist: Red (#f44336)
- Supporting: Blue (#2196F3)
- Node size: 8 units (nodeRelSize)
- Link width: 2 units
- Link opacity: 0.6
4. **Performance Optimizations**
- Disabled force simulation cooldown
- Zero warmup ticks
- Removed hover effects and tooltips
- Static graph configuration
5. **UI Components**
- Main graph container (600x600px)
- Right-side information panel (300px width)
- Smooth transitions for panel visibility
- Responsive layout with flex container
### Implementation Details
```javascript
// Node Click Handler
const handleNodeClick = useCallback(
(node) => {
if (!node || isProcessingClick) return;
setIsProcessingClick(true);
setSelectedNode(node);
onNodeSelect(node);
// Reset after 300ms
clickTimeoutRef.current = setTimeout(() => {
setIsProcessingClick(false);
}, 300);
},
[onNodeSelect, isProcessingClick]
);
// Graph Configuration
const graphConfig = {
staticGraph: true,
enableNodeDrag: false,
enableZoom: true,
minZoom: 0.5,
maxZoom: 2.5,
cooldownTime: 0,
warmupTicks: 0,
nodeLabel: null,
enableNodeHover: false,
};
```
### Styling
The component uses a flex layout with the following structure:
```css
// Container
{
display: flex;
gap: 20px;
width: 100%;
maxwidth: 1000px;
margin: 0 auto;
}
// Graph Container
{
width: 600px;
height: 600px;
border: 1px solid #eee;
position: relative;
}
// Information Panel
{
width: 300px;
padding: 20px;
backgroundcolor: #fff;
border: 1px solid #eee;
borderradius: 4px;
transition: opacity 0.2s ease-in-out;
}
```
### Best Practices
1. **State Management**
- Use of React hooks for state
- Proper cleanup of timeouts
- Controlled component updates
2. **Performance**
- Debounced click handling
- Static node positioning
- Minimal re-renders
3. **User Experience**
- Smooth transitions
- Clear visual feedback
- Responsive layout
4. **Code Organization**
- Modular component structure
- Clear configuration objects
- Type-safe interfaces

View file

@ -12,7 +12,6 @@ import NetworkVisualization from './pages/NetworkVisualization';
// Lazy load other pages
const Quiz = React.lazy(() => import('./pages/Quiz'));
const Advice = React.lazy(() => import('./pages/Advice'));
const Vendors = React.lazy(() => import('./pages/Vendors'));
const MarketCalculator = React.lazy(() => import('./pages/MarketCalculator'));
@ -27,7 +26,6 @@ function App() {
<Route path="/blogs" element={<Blogs />} />
<Route path="/blogs/:id" element={<BlogPost />} />
<Route path="/quiz" element={<Quiz />} />
<Route path="/advice" element={<Advice />} />
<Route path="/vendors" element={<Vendors />} />
<Route path="/success-stories" element={<SuccessStories />} />
<Route path="/market-calculator" element={<MarketCalculator />} />

View file

@ -11,26 +11,41 @@ const Navbar = () => {
<span className="font-cormorant text-xl text-sage-900">Austen's Wedding Guide</span>
</Link>
<nav className="flex items-center space-x-6">
<Link to="/blogs" className="text-sage-700 hover:text-sage-900 transition-colors">
Character Blogs
</Link>
<Link to="/quiz" className="text-sage-700 hover:text-sage-900 transition-colors">
Bride Quiz
</Link>
<Link to="/dear-jane" className="text-sage-700 hover:text-sage-900 transition-colors">
Dear Jane
</Link>
<Link to="/vendors" className="text-sage-700 hover:text-sage-900 transition-colors">
Vendors
</Link>
<Link to="/success-stories" className="text-sage-700 hover:text-sage-900 transition-colors">
Success Stories
</Link>
<Link to="/market-calculator" className="text-sage-700 hover:text-sage-900 transition-colors">
Market Value
</Link>
<div className="flex items-center space-x-2">
<nav className="flex items-center">
<div className="flex items-center space-x-2 mr-6">
<Link
to="/blogs"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
>
Character Blogs
</Link>
<Link
to="/quiz"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
>
Bride Quiz
</Link>
<Link
to="/vendors"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
>
Vendors
</Link>
<Link
to="/success-stories"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
>
Success Stories
</Link>
<Link
to="/market-calculator"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"
>
Market Value
</Link>
</div>
<div className="flex items-center space-x-2 border-l border-sage-200 pl-6">
<Link
to="/analysis"
className="bg-sage-100 text-sage-700 hover:bg-sage-200 px-4 py-2 rounded-md transition-colors"

View file

@ -1,156 +0,0 @@
export interface DearJaneLetter {
id: string;
from: string;
subject: string;
category: 'courtship' | 'marriage' | 'family' | 'society' | 'heartbreak';
question: string;
answer: string[];
date: string;
relatedBook?: {
title: string;
character: string;
quote: string;
};
}
export const dearJaneLetters: DearJaneLetter[] = [
{
id: '1',
from: 'Hopelessly Romantic',
subject: 'Should I Wait for True Love?',
category: 'courtship',
date: '1813-01-15',
question: 'Dear Jane, I am twenty-seven and have received a proposal from a respectable gentleman. He is kind and well-situated, but I feel no passionate attachment. Should I accept for security\'s sake, or wait for true love?',
answer: [
'My dear Hopelessly Romantic,',
'While the heart must not be entirely silent in matters of marriage, neither should it be the only voice in the conversation. Consider that happiness in marriage is not merely a matter of passionate beginnings, but of compatible temperaments and mutual respect.',
'However, do not mistake mere security for contentment. A marriage without any affection is as imprudent as one based solely on passionate feelings. The ideal lies somewhere between Charlotte Lucas\'s pragmatism and Marianne Dashwood\'s romanticism.',
'Examine your feelings carefully. Is your lack of passion truly indifference, or merely the absence of drama that so many mistake for love? Sometimes the steadiest attachments grow from the most modest beginnings.'
],
relatedBook: {
title: 'Sense and Sensibility',
character: 'Marianne Dashwood',
quote: 'The more I know of the world, the more I am convinced that I shall never see a man whom I can really love.'
}
},
{
id: '2',
from: 'Concerned Sister',
subject: 'My Sister\'s Imprudent Attachment',
category: 'family',
date: '1813-02-01',
question: 'Dear Jane, My younger sister has formed an attachment to a gentleman of questionable character. How can I guide her toward prudence without seeming to interfere?',
answer: [
'My dear Concerned Sister,',
'Ah, the delicate art of sisterly guidance! One must tread carefully when matters of the heart are concerned, particularly when dealing with a younger sister who may mistake experience for interference.',
'Remember how our dear Elizabeth Bennet handled her sister Lydia\'s situation. Direct opposition often strengthens such attachments. Instead, guide your sister to examine the gentleman\'s character through his actions rather than his words.',
'Perhaps arrange situations where his true nature might reveal itself naturally. The best advice is often that which allows the recipient to believe they have arrived at the conclusion independently.'
],
relatedBook: {
title: 'Pride and Prejudice',
character: 'Elizabeth Bennet',
quote: 'We all know him to be a proud, unpleasant sort of man; but this would be nothing if you really liked him.'
}
},
{
id: '3',
from: 'Socially Anxious',
subject: 'Navigating Social Gatherings',
category: 'society',
date: '1813-03-15',
question: 'Dear Jane, I find myself overwhelmed at social gatherings, particularly when expected to dance and converse with potential suitors. How can I overcome my natural reserve without compromising my dignity?',
answer: [
'My dear Socially Anxious,',
'Take heart in knowing that even the most accomplished among us have felt the weight of social expectations. Consider our dear Anne Elliot, whose quiet dignity and genuine nature eventually won the day over more boisterous displays.',
'Rather than attempting to transform yourself into a social butterfly, focus on meaningful connections. A well-timed observation or thoughtful question often carries more weight than hours of idle chatter.',
'Remember, those worth knowing will appreciate your authentic self. As for dancing, consider it an opportunity for observation rather than a test of social prowess.'
],
relatedBook: {
title: 'Persuasion',
character: 'Anne Elliot',
quote: 'Time will explain.'
}
},
{
id: '4',
from: 'Disappointed in Bath',
subject: 'Recovering from Heartbreak',
category: 'heartbreak',
date: '1813-04-01',
question: 'Dear Jane, I recently discovered that the gentleman I had set my hopes upon is engaged to another. How does one recover from such a disappointment while maintaining one\'s composure in society?',
answer: [
'My dear Disappointed,',
'First, allow me to commend your strength in seeking guidance rather than retreating into despair. The pain of disappointed hopes is keen, but it need not define your future happiness.',
'Consider how our dear Jane Bennet conducted herself when faced with similar circumstances. Her gentle nature and genuine goodwill, even toward those who had caused her pain, preserved both her dignity and her peace of mind.',
'Channel your energies into self-improvement and the cultivation of true friendships. Time, that great healer, works most effectively when we give ourselves permission to grow from our disappointments.'
],
relatedBook: {
title: 'Pride and Prejudice',
character: 'Jane Bennet',
quote: 'I would not wish to be hasty in censuring anyone; but I always speak what I think.'
}
},
{
id: '5',
from: 'Newly Married',
subject: 'Adjusting to Married Life',
category: 'marriage',
date: '1813-05-15',
question: 'Dear Jane, Having recently entered the married state, I find myself struggling to balance my own pursuits with my husband\'s expectations. How can I maintain my independence while building a harmonious partnership?',
answer: [
'My dear Newly Married,',
'The transition from maiden to wife need not mean the abandonment of one\'s individual character. Indeed, the finest marriages are those where both parties encourage each other\'s growth and interests.',
'Consider how Emma Woodhouse and Mr. Knightley maintained their lively debates and individual pursuits even after marriage. Their relationship was strengthened by their ability to challenge and support one another.',
'Establish habits of open communication early in your marriage. A wise partner will value your happiness and independence as much as your devotion.'
],
relatedBook: {
title: 'Emma',
character: 'Emma Woodhouse',
quote: 'I may have lost my heart, but not my self-control.'
}
}
];
export interface UserSubmittedQuestion {
id: string;
from: string;
subject: string;
category: DearJaneLetter['category'];
question: string;
date: string;
status: 'pending' | 'answered';
}
export const generateQuestionId = () => {
return `q-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
};
export const saveUserQuestion = (question: Omit<UserSubmittedQuestion, 'id' | 'date' | 'status'>): UserSubmittedQuestion => {
const newQuestion: UserSubmittedQuestion = {
...question,
id: generateQuestionId(),
date: new Date().toISOString(),
status: 'pending'
};
// Get existing questions from localStorage
const existingQuestions = getUserQuestions();
// Add new question to the list
const updatedQuestions = [newQuestion, ...existingQuestions];
// Save back to localStorage
localStorage.setItem('userQuestions', JSON.stringify(updatedQuestions));
return newQuestion;
};
export const getUserQuestions = (): UserSubmittedQuestion[] => {
const stored = localStorage.getItem('userQuestions');
if (!stored) return [];
try {
return JSON.parse(stored);
} catch {
return [];
}
};

View file

@ -1,290 +0,0 @@
import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Mail, Heart, Book, Users, Home, MessageCircle, PenTool, Clock } from 'lucide-react';
import { dearJaneLetters, DearJaneLetter, UserSubmittedQuestion, saveUserQuestion, getUserQuestions } from '@/data/dear-jane';
import { useToast } from '@/components/ui/use-toast';
const categoryIcons = {
courtship: <Heart className="h-4 w-4" />,
marriage: <Users className="h-4 w-4" />,
family: <Home className="h-4 w-4" />,
society: <MessageCircle className="h-4 w-4" />,
heartbreak: <Book className="h-4 w-4" />
};
const categoryNames = {
courtship: 'Matters of Courtship',
marriage: 'Marriage & Partnership',
family: 'Family Relations',
society: 'Social Etiquette',
heartbreak: 'Healing Hearts'
};
const categoryDescriptions = {
courtship: 'Navigate the delicate dance of courtship with Jane\'s guidance on matters of the heart.',
marriage: 'Discover wisdom for maintaining harmony and growth in matrimonial life.',
family: 'Learn to handle family matters with grace, wisdom, and understanding.',
society: 'Master the art of social etiquette while staying true to yourself.',
heartbreak: 'Find comfort and strength in Jane\'s advice for healing a wounded heart.'
};
const DearJane = () => {
const { toast } = useToast();
const [selectedLetter, setSelectedLetter] = useState<DearJaneLetter>(dearJaneLetters[0]);
const [selectedCategory, setSelectedCategory] = useState<DearJaneLetter['category']>('courtship');
const [showSubmitForm, setShowSubmitForm] = useState(false);
const [userQuestions, setUserQuestions] = useState<UserSubmittedQuestion[]>([]);
const [formData, setFormData] = useState({
subject: '',
from: '',
question: '',
category: 'courtship' as DearJaneLetter['category']
});
useEffect(() => {
setUserQuestions(getUserQuestions());
}, []);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!formData.subject || !formData.from || !formData.question) {
toast({
title: "Missing Information",
description: "Please fill in all fields before submitting.",
variant: "destructive"
});
return;
}
try {
const newQuestion = saveUserQuestion(formData);
setUserQuestions(prev => [newQuestion, ...prev]);
setShowSubmitForm(false);
setFormData({
subject: '',
from: '',
question: '',
category: 'courtship'
});
toast({
title: "Question Submitted",
description: "Your question has been submitted successfully. Jane will respond soon!"
});
} catch (err) {
console.error('Error submitting question:', err);
toast({
title: "Submission Error",
description: err instanceof Error ? err.message : "There was an error submitting your question. Please try again.",
variant: "destructive"
});
}
};
const allLetters = [...dearJaneLetters, ...userQuestions];
const filteredLetters = allLetters.filter(letter => letter.category === selectedCategory);
return (
<div className="space-y-8 max-w-6xl mx-auto">
<header className="text-center space-y-4">
<h1 className="font-cormorant text-4xl text-sage-900">Dear Jane</h1>
<p className="text-sage-700 max-w-2xl mx-auto">
Seeking counsel in matters of the heart? Let Jane Austen's timeless wisdom guide you through your romantic predicaments.
</p>
<Button
onClick={() => setShowSubmitForm(!showSubmitForm)}
className="bg-sage-500 hover:bg-sage-600 text-white"
>
<PenTool className="h-4 w-4 mr-2" />
{showSubmitForm ? 'Close Form' : 'Ask Jane for Advice'}
</Button>
</header>
{showSubmitForm && (
<Card className="max-w-2xl mx-auto bg-cream-50">
<CardHeader>
<CardTitle className="font-cormorant text-2xl text-sage-900">Submit Your Question</CardTitle>
<p className="text-sage-700 text-sm">
Pour your heart out to Jane, and she shall guide you with her timeless wisdom.
</p>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-sage-700 mb-1">
Subject
</label>
<input
type="text"
value={formData.subject}
onChange={(e) => setFormData(prev => ({ ...prev, subject: e.target.value }))}
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500 bg-white"
placeholder="e.g., Advice on a Delicate Matter"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-sage-700 mb-1">
Your Question
</label>
<textarea
value={formData.question}
onChange={(e) => setFormData(prev => ({ ...prev, question: e.target.value }))}
className="w-full h-32 p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500 bg-white"
placeholder="Dear Jane..."
required
/>
</div>
<div>
<label className="block text-sm font-medium text-sage-700 mb-1">
Sign As
</label>
<input
type="text"
value={formData.from}
onChange={(e) => setFormData(prev => ({ ...prev, from: e.target.value }))}
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500 bg-white"
placeholder="e.g., Hopelessly Romantic"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-sage-700 mb-1">
Category
</label>
<select
value={formData.category}
onChange={(e) => setFormData(prev => ({ ...prev, category: e.target.value as DearJaneLetter['category'] }))}
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500 bg-white"
>
{Object.entries(categoryNames).map(([key, name]) => (
<option key={key} value={key}>{name}</option>
))}
</select>
</div>
<Button type="submit" className="w-full bg-sage-500 hover:bg-sage-600 text-white">
<Mail className="h-4 w-4 mr-2" />
Send Letter to Jane
</Button>
</form>
</CardContent>
</Card>
)}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<Card className="lg:col-span-1 bg-cream-50">
<CardHeader>
<CardTitle className="font-cormorant text-2xl text-sage-900">Browse by Category</CardTitle>
<p className="text-sage-700 text-sm">
{categoryDescriptions[selectedCategory]}
</p>
</CardHeader>
<CardContent>
<Tabs value={selectedCategory} onValueChange={(value) => setSelectedCategory(value as DearJaneLetter['category'])}>
<TabsList className="grid grid-cols-1 gap-2">
{Object.entries(categoryNames).map(([key, name]) => (
<TabsTrigger
key={key}
value={key}
className="w-full flex items-center justify-start gap-2 p-2"
>
{categoryIcons[key as keyof typeof categoryIcons]}
<div className="text-left">
<div className="font-medium">{name}</div>
<div className="text-xs text-sage-600">
{filteredLetters.length} letters
</div>
</div>
</TabsTrigger>
))}
</TabsList>
</Tabs>
<ScrollArea className="h-[400px] mt-4">
<div className="space-y-2">
{filteredLetters.map((letter) => (
<Button
key={letter.id}
variant={selectedLetter.id === letter.id ? 'secondary' : 'ghost'}
className="w-full justify-start text-left"
onClick={() => setSelectedLetter(letter as DearJaneLetter)}
>
<Mail className="mr-2 h-4 w-4 flex-shrink-0" />
<div className="truncate flex-1">
<div className="font-medium">{letter.subject}</div>
<div className="text-xs text-sage-600 flex items-center gap-1">
<span>From: {letter.from}</span>
{'status' in letter && (
<>
<span></span>
<Clock className="h-3 w-3" />
<span>{letter.status === 'pending' ? 'Awaiting Response' : 'Answered'}</span>
</>
)}
</div>
</div>
</Button>
))}
</div>
</ScrollArea>
</CardContent>
</Card>
<Card className="lg:col-span-2 bg-cream-50">
<CardHeader>
<div className="space-y-2">
<CardTitle className="font-cormorant text-2xl text-sage-900">
{selectedLetter.subject}
</CardTitle>
<div className="flex items-center gap-2 text-sm text-sage-600">
<span>From: {selectedLetter.from}</span>
<span></span>
<span>{new Date(selectedLetter.date).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
})}</span>
</div>
</div>
</CardHeader>
<CardContent>
<div className="prose prose-sage max-w-none">
<div className="mb-8">
<p className="italic text-sage-700">{selectedLetter.question}</p>
</div>
<div className="space-y-4">
{selectedLetter.answer.map((paragraph, index) => (
<p key={index} className="text-sage-800">
{paragraph}
</p>
))}
<p className="text-right font-cormorant text-sage-900">
Yours truly,<br />Jane Austen
</p>
</div>
{selectedLetter.relatedBook && (
<div className="mt-8 p-4 bg-sage-50 rounded-lg">
<h3 className="font-cormorant text-xl text-sage-900 mb-2">
From {selectedLetter.relatedBook.title}
</h3>
<blockquote className="italic text-sage-700 border-l-4 border-sage-200 pl-4">
"{selectedLetter.relatedBook.quote}"
<footer className="text-sage-600 mt-2">
{selectedLetter.relatedBook.character}
</footer>
</blockquote>
</div>
)}
</div>
</CardContent>
</Card>
</div>
</div>
);
};
export default DearJane;

View file

@ -55,16 +55,6 @@ const Home = () => {
</div>
</Link>
{/* Dear Jane */}
<Link to="/advice" className="feature-card">
<div className="bg-cream-100 p-6 rounded-lg hover:shadow-lg transition">
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Dear Jane</h2>
<p className="text-sage-700">
Seeking counsel? Let Jane's timeless wisdom guide you through modern romance
</p>
</div>
</Link>
{/* Vendor Directory */}
<Link to="/vendors" className="feature-card">
<div className="bg-sage-100 p-6 rounded-lg hover:shadow-lg transition">