mirror of
https://github.com/harivansh-afk/Austens-Wedding-Guide.git
synced 2026-04-15 05:02:07 +00:00
new UI
This commit is contained in:
parent
147ab53153
commit
8fd1ca8260
17 changed files with 1111 additions and 412 deletions
|
|
@ -4,9 +4,10 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>⚡️ Bolt.new + Vite + React</title>
|
||||
<title>Austen's Wedding Guide</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<body class="bg-cream-50">
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
|
|
|||
25
package-lock.json
generated
25
package-lock.json
generated
|
|
@ -37,7 +37,8 @@
|
|||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
|
|
@ -2640,10 +2641,23 @@
|
|||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@tailwindcss/forms": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz",
|
||||
"integrity": "sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz",
|
||||
"integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
|
|
@ -4714,6 +4728,15 @@
|
|||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mini-svg-data-uri": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mini-svg-data-uri": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.0.0",
|
||||
|
|
@ -76,4 +77,4 @@
|
|||
"typescript-eslint": "^8.7.0",
|
||||
"vite": "^5.4.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
42
src/App.tsx
42
src/App.tsx
|
|
@ -1,20 +1,34 @@
|
|||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { ThemeProvider } from '@/components/theme-provider';
|
||||
import { Toaster } from '@/components/ui/sonner';
|
||||
import Layout from '@/components/Layout';
|
||||
import Routes from '@/components/Routes';
|
||||
import React, { Suspense } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||
import MainLayout from './components/layout/MainLayout';
|
||||
import Home from './pages/Home';
|
||||
|
||||
// Lazy load other pages
|
||||
const Blogs = React.lazy(() => import('./pages/Blogs'));
|
||||
const Quiz = React.lazy(() => import('./pages/Quiz'));
|
||||
const Advice = React.lazy(() => import('./pages/Advice'));
|
||||
const Vendors = React.lazy(() => import('./pages/Vendors'));
|
||||
const Stories = React.lazy(() => import('./pages/Stories'));
|
||||
const MarketCalculator = React.lazy(() => import('./pages/MarketCalculator'));
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
||||
<Router>
|
||||
<Layout>
|
||||
<Routes />
|
||||
</Layout>
|
||||
</Router>
|
||||
<Toaster />
|
||||
</ThemeProvider>
|
||||
<Router>
|
||||
<MainLayout>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/blogs/*" element={<Blogs />} />
|
||||
<Route path="/quiz" element={<Quiz />} />
|
||||
<Route path="/advice" element={<Advice />} />
|
||||
<Route path="/vendors" element={<Vendors />} />
|
||||
<Route path="/stories" element={<Stories />} />
|
||||
<Route path="/calculator" element={<MarketCalculator />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</MainLayout>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
|
|
|||
42
src/components/layout/MainLayout.tsx
Normal file
42
src/components/layout/MainLayout.tsx
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
const MainLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className="min-h-screen bg-cream-50">
|
||||
<nav className="bg-sage-100 border-b border-sage-200">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex justify-between h-16">
|
||||
<div className="flex">
|
||||
<Link to="/" className="flex items-center font-cormorant text-2xl text-sage-900">
|
||||
Austen's Wedding Guide
|
||||
</Link>
|
||||
</div>
|
||||
<div className="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||
<Link to="/blogs" className="nav-link">Character Blogs</Link>
|
||||
<Link to="/quiz" className="nav-link">Bride Quiz</Link>
|
||||
<Link to="/advice" className="nav-link">Dear Jane</Link>
|
||||
<Link to="/vendors" className="nav-link">Vendors</Link>
|
||||
<Link to="/stories" className="nav-link">Success Stories</Link>
|
||||
<Link to="/calculator" className="nav-link">Market Value</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
<footer className="bg-sage-100 border-t border-sage-200 mt-12">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div className="text-center font-cormorant text-sage-900">
|
||||
<p className="text-lg">"It is a truth universally acknowledged..."</p>
|
||||
<p className="mt-2">© {new Date().getFullYear()} Austen's Wedding Guide</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainLayout;
|
||||
130
src/index.css
130
src/index.css
|
|
@ -1,128 +1,26 @@
|
|||
@import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap");
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #1a1a1a;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5.9% 10%;
|
||||
--radius: 0.75rem;
|
||||
html {
|
||||
@apply font-lato text-sage-900 bg-cream-50;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-cormorant;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground min-h-screen;
|
||||
@layer components {
|
||||
.nav-link {
|
||||
@apply inline-flex items-center px-1 pt-1 text-sm font-medium border-b-2 border-transparent text-sage-700 hover:text-sage-900 hover:border-sage-500 transition-colors duration-200;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
16
src/main.tsx
16
src/main.tsx
|
|
@ -1,10 +1,10 @@
|
|||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import './index.css';
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
|
|
|||
127
src/pages/Advice.tsx
Normal file
127
src/pages/Advice.tsx
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
import { useState } from 'react';
|
||||
import type { AdviceQuestion } from '../types';
|
||||
|
||||
const SAMPLE_QUESTIONS: AdviceQuestion[] = [
|
||||
{
|
||||
id: '1',
|
||||
question: "Dear Jane, I find myself in a peculiar situation. A gentleman of considerable fortune has proposed, but while he can offer security and comfort, his conversation leaves much to be desired. My family urges me to accept, but my heart hesitates. What would you advise?",
|
||||
askedBy: "Prudently Pondering",
|
||||
response: "My dear Prudently Pondering, While security in marriage is not to be scorned, neither should it be the sole consideration. A marriage lacking in intellectual companionship may secure your future, but at what cost to your present happiness? As I once wrote through Elizabeth Bennet, 'I am determined that nothing but the very deepest love will induce me into matrimony.' Consider whether you could respect this gentleman enough to spend your days in his company, for comfort without compatibility is but a gilded cage.",
|
||||
date: "2023-11-24"
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
question: "Dear Jane, I've fallen deeply in love with someone my family considers beneath our social station. They insist I look higher, but my heart says otherwise. Should I follow my feelings or bow to familial duty?",
|
||||
askedBy: "Torn Between Two Worlds",
|
||||
response: "Dearest Torn, The challenge you face echoes through many a drawing room. While duty to family holds weight, remember that it is you who must live with the consequences of this choice. As Emma Woodhouse learned, true worth lies not in social position but in character. Examine both your heart's inclination and your suitor's merit - if you find them equally sound, perhaps it is your family's understanding that needs elevation, not your choice of partner.",
|
||||
date: "2023-11-23"
|
||||
}
|
||||
];
|
||||
|
||||
const Advice = () => {
|
||||
const [showSubmitForm, setShowSubmitForm] = useState(false);
|
||||
const [newQuestion, setNewQuestion] = useState({
|
||||
question: '',
|
||||
askedBy: ''
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
// In a real app, this would send the question to a backend
|
||||
setShowSubmitForm(false);
|
||||
setNewQuestion({ question: '', askedBy: '' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto space-y-12">
|
||||
{/* Header */}
|
||||
<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>
|
||||
</header>
|
||||
|
||||
{/* Submit Question Button */}
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={() => setShowSubmitForm(!showSubmitForm)}
|
||||
className="bg-sage-500 text-white px-6 py-3 rounded-lg hover:bg-sage-600 transition"
|
||||
>
|
||||
{showSubmitForm ? 'Cancel' : 'Submit Your Question'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Question Form */}
|
||||
{showSubmitForm && (
|
||||
<form onSubmit={handleSubmit} className="bg-cream-50 p-8 rounded-lg space-y-6">
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="question" className="block text-sage-900 font-semibold">
|
||||
Your Question
|
||||
</label>
|
||||
<textarea
|
||||
id="question"
|
||||
value={newQuestion.question}
|
||||
onChange={(e) => setNewQuestion(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"
|
||||
placeholder="Dear Jane..."
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="askedBy" className="block text-sage-900 font-semibold">
|
||||
Sign As
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="askedBy"
|
||||
value={newQuestion.askedBy}
|
||||
onChange={(e) => setNewQuestion(prev => ({ ...prev, askedBy: e.target.value }))}
|
||||
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500"
|
||||
placeholder="e.g., Hopelessly Romantic"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-sage-500 text-white px-6 py-3 rounded-lg hover:bg-sage-600 transition"
|
||||
>
|
||||
Submit Question
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
{/* Questions List */}
|
||||
<div className="space-y-8">
|
||||
{SAMPLE_QUESTIONS.map((q) => (
|
||||
<article key={q.id} className="bg-cream-50 p-8 rounded-lg space-y-6">
|
||||
<div className="space-y-4">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900">
|
||||
{q.question}
|
||||
</h2>
|
||||
<p className="text-sage-700 italic">
|
||||
- {q.askedBy}
|
||||
</p>
|
||||
<p className="text-sage-500 text-sm">
|
||||
{new Date(q.date).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{q.response && (
|
||||
<div className="border-t border-sage-200 pt-6">
|
||||
<p className="text-sage-900 font-cormorant text-lg">
|
||||
{q.response}
|
||||
</p>
|
||||
<p className="text-sage-700 italic mt-4">
|
||||
- Jane Austen
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Advice;
|
||||
|
|
@ -1,41 +1,121 @@
|
|||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader } from '@/components/ui/card';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import BlogPost from '@/components/BlogPost';
|
||||
import { blogPosts } from '@/data/blog-posts';
|
||||
import { Routes, Route, Link, useLocation } from 'react-router-dom';
|
||||
import type { BlogPost } from '../types';
|
||||
|
||||
const Blogs = () => {
|
||||
const [currentAuthor, setCurrentAuthor] = useState('charlotte');
|
||||
const SAMPLE_POSTS: BlogPost[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'The Art of Practical Partnership',
|
||||
author: 'Charlotte Lucas',
|
||||
content: 'In my experience, happiness in marriage is entirely a matter of chance...',
|
||||
date: '2023-11-25',
|
||||
category: 'charlotte'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Finding True Romance in a Modern World',
|
||||
author: 'Marianne Dashwood',
|
||||
content: 'To love is to burn, to be on fire with passion that consumes the soul...',
|
||||
date: '2023-11-24',
|
||||
category: 'marianne'
|
||||
}
|
||||
];
|
||||
|
||||
const BlogList = () => {
|
||||
const [filter, setFilter] = useState<'all' | 'charlotte' | 'marianne'>('all');
|
||||
|
||||
const filteredPosts = SAMPLE_POSTS.filter(post =>
|
||||
filter === 'all' ? true : post.category === filter
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-serif text-4xl">Character Blogs</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
Matrimonial musings from your favorite Austen characters
|
||||
</p>
|
||||
</header>
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">Character Blogs</h1>
|
||||
<div className="space-x-4">
|
||||
<button
|
||||
onClick={() => setFilter('all')}
|
||||
className={`px-4 py-2 rounded ${
|
||||
filter === 'all' ? 'bg-sage-500 text-white' : 'bg-sage-100'
|
||||
}`}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('charlotte')}
|
||||
className={`px-4 py-2 rounded ${
|
||||
filter === 'charlotte' ? 'bg-sage-500 text-white' : 'bg-sage-100'
|
||||
}`}
|
||||
>
|
||||
Charlotte
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('marianne')}
|
||||
className={`px-4 py-2 rounded ${
|
||||
filter === 'marianne' ? 'bg-sage-500 text-white' : 'bg-sage-100'
|
||||
}`}
|
||||
>
|
||||
Marianne
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="max-w-4xl mx-auto">
|
||||
<CardHeader>
|
||||
<Tabs value={currentAuthor} onValueChange={setCurrentAuthor}>
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="charlotte">Charlotte Lucas</TabsTrigger>
|
||||
<TabsTrigger value="marianne">Marianne Dashwood</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
{blogPosts[currentAuthor].map((post) => (
|
||||
<BlogPost key={post.id} post={post} />
|
||||
))}
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="grid gap-6">
|
||||
{filteredPosts.map(post => (
|
||||
<Link
|
||||
key={post.id}
|
||||
to={`/blogs/${post.id}`}
|
||||
className="block bg-cream-50 p-6 rounded-lg hover:shadow-lg transition"
|
||||
>
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-2">{post.title}</h2>
|
||||
<p className="text-sage-700 mb-4">{post.content.substring(0, 150)}...</p>
|
||||
<div className="flex justify-between text-sage-500">
|
||||
<span>{post.author}</span>
|
||||
<span>{new Date(post.date).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Blogs;
|
||||
const BlogPost = () => {
|
||||
const { pathname } = useLocation();
|
||||
const postId = pathname.split('/').pop();
|
||||
const post = SAMPLE_POSTS.find(p => p.id === postId);
|
||||
|
||||
if (!post) return <div>Post not found</div>;
|
||||
|
||||
return (
|
||||
<article className="max-w-3xl mx-auto space-y-6">
|
||||
<Link to="/blogs" className="text-sage-500 hover:text-sage-600">
|
||||
← Back to Blogs
|
||||
</Link>
|
||||
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">{post.title}</h1>
|
||||
<div className="text-sage-700">
|
||||
<span>{post.author}</span>
|
||||
<span className="mx-2">•</span>
|
||||
<span>{new Date(post.date).toLocaleDateString()}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="prose prose-sage max-w-none">
|
||||
<p>{post.content}</p>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
};
|
||||
|
||||
const Blogs = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/" element={<BlogList />} />
|
||||
<Route path="/:id" element={<BlogPost />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
export default Blogs;
|
||||
|
|
|
|||
|
|
@ -1,85 +1,91 @@
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { BookOpen, Heart, Mail, Users } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const Home = () => {
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
<section className="text-center space-y-4">
|
||||
<h1 className="font-serif text-4xl md:text-6xl">
|
||||
{/* Hero Section */}
|
||||
<section className="text-center py-16 bg-cream-100 rounded-lg">
|
||||
<h1 className="font-cormorant text-5xl text-sage-900 mb-4">
|
||||
Welcome to Austen's Wedding Guide
|
||||
</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
A satirical blend of Regency-era matrimonial wisdom and modern wedding culture
|
||||
<p className="font-lato text-lg text-sage-700 max-w-2xl mx-auto">
|
||||
Your modern guide to matrimonial bliss, as Jane Austen would have imagined it
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<BookOpen className="h-8 w-8" />
|
||||
<h3 className="font-serif text-xl">Character Blogs</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Wisdom and wit from your favorite Austen characters
|
||||
{/* Featured Sections */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{/* Character Blogs */}
|
||||
<Link to="/blogs" className="feature-card">
|
||||
<div className="bg-sage-50 p-6 rounded-lg hover:shadow-lg transition">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Character Blogs</h2>
|
||||
<p className="text-sage-700">
|
||||
Wisdom and wit from Charlotte Lucas's practical advice to Marianne Dashwood's romantic musings
|
||||
</p>
|
||||
<Link to="/blogs">
|
||||
<Button className="w-full">Read Blogs</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<Mail className="h-8 w-8" />
|
||||
<h3 className="font-serif text-xl">Dear Jane</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Matrimonial advice with Austen's signature wit
|
||||
{/* Bride Quiz */}
|
||||
<Link to="/quiz" className="feature-card">
|
||||
<div className="bg-rose-50 p-6 rounded-lg hover:shadow-lg transition">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Which Austen Bride Are You?</h2>
|
||||
<p className="text-sage-700">
|
||||
Discover your literary matrimonial counterpart through our delightful quiz
|
||||
</p>
|
||||
<Link to="/dear-jane">
|
||||
<Button className="w-full">Seek Advice</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<Users className="h-8 w-8" />
|
||||
<h3 className="font-serif text-xl">Quiz</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Discover which Austen bride matches your personality
|
||||
{/* 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>
|
||||
<Link to="/quiz">
|
||||
<Button className="w-full">Take Quiz</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6 space-y-4">
|
||||
<Heart className="h-8 w-8" />
|
||||
<h3 className="font-serif text-xl">Success Stories</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Tales of matrimonial triumph from the pages of Austen
|
||||
{/* Vendor Directory */}
|
||||
<Link to="/vendors" className="feature-card">
|
||||
<div className="bg-sage-100 p-6 rounded-lg hover:shadow-lg transition">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Vendor Directory</h2>
|
||||
<p className="text-sage-700">
|
||||
From Pemberley Estate venues to Mrs. Bennet's matchmaking services
|
||||
</p>
|
||||
<Link to="/success-stories">
|
||||
<Button className="w-full">Read Stories</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<section className="bg-muted p-8 rounded-lg">
|
||||
<div className="max-w-3xl mx-auto text-center space-y-4">
|
||||
<h2 className="font-serif text-3xl">Featured Quote</h2>
|
||||
<blockquote className="text-xl italic">
|
||||
"It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife."
|
||||
</blockquote>
|
||||
<p className="text-muted-foreground">- Pride and Prejudice</p>
|
||||
{/* Success Stories */}
|
||||
<Link to="/stories" className="feature-card">
|
||||
<div className="bg-rose-100 p-6 rounded-lg hover:shadow-lg transition">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Success Stories</h2>
|
||||
<p className="text-sage-700">
|
||||
Real tales of romance from our beloved characters
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Featured Article */}
|
||||
<div className="bg-cream-50 p-6 rounded-lg">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-3">Latest from Charlotte</h2>
|
||||
<p className="text-sage-700 mb-4">
|
||||
"The Art of Practical Partnership: A Guide to Sensible Matches"
|
||||
</p>
|
||||
<Link to="/blogs/charlotte" className="text-sage-500 hover:text-sage-600">
|
||||
Read More →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quote Section */}
|
||||
<section className="text-center py-12 bg-sage-50 rounded-lg">
|
||||
<blockquote className="font-cormorant text-2xl text-sage-900 italic">
|
||||
"It is a truth universally acknowledged, that a single person in possession of a good fortune, must be in want of a spouse."
|
||||
</blockquote>
|
||||
<p className="mt-4 text-sage-700">- Adapted from Pride and Prejudice</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Home;
|
||||
export default Home;
|
||||
|
|
|
|||
222
src/pages/MarketCalculator.tsx
Normal file
222
src/pages/MarketCalculator.tsx
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
interface MarketValue {
|
||||
income: number;
|
||||
estate: string;
|
||||
connections: string;
|
||||
accomplishments: string[];
|
||||
personality: string[];
|
||||
}
|
||||
|
||||
const ACCOMPLISHMENTS = [
|
||||
'Playing the pianoforte',
|
||||
'Drawing landscapes',
|
||||
'Speaking French',
|
||||
'Embroidery',
|
||||
'Reading extensively',
|
||||
'Dancing gracefully',
|
||||
'Writing poetry',
|
||||
'Maintaining witty correspondence'
|
||||
];
|
||||
|
||||
const PERSONALITY_TRAITS = [
|
||||
'Wit and vivacity',
|
||||
'Serene temperament',
|
||||
'Excellent manners',
|
||||
'Proper reserve',
|
||||
'Charming conversation',
|
||||
'Filial devotion',
|
||||
'Prudent judgment',
|
||||
'Musical taste'
|
||||
];
|
||||
|
||||
const MarketCalculator = () => {
|
||||
const [values, setValues] = useState<MarketValue>({
|
||||
income: 0,
|
||||
estate: '',
|
||||
connections: '',
|
||||
accomplishments: [],
|
||||
personality: []
|
||||
});
|
||||
|
||||
const [result, setResult] = useState<string>('');
|
||||
|
||||
const calculateMarketValue = () => {
|
||||
let score = 0;
|
||||
|
||||
// Income calculations (in pounds per annum)
|
||||
score += Math.floor(values.income / 1000) * 5;
|
||||
|
||||
// Estate value
|
||||
if (values.estate.toLowerCase().includes('pemberley')) score += 50;
|
||||
if (values.estate.toLowerCase().includes('estate')) score += 20;
|
||||
if (values.estate.toLowerCase().includes('manor')) score += 15;
|
||||
if (values.estate.toLowerCase().includes('cottage')) score += 5;
|
||||
|
||||
// Social connections
|
||||
if (values.connections.toLowerCase().includes('nobility')) score += 25;
|
||||
if (values.connections.toLowerCase().includes('london')) score += 15;
|
||||
if (values.connections.toLowerCase().includes('bath')) score += 10;
|
||||
|
||||
// Accomplishments
|
||||
score += values.accomplishments.length * 8;
|
||||
|
||||
// Personality traits
|
||||
score += values.personality.length * 6;
|
||||
|
||||
// Calculate result
|
||||
let result = '';
|
||||
if (score >= 100) {
|
||||
result = "Most Eligible! You rival Mr. Darcy in desirability. Mrs. Bennet would faint from joy at the prospect of your acquaintance.";
|
||||
} else if (score >= 75) {
|
||||
result = "Highly Suitable! Like Mr. Bingley, you present an excellent prospect for any family's eldest daughter.";
|
||||
} else if (score >= 50) {
|
||||
result = "Quite Promising! Colonel Brandon would consider you a worthy competitor in the marriage market.";
|
||||
} else if (score >= 25) {
|
||||
result = "Moderately Advantageous! You stand somewhere between a country parson and a militia officer in eligibility.";
|
||||
} else {
|
||||
result = "Rather Modest! Perhaps consider a position as a governess or companion while improving your prospects?";
|
||||
}
|
||||
|
||||
setResult(result);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto space-y-12">
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">Marriage Market Calculator</h1>
|
||||
<p className="text-sage-700 max-w-2xl mx-auto">
|
||||
Determine your standing in the marriage market through this thoroughly scientific calculation of your worldly and personal merits
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="bg-cream-50 p-8 rounded-lg space-y-6">
|
||||
{/* Income Input */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sage-900 font-semibold">
|
||||
Annual Income (in pounds)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={values.income}
|
||||
onChange={(e) => setValues({ ...values, income: Number(e.target.value) })}
|
||||
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500"
|
||||
placeholder="e.g., 10000"
|
||||
/>
|
||||
<p className="text-sm text-sage-600 italic">
|
||||
"A single man in possession of a good fortune must be in want of a wife."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Estate Input */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sage-900 font-semibold">
|
||||
Principal Estate
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={values.estate}
|
||||
onChange={(e) => setValues({ ...values, estate: e.target.value })}
|
||||
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500"
|
||||
placeholder="e.g., Pemberley Estate"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Connections Input */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sage-900 font-semibold">
|
||||
Social Connections
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={values.connections}
|
||||
onChange={(e) => setValues({ ...values, connections: e.target.value })}
|
||||
className="w-full p-3 rounded-lg border-sage-200 focus:ring-sage-500 focus:border-sage-500"
|
||||
placeholder="e.g., Connected to nobility in London"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Accomplishments Selection */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sage-900 font-semibold">
|
||||
Notable Accomplishments
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{ACCOMPLISHMENTS.map((accomplishment) => (
|
||||
<label key={accomplishment} className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={values.accomplishments.includes(accomplishment)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setValues({
|
||||
...values,
|
||||
accomplishments: [...values.accomplishments, accomplishment]
|
||||
});
|
||||
} else {
|
||||
setValues({
|
||||
...values,
|
||||
accomplishments: values.accomplishments.filter(a => a !== accomplishment)
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="rounded border-sage-300 text-sage-600 focus:ring-sage-500"
|
||||
/>
|
||||
<span className="text-sage-700">{accomplishment}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Personality Traits Selection */}
|
||||
<div className="space-y-2">
|
||||
<label className="block text-sage-900 font-semibold">
|
||||
Personal Qualities
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{PERSONALITY_TRAITS.map((trait) => (
|
||||
<label key={trait} className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={values.personality.includes(trait)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setValues({
|
||||
...values,
|
||||
personality: [...values.personality, trait]
|
||||
});
|
||||
} else {
|
||||
setValues({
|
||||
...values,
|
||||
personality: values.personality.filter(t => t !== trait)
|
||||
});
|
||||
}
|
||||
}}
|
||||
className="rounded border-sage-300 text-sage-600 focus:ring-sage-500"
|
||||
/>
|
||||
<span className="text-sage-700">{trait}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={calculateMarketValue}
|
||||
className="w-full bg-sage-500 text-white px-6 py-3 rounded-lg hover:bg-sage-600 transition"
|
||||
>
|
||||
Calculate Market Value
|
||||
</button>
|
||||
|
||||
{result && (
|
||||
<div className="mt-6 p-6 bg-sage-50 rounded-lg">
|
||||
<p className="font-cormorant text-xl text-sage-900 italic text-center">
|
||||
{result}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MarketCalculator;
|
||||
|
|
@ -1,127 +1,176 @@
|
|||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { quizQuestions, type QuizResult } from '@/data/quiz';
|
||||
import { quizResults } from '@/data/quiz';
|
||||
import type { QuizQuestion, AustenCharacter } from '../types';
|
||||
|
||||
const QUIZ_QUESTIONS: QuizQuestion[] = [
|
||||
{
|
||||
id: '1',
|
||||
question: 'How do you approach matters of the heart?',
|
||||
options: [
|
||||
{
|
||||
text: 'With passion and spontaneity',
|
||||
character: 'Marianne Dashwood',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'With wit and intelligence',
|
||||
character: 'Elizabeth Bennet',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'With careful consideration and practicality',
|
||||
character: 'Elinor Dashwood',
|
||||
points: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
question: 'What quality do you value most in a potential partner?',
|
||||
options: [
|
||||
{
|
||||
text: 'Their social standing and connections',
|
||||
character: 'Emma Woodhouse',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'Their character and principles',
|
||||
character: 'Elizabeth Bennet',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'Their stability and reliability',
|
||||
character: 'Anne Elliot',
|
||||
points: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
question: 'How do you feel about social gatherings?',
|
||||
options: [
|
||||
{
|
||||
text: 'I love being the center of attention',
|
||||
character: 'Emma Woodhouse',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'I enjoy observing and making witty observations',
|
||||
character: 'Elizabeth Bennet',
|
||||
points: 2
|
||||
},
|
||||
{
|
||||
text: 'I prefer intimate gatherings with close friends',
|
||||
character: 'Anne Elliot',
|
||||
points: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const CHARACTER_DESCRIPTIONS: Record<AustenCharacter, string> = {
|
||||
'Elizabeth Bennet': 'Like Elizabeth, you are witty, intelligent, and not afraid to speak your mind. You value genuine connections over social status and aren\'t afraid to turn down an unsuitable match.',
|
||||
'Emma Woodhouse': 'Like Emma, you are confident, socially adept, and perhaps a bit meddlesome (in the best way). You have a natural talent for bringing people together.',
|
||||
'Marianne Dashwood': 'Like Marianne, you wear your heart on your sleeve and believe in passionate, romantic love. You\'re not afraid to express your emotions openly.',
|
||||
'Anne Elliot': 'Like Anne, you are thoughtful, patient, and deeply loyal. You understand that true love sometimes requires waiting for the right moment.',
|
||||
'Catherine Morland': 'Like Catherine, you approach life with wonder and imagination. Your romantic nature sometimes leads you to see mysteries where there are none.',
|
||||
'Elinor Dashwood': 'Like Elinor, you are sensible and responsible, often putting others\' needs before your own. You handle matters of the heart with grace and discretion.'
|
||||
};
|
||||
|
||||
const Quiz = () => {
|
||||
const [currentQuestion, setCurrentQuestion] = useState(0);
|
||||
const [answers, setAnswers] = useState<string[]>([]);
|
||||
const [result, setResult] = useState<QuizResult | null>(null);
|
||||
const [answers, setAnswers] = useState<Record<string, AustenCharacter>>({});
|
||||
const [result, setResult] = useState<AustenCharacter | null>(null);
|
||||
|
||||
const handleAnswer = (value: string) => {
|
||||
const newAnswers = [...answers];
|
||||
newAnswers[currentQuestion] = value;
|
||||
setAnswers(newAnswers);
|
||||
};
|
||||
const handleAnswer = (character: AustenCharacter) => {
|
||||
setAnswers(prev => ({
|
||||
...prev,
|
||||
[QUIZ_QUESTIONS[currentQuestion].id]: character
|
||||
}));
|
||||
|
||||
const handleNext = () => {
|
||||
if (currentQuestion < quizQuestions.length - 1) {
|
||||
setCurrentQuestion(currentQuestion + 1);
|
||||
if (currentQuestion < QUIZ_QUESTIONS.length - 1) {
|
||||
setCurrentQuestion(prev => prev + 1);
|
||||
} else {
|
||||
calculateResult();
|
||||
// Calculate result
|
||||
const characterCounts: Record<AustenCharacter, number> = Object.values(answers).reduce(
|
||||
(acc, character) => ({
|
||||
...acc,
|
||||
[character]: (acc[character] || 0) + 1
|
||||
}),
|
||||
{} as Record<AustenCharacter, number>
|
||||
);
|
||||
|
||||
const result = Object.entries(characterCounts).reduce(
|
||||
(max, [character, count]) => (count > (characterCounts[max] || 0) ? character : max),
|
||||
Object.keys(characterCounts)[0]
|
||||
) as AustenCharacter;
|
||||
|
||||
setResult(result);
|
||||
}
|
||||
};
|
||||
|
||||
const calculateResult = () => {
|
||||
// Simple calculation - most frequent answer determines the result
|
||||
const counts = answers.reduce((acc, answer) => {
|
||||
acc[answer] = (acc[answer] || 0) + 1;
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const result = Object.entries(counts).reduce((a, b) =>
|
||||
counts[a[0]] > counts[b[0]] ? a : b
|
||||
)[0];
|
||||
|
||||
setResult(quizResults[result]);
|
||||
const resetQuiz = () => {
|
||||
setCurrentQuestion(0);
|
||||
setAnswers({});
|
||||
setResult(null);
|
||||
};
|
||||
|
||||
if (result) {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-serif text-4xl">Your Result</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
You are most like...
|
||||
<div className="max-w-2xl mx-auto text-center space-y-8">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">Your Result</h1>
|
||||
<div className="bg-cream-50 p-8 rounded-lg">
|
||||
<h2 className="font-cormorant text-3xl text-sage-900 mb-4">
|
||||
You are {result}!
|
||||
</h2>
|
||||
<p className="text-sage-700 mb-6">
|
||||
{CHARACTER_DESCRIPTIONS[result]}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Card className="max-w-2xl mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif text-2xl">{result.character}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="italic text-muted-foreground">{result.quote}</p>
|
||||
<p>{result.description}</p>
|
||||
<Button
|
||||
className="w-full mt-4"
|
||||
onClick={() => {
|
||||
setCurrentQuestion(0);
|
||||
setAnswers([]);
|
||||
setResult(null);
|
||||
}}
|
||||
>
|
||||
Take Quiz Again
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<button
|
||||
onClick={resetQuiz}
|
||||
className="bg-sage-500 text-white px-6 py-2 rounded hover:bg-sage-600 transition"
|
||||
>
|
||||
Take Quiz Again
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="max-w-2xl mx-auto space-y-8">
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-serif text-4xl">Which Austen Bride Are You?</h1>
|
||||
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
|
||||
Answer these questions to discover your Austen heroine match
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">
|
||||
Which Austen Bride Are You?
|
||||
</h1>
|
||||
<p className="text-sage-700">
|
||||
Answer these questions to discover your literary matrimonial counterpart
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Card className="max-w-2xl mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle className="font-serif">
|
||||
Question {currentQuestion + 1} of {quizQuestions.length}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-lg mb-6">{quizQuestions[currentQuestion].question}</p>
|
||||
<RadioGroup
|
||||
value={answers[currentQuestion]}
|
||||
onValueChange={handleAnswer}
|
||||
className="space-y-4"
|
||||
>
|
||||
{quizQuestions[currentQuestion].options.map((option) => (
|
||||
<div
|
||||
key={option.value}
|
||||
className="flex items-start space-x-3 rounded-lg border p-4 transition-colors hover:bg-accent cursor-pointer"
|
||||
onClick={() => handleAnswer(option.value)}
|
||||
>
|
||||
<RadioGroupItem value={option.value} id={option.value} className="mt-1" />
|
||||
<Label
|
||||
htmlFor={option.value}
|
||||
className="cursor-pointer flex-grow text-base leading-relaxed"
|
||||
>
|
||||
{option.label}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</RadioGroup>
|
||||
<Button
|
||||
className="w-full mt-8"
|
||||
size="lg"
|
||||
onClick={handleNext}
|
||||
disabled={!answers[currentQuestion]}
|
||||
>
|
||||
{currentQuestion === quizQuestions.length - 1 ? 'See Result' : 'Next Question'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="bg-cream-50 p-8 rounded-lg">
|
||||
<div className="mb-6">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-2">
|
||||
Question {currentQuestion + 1} of {QUIZ_QUESTIONS.length}
|
||||
</h2>
|
||||
<p className="text-sage-700">
|
||||
{QUIZ_QUESTIONS[currentQuestion].question}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{QUIZ_QUESTIONS[currentQuestion].options.map((option) => (
|
||||
<button
|
||||
key={option.text}
|
||||
onClick={() => handleAnswer(option.character)}
|
||||
className="w-full text-left p-4 rounded bg-white hover:bg-sage-50 transition"
|
||||
>
|
||||
{option.text}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Quiz;
|
||||
|
|
|
|||
98
src/pages/Stories.tsx
Normal file
98
src/pages/Stories.tsx
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import type { WeddingStory } from '../types';
|
||||
|
||||
const SAMPLE_STORIES: WeddingStory[] = [
|
||||
{
|
||||
id: '1',
|
||||
couple: 'Elizabeth Bennet & Fitzwilliam Darcy',
|
||||
story: "What began as mutual prejudice transformed into the deepest love. Through misunderstandings and societal pressures, we discovered that first impressions are not always to be trusted. Our journey taught us the importance of overcoming pride and learning to see the truth in each other's hearts.",
|
||||
date: '1813',
|
||||
location: 'Longbourn Church, followed by a celebration at Pemberley',
|
||||
imageUrl: '/images/lizzy-darcy.jpg',
|
||||
quotes: [
|
||||
'"In vain I have struggled. It will not do. My feelings will not be repressed. You must allow me to tell you how ardently I admire and love you."',
|
||||
'"You have bewitched me, body and soul, and I love... I love... I love you."'
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
couple: 'Emma Woodhouse & George Knightley',
|
||||
story: "Sometimes love is right before our eyes, if only we have the wisdom to see it. What started as a friendship built on honesty and mutual respect blossomed into something more profound. Mr. Knightley helped me see my own faults and grow into a better person, while never losing faith in my essential character.",
|
||||
date: '1815',
|
||||
location: 'Hartfield Estate',
|
||||
imageUrl: '/images/emma-knightley.jpg',
|
||||
quotes: [
|
||||
'"If I loved you less, I might be able to talk about it more."',
|
||||
'"My dearest Emma, for dearest you will always be..."'
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const Stories = () => {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto space-y-12">
|
||||
{/* Header */}
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">Success Stories</h1>
|
||||
<p className="text-sage-700 max-w-2xl mx-auto">
|
||||
Tales of love and matrimonial bliss from our most beloved couples
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{/* Stories */}
|
||||
<div className="space-y-16">
|
||||
{SAMPLE_STORIES.map((story) => (
|
||||
<article key={story.id} className="bg-cream-50 rounded-lg overflow-hidden">
|
||||
<div className="aspect-w-16 aspect-h-9 bg-sage-200">
|
||||
{/* In a real app, this would be a proper image */}
|
||||
<div className="w-full h-64 bg-sage-300" />
|
||||
</div>
|
||||
|
||||
<div className="p-8 space-y-6">
|
||||
<header className="text-center space-y-2">
|
||||
<h2 className="font-cormorant text-3xl text-sage-900">
|
||||
{story.couple}
|
||||
</h2>
|
||||
<p className="text-sage-700">
|
||||
{story.location} • {story.date}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div className="prose prose-sage max-w-none">
|
||||
<p className="text-sage-800">{story.story}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{story.quotes.map((quote, index) => (
|
||||
<blockquote key={index} className="border-l-4 border-sage-300 pl-4 italic text-sage-700">
|
||||
{quote}
|
||||
</blockquote>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center">
|
||||
<button className="bg-sage-500 text-white px-6 py-2 rounded-lg hover:bg-sage-600 transition">
|
||||
Read Full Story
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Submit Your Story CTA */}
|
||||
<section className="bg-sage-50 p-8 rounded-lg text-center space-y-4">
|
||||
<h2 className="font-cormorant text-2xl text-sage-900">
|
||||
Share Your Love Story
|
||||
</h2>
|
||||
<p className="text-sage-700">
|
||||
Have you found your perfect match? We'd love to feature your story in our collection.
|
||||
</p>
|
||||
<button className="bg-sage-500 text-white px-6 py-2 rounded-lg hover:bg-sage-600 transition">
|
||||
Submit Your Story
|
||||
</button>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Stories;
|
||||
121
src/pages/Vendors.tsx
Normal file
121
src/pages/Vendors.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { useState } from 'react';
|
||||
import type { VendorListing } from '../types';
|
||||
|
||||
const SAMPLE_VENDORS: VendorListing[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Pemberley Estate',
|
||||
description: 'A grand estate offering the perfect setting for your matrimonial celebration. With its extensive grounds and elegant halls, Pemberley provides an atmosphere of refined sophistication that would please even the most discerning of couples.',
|
||||
category: 'venue',
|
||||
location: 'Derbyshire',
|
||||
imageUrl: '/images/pemberley.jpg'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Mrs. Bennet\'s Matchmaking Services',
|
||||
description: 'With five daughters successfully married off, Mrs. Bennet brings her expertise to your search for the perfect match. Specializing in gentlemen of good fortune.',
|
||||
category: 'services',
|
||||
location: 'Longbourn, Hertfordshire',
|
||||
imageUrl: '/images/matchmaking.jpg'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: 'Modiste Madame Delafield',
|
||||
description: 'Exquisite wedding attire that combines Regency elegance with modern sensibilities. Our designs have graced the most fashionable assemblies in Bath.',
|
||||
category: 'attire',
|
||||
location: 'Bath',
|
||||
imageUrl: '/images/modiste.jpg'
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
name: 'Meryton Assembly Catering',
|
||||
description: 'From intimate family dinners to grand balls, we provide the finest refreshments worthy of any social occasion. Known for our delectable white soup.',
|
||||
category: 'catering',
|
||||
location: 'Meryton',
|
||||
imageUrl: '/images/catering.jpg'
|
||||
}
|
||||
];
|
||||
|
||||
const Vendors = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<VendorListing['category'] | 'all'>('all');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const filteredVendors = SAMPLE_VENDORS.filter(vendor => {
|
||||
const matchesCategory = selectedCategory === 'all' || vendor.category === selectedCategory;
|
||||
const matchesSearch = vendor.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
vendor.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
vendor.location.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesCategory && matchesSearch;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto space-y-12">
|
||||
{/* Header */}
|
||||
<header className="text-center space-y-4">
|
||||
<h1 className="font-cormorant text-4xl text-sage-900">Vendor Directory</h1>
|
||||
<p className="text-sage-700 max-w-2xl mx-auto">
|
||||
Discover the finest establishments and services to ensure your special day is nothing short of perfect
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{/* Filters */}
|
||||
<div className="flex flex-col sm:flex-row justify-between gap-4">
|
||||
<div className="flex gap-2">
|
||||
{(['all', 'venue', 'services', 'attire', 'catering'] as const).map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
className={`px-4 py-2 rounded-lg capitalize ${
|
||||
selectedCategory === category
|
||||
? 'bg-sage-500 text-white'
|
||||
: 'bg-sage-100 text-sage-700 hover:bg-sage-200'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search vendors..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="px-4 py-2 rounded-lg border border-sage-200 focus:ring-sage-500 focus:border-sage-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Vendor Grid */}
|
||||
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{filteredVendors.map((vendor) => (
|
||||
<article key={vendor.id} className="bg-cream-50 rounded-lg overflow-hidden shadow-lg">
|
||||
<div className="aspect-w-16 aspect-h-9 bg-sage-200">
|
||||
{/* In a real app, this would be a proper image */}
|
||||
<div className="w-full h-48 bg-sage-300" />
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div>
|
||||
<h2 className="font-cormorant text-2xl text-sage-900 mb-2">{vendor.name}</h2>
|
||||
<p className="text-sage-700 text-sm mb-2">{vendor.location}</p>
|
||||
<span className="inline-block px-3 py-1 rounded-full text-xs capitalize bg-sage-100 text-sage-700">
|
||||
{vendor.category}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sage-700">{vendor.description}</p>
|
||||
<button className="w-full bg-sage-500 text-white px-4 py-2 rounded-lg hover:bg-sage-600 transition">
|
||||
Request Information
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{filteredVendors.length === 0 && (
|
||||
<div className="text-center py-12 text-sage-700">
|
||||
No vendors found matching your criteria
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Vendors;
|
||||
53
src/types/index.ts
Normal file
53
src/types/index.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
export interface BlogPost {
|
||||
id: string;
|
||||
title: string;
|
||||
author: string;
|
||||
content: string;
|
||||
date: string;
|
||||
category: 'charlotte' | 'marianne';
|
||||
}
|
||||
|
||||
export interface QuizQuestion {
|
||||
id: string;
|
||||
question: string;
|
||||
options: {
|
||||
text: string;
|
||||
character: AustenCharacter;
|
||||
points: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type AustenCharacter =
|
||||
| 'Elizabeth Bennet'
|
||||
| 'Emma Woodhouse'
|
||||
| 'Marianne Dashwood'
|
||||
| 'Anne Elliot'
|
||||
| 'Catherine Morland'
|
||||
| 'Elinor Dashwood';
|
||||
|
||||
export interface AdviceQuestion {
|
||||
id: string;
|
||||
question: string;
|
||||
askedBy: string;
|
||||
response?: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
export interface VendorListing {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: 'venue' | 'services' | 'attire' | 'catering';
|
||||
location: string;
|
||||
imageUrl: string;
|
||||
}
|
||||
|
||||
export interface WeddingStory {
|
||||
id: string;
|
||||
couple: string;
|
||||
story: string;
|
||||
date: string;
|
||||
location: string;
|
||||
imageUrl: string;
|
||||
quotes: string[];
|
||||
}
|
||||
|
|
@ -1,83 +1,49 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: ['class'],
|
||||
module.exports = {
|
||||
content: [
|
||||
'./pages/**/*.{ts,tsx}',
|
||||
'./components/**/*.{ts,tsx}',
|
||||
'./app/**/*.{ts,tsx}',
|
||||
'./src/**/*.{ts,tsx}',
|
||||
"./index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: '2rem',
|
||||
screens: {
|
||||
'2xl': '1400px',
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
typography: {
|
||||
DEFAULT: {
|
||||
css: {
|
||||
maxWidth: '100%',
|
||||
},
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)',
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))',
|
||||
cream: {
|
||||
50: '#FFFBF7',
|
||||
100: '#FFF7ED',
|
||||
200: '#FFE4CA',
|
||||
300: '#FFD1A7',
|
||||
400: '#FFBE84',
|
||||
500: '#FFAB61',
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))',
|
||||
sage: {
|
||||
50: '#F4F7F4',
|
||||
100: '#E8EFE8',
|
||||
200: '#D1DFD1',
|
||||
300: '#BACFBA',
|
||||
400: '#A3BFA3',
|
||||
500: '#8CAF8C',
|
||||
600: '#7A9D7A',
|
||||
700: '#688B68',
|
||||
800: '#567956',
|
||||
900: '#2C3B2C',
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))',
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))',
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))',
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))',
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))',
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
},
|
||||
keyframes: {
|
||||
'accordion-down': {
|
||||
from: { height: '0' },
|
||||
to: { height: 'var(--radix-accordion-content-height)' },
|
||||
},
|
||||
'accordion-up': {
|
||||
from: { height: 'var(--radix-accordion-content-height)' },
|
||||
to: { height: '0' },
|
||||
rose: {
|
||||
50: '#FFF5F5',
|
||||
100: '#FFE8E8',
|
||||
200: '#FFD1D1',
|
||||
300: '#FFBABA',
|
||||
400: '#FFA3A3',
|
||||
500: '#FF8C8C',
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out',
|
||||
fontFamily: {
|
||||
cormorant: ['Cormorant Garamond', 'serif'],
|
||||
lato: ['Lato', 'sans-serif'],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')],
|
||||
};
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/forms'),
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import path from 'path';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
|
|
@ -9,7 +10,4 @@ export default defineConfig({
|
|||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['lucide-react'],
|
||||
},
|
||||
});
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue