97 lines
3.5 KiB
TypeScript
97 lines
3.5 KiB
TypeScript
import { Routes, Route, Link, useLocation } from 'react-router-dom';
|
|
import { RecipeListPage } from './pages/RecipeListPage';
|
|
import { RecipeDetailPage } from './pages/RecipeDetailPage';
|
|
import { CookModePage } from './pages/CookModePage';
|
|
import { NotFoundPage } from './pages/NotFoundPage';
|
|
import { ErrorBoundary } from './components/ErrorBoundary';
|
|
import { ToastContainer } from './components/Toast';
|
|
import { useToast } from './hooks/useToast';
|
|
import { createContext, useContext } from 'react';
|
|
|
|
// Create toast context to share toast functionality across the app
|
|
interface ToastContextType {
|
|
success: (message: string, duration?: number) => string;
|
|
error: (message: string, duration?: number) => string;
|
|
info: (message: string, duration?: number) => string;
|
|
warning: (message: string, duration?: number) => string;
|
|
}
|
|
|
|
const ToastContext = createContext<ToastContextType | null>(null);
|
|
|
|
export function useToastContext() {
|
|
const context = useContext(ToastContext);
|
|
if (!context) {
|
|
throw new Error('useToastContext must be used within ToastProvider');
|
|
}
|
|
return context;
|
|
}
|
|
|
|
function App() {
|
|
const location = useLocation();
|
|
const toast = useToast();
|
|
|
|
const isActive = (path: string) => {
|
|
if (path === '/' && location.pathname === '/') return true;
|
|
if (path !== '/' && location.pathname.startsWith(path)) return true;
|
|
return false;
|
|
};
|
|
|
|
const linkClass = (path: string) => {
|
|
const base = "px-3 py-2 rounded-md text-sm font-medium transition-colors";
|
|
return isActive(path)
|
|
? `${base} bg-blue-100 text-blue-700`
|
|
: `${base} text-gray-700 hover:bg-gray-100`;
|
|
};
|
|
|
|
return (
|
|
<ErrorBoundary>
|
|
<ToastContext.Provider value={toast}>
|
|
<div className="min-h-screen bg-gray-50">
|
|
<ToastContainer messages={toast.messages} onClose={toast.removeToast} />
|
|
|
|
<header className="bg-white shadow">
|
|
<div className="max-w-7xl mx-auto px-4">
|
|
<div className="flex items-center justify-between h-16">
|
|
<div className="flex items-center">
|
|
<Link to="/" className="flex-shrink-0">
|
|
<h1 className="text-2xl font-bold text-gray-900">Recipe Manager</h1>
|
|
</Link>
|
|
</div>
|
|
|
|
<nav className="flex space-x-4">
|
|
<Link to="/" className={linkClass('/')}>
|
|
Recipes
|
|
</Link>
|
|
<Link to="/recipe/new" className={linkClass('/recipe/new')}>
|
|
Add Recipe
|
|
</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="max-w-7xl mx-auto py-6 px-4">
|
|
<Routes>
|
|
<Route path="/" element={<RecipeListPage />} />
|
|
<Route path="/recipe/new" element={<RecipeDetailPage />} />
|
|
<Route path="/recipe/:id" element={<RecipeDetailPage />} />
|
|
<Route path="/recipe/:id/cook" element={<CookModePage />} />
|
|
<Route path="*" element={<NotFoundPage />} />
|
|
</Routes>
|
|
</main>
|
|
|
|
<footer className="bg-white border-t mt-12">
|
|
<div className="max-w-7xl mx-auto py-6 px-4">
|
|
<p className="text-center text-sm text-gray-500">
|
|
Recipe Manager MVP - Built with React + Vite + TypeScript
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</ToastContext.Provider>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
export default App;
|