feat(frontend): set up React Router with page components and navigation
- Updated main.tsx to wrap App in BrowserRouter - Configured routes in App.tsx with header navigation - Created page components: RecipeListPage, RecipeDetailPage, CookModePage, NotFoundPage - Added active link highlighting for current route - Defined routes: / (list), /recipe/new, /recipe/:id, /recipe/:id/cook, * (404) - Updated TODO.md to mark React Router task as complete - Verified build and dev server work correctly
This commit is contained in:
parent
427fa46cf0
commit
94c061a850
10
TODO.md
10
TODO.md
|
|
@ -18,7 +18,7 @@
|
|||
### Frontend Setup
|
||||
- [x] Initialize React + Vite project
|
||||
- [x] Configure Tailwind CSS
|
||||
- [ ] Set up React Router
|
||||
- [x] Set up React Router
|
||||
- [ ] Create recipe list page
|
||||
- [ ] Create recipe detail/edit page
|
||||
- [ ] Implement cook mode UI
|
||||
|
|
@ -46,6 +46,14 @@
|
|||
## ✅ Completed Tasks
|
||||
|
||||
### 2026-03-24
|
||||
- **React Router setup**
|
||||
- Updated main.tsx to wrap App in BrowserRouter
|
||||
- Configured routes in App.tsx with navigation header
|
||||
- Created page components: RecipeListPage, RecipeDetailPage, CookModePage, NotFoundPage
|
||||
- Added active link highlighting in navigation
|
||||
- Defined routes: / (list), /recipe/new, /recipe/:id, /recipe/:id/cook, * (404)
|
||||
- Verified build and dev server work correctly
|
||||
|
||||
- **Tailwind CSS configuration**
|
||||
- Installed Tailwind CSS v4 with PostCSS and Autoprefixer
|
||||
- Installed @tailwindcss/postcss plugin for v4 compatibility
|
||||
|
|
|
|||
|
|
@ -1,24 +1,67 @@
|
|||
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';
|
||||
|
||||
function App() {
|
||||
const location = useLocation();
|
||||
|
||||
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 (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<header className="bg-white shadow">
|
||||
<div className="max-w-7xl mx-auto py-6 px-4">
|
||||
<h1 className="text-3xl font-bold text-gray-900">Recipe Manager</h1>
|
||||
<p className="mt-2 text-gray-600">Frontend initialized and ready for development</p>
|
||||
<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">
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<p className="text-gray-700 mb-4">
|
||||
✅ Vite + React + TypeScript project created successfully.
|
||||
</p>
|
||||
<p className="text-gray-700">
|
||||
✅ Tailwind CSS configured and working!
|
||||
<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>
|
||||
</main>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import { useParams } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* CookModePage - Hands-free cooking interface with wake lock
|
||||
*/
|
||||
export function CookModePage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Cook Mode {id && `- Recipe #${id}`}
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Step-by-step cooking interface
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<p className="text-gray-600">
|
||||
Cook mode interface will be implemented later
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { Link } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* NotFoundPage - 404 error page
|
||||
*/
|
||||
export function NotFoundPage() {
|
||||
return (
|
||||
<div className="text-center py-12">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-4">404</h2>
|
||||
<p className="text-xl text-gray-600 mb-8">Page not found</p>
|
||||
<Link
|
||||
to="/"
|
||||
className="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
>
|
||||
Back to Recipes
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import { useParams } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* RecipeDetailPage - View and edit a single recipe
|
||||
*/
|
||||
export function RecipeDetailPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
Recipe Details {id ? `#${id}` : '(New)'}
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
View and edit recipe information
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<p className="text-gray-600">
|
||||
Recipe detail/edit form will be implemented here (next task)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* RecipeListPage - Displays a list of all recipes with search and filtering
|
||||
*/
|
||||
export function RecipeListPage() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">My Recipes</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">
|
||||
Browse and search your recipe collection
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow p-6">
|
||||
<p className="text-gray-600">
|
||||
Recipe list will be implemented here (next task)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue