version11
This commit is contained in:
parent
e87d7c53d1
commit
d6a0b8edb5
@ -1,19 +1,19 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, Outlet } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
const AdminLayout: React.FC = () => {
|
const AdminLayout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-gray-100">
|
<div className="flex min-h-screen bg-gray-100">
|
||||||
<aside className="w-64 bg-white shadow-md">
|
<aside className="w-64 bg-gray-900 text-white p-6">
|
||||||
<div className="p-6 text-2xl font-bold text-gray-800">Admin Panel</div>
|
<h1 className="text-2xl font-bold mb-8">Admin</h1>
|
||||||
<nav className="mt-6">
|
<nav className="space-y-4">
|
||||||
<Link to="/admin/products" className="block py-2.5 px-4 rounded hover:bg-gray-200">Products</Link>
|
<NavLink to="/admin/products" className={({isActive}) => isActive ? 'block text-blue-400' : 'block hover:text-gray-300'}>Products</NavLink>
|
||||||
<Link to="/admin/categories" className="block py-2.5 px-4 rounded hover:bg-gray-200">Categories</Link>
|
<NavLink to="/admin/sellers" className={({isActive}) => isActive ? 'block text-blue-400' : 'block hover:text-gray-300'}>Sellers</NavLink>
|
||||||
<Link to="/admin/sellers" className="block py-2.5 px-4 rounded hover:bg-gray-200">Sellers</Link>
|
<NavLink to="/admin/categories" className={({isActive}) => isActive ? 'block text-blue-400' : 'block hover:text-gray-300'}>Categories</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
<main className="flex-1 p-8">
|
<main className="flex-1 p-8">
|
||||||
<Outlet />
|
{children}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,12 +4,15 @@ import LoginPage from './LoginPage';
|
|||||||
import ProductList from './ProductList';
|
import ProductList from './ProductList';
|
||||||
import ProductDetail from './ProductDetail';
|
import ProductDetail from './ProductDetail';
|
||||||
import CartPage from './CartPage';
|
import CartPage from './CartPage';
|
||||||
|
import UserProfile from './UserProfile';
|
||||||
import AdminLayout from './AdminLayout';
|
import AdminLayout from './AdminLayout';
|
||||||
import AdminRoute from './AdminRoute';
|
import AdminRoute from './AdminRoute';
|
||||||
import ProductManagement from './ProductManagement';
|
import ProductManagement from './ProductManagement';
|
||||||
import ProductForm from './ProductForm';
|
import ProductForm from './ProductForm';
|
||||||
import SellerManagement from './SellerManagement';
|
import SellerManagement from './SellerManagement';
|
||||||
|
import SellerForm from './SellerForm';
|
||||||
import CategoryManagement from './CategoryManagement';
|
import CategoryManagement from './CategoryManagement';
|
||||||
|
import CategoryForm from './CategoryForm';
|
||||||
import { CartProvider } from './CartContext';
|
import { CartProvider } from './CartContext';
|
||||||
import { AuthProvider } from './AuthContext';
|
import { AuthProvider } from './AuthContext';
|
||||||
|
|
||||||
@ -28,6 +31,7 @@ function App() {
|
|||||||
<Route path="/product/:id" element={<ProductDetail />} />
|
<Route path="/product/:id" element={<ProductDetail />} />
|
||||||
<Route path="/cart" element={<CartPage />} />
|
<Route path="/cart" element={<CartPage />} />
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
|
<Route path="/profile" element={<UserProfile />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
@ -38,7 +42,11 @@ function App() {
|
|||||||
<Route path="products/create" element={<ProductForm />} />
|
<Route path="products/create" element={<ProductForm />} />
|
||||||
<Route path="products/edit/:id" element={<ProductForm />} />
|
<Route path="products/edit/:id" element={<ProductForm />} />
|
||||||
<Route path="categories" element={<CategoryManagement />} />
|
<Route path="categories" element={<CategoryManagement />} />
|
||||||
|
<Route path="categories/create" element={<CategoryForm />} />
|
||||||
|
<Route path="categories/edit/:id" element={<CategoryForm />} />
|
||||||
<Route path="sellers" element={<SellerManagement />} />
|
<Route path="sellers" element={<SellerManagement />} />
|
||||||
|
<Route path="sellers/create" element={<SellerForm />} />
|
||||||
|
<Route path="sellers/edit/:id" element={<SellerForm />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
34
frontend/src/CategoryForm.tsx
Normal file
34
frontend/src/CategoryForm.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
const CategoryForm: React.FC = () => {
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [formData, setFormData] = useState({ name: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
axios.get(`http://127.0.0.1:8000/categories/${id}/`)
|
||||||
|
.then(res => setFormData(res.data));
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const action = id ? axios.put(`http://127.0.0.1:8000/categories/${id}/`, formData) : axios.post('http://127.0.0.1:8000/categories/', formData);
|
||||||
|
action.then(() => navigate('/admin/categories'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h2 className="text-xl font-bold mb-4">{id ? 'Edit' : 'Create'} Category</h2>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<input className="block w-full p-2 border" placeholder="Name" value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} />
|
||||||
|
<button type="submit" className="bg-blue-500 text-white p-2 rounded">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CategoryForm;
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
interface Category {
|
interface Category {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
slug: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const CategoryManagement: React.FC = () => {
|
const CategoryManagement: React.FC = () => {
|
||||||
@ -39,13 +39,15 @@ const CategoryManagement: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<h2 className="text-xl font-bold mb-4">Category Management</h2>
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Category Management</h2>
|
||||||
|
<Link to="/admin/categories/create" className="bg-green-500 text-white px-4 py-2 rounded">Create Category</Link>
|
||||||
|
</div>
|
||||||
<table className="min-w-full bg-white border border-gray-200">
|
<table className="min-w-full bg-white border border-gray-200">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="py-2 px-4 border-b">ID</th>
|
<th className="py-2 px-4 border-b">ID</th>
|
||||||
<th className="py-2 px-4 border-b">Name</th>
|
<th className="py-2 px-4 border-b">Name</th>
|
||||||
<th className="py-2 px-4 border-b">Slug</th>
|
|
||||||
<th className="py-2 px-4 border-b">Actions</th>
|
<th className="py-2 px-4 border-b">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -54,8 +56,8 @@ const CategoryManagement: React.FC = () => {
|
|||||||
<tr key={category.id}>
|
<tr key={category.id}>
|
||||||
<td className="py-2 px-4 border-b">{category.id}</td>
|
<td className="py-2 px-4 border-b">{category.id}</td>
|
||||||
<td className="py-2 px-4 border-b">{category.name}</td>
|
<td className="py-2 px-4 border-b">{category.name}</td>
|
||||||
<td className="py-2 px-4 border-b">{category.slug}</td>
|
<td className="py-2 px-4 border-b space-x-2">
|
||||||
<td className="py-2 px-4 border-b">
|
<Link to={`/admin/categories/edit/${category.id}`} className="bg-blue-500 text-white px-2 py-1 rounded">Edit</Link>
|
||||||
<button
|
<button
|
||||||
onClick={() => deleteCategory(category.id)}
|
onClick={() => deleteCategory(category.id)}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded"
|
className="bg-red-500 text-white px-2 py-1 rounded"
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||||||
const ProductForm: React.FC = () => {
|
const ProductForm: React.FC = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [formData, setFormData] = useState({ name: '', price: '', description: '', category: '' });
|
const [formData, setFormData] = useState({ name: '', price: '', description: '', category: '', image: null as File | null });
|
||||||
const [categories, setCategories] = useState<{ id: number; name: string }[]>([]);
|
const [categories, setCategories] = useState<{ id: number; name: string }[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -20,22 +20,32 @@ const ProductForm: React.FC = () => {
|
|||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const action = id ? axios.put(`http://127.0.0.1:8000/products/${id}/`, formData) : axios.post('http://127.0.0.1:8000/products/', formData);
|
const data = new FormData();
|
||||||
|
data.append('name', formData.name);
|
||||||
|
data.append('price', formData.price);
|
||||||
|
data.append('description', formData.description);
|
||||||
|
data.append('category', formData.category);
|
||||||
|
if (formData.image) data.append('image', formData.image);
|
||||||
|
|
||||||
|
const action = id ? axios.put(`http://127.0.0.1:8000/products/${id}/`, data, { headers: { 'Content-Type': 'multipart/form-data' } })
|
||||||
|
: axios.post('http://127.0.0.1:8000/products/', data, { headers: { 'Content-Type': 'multipart/form-data' } });
|
||||||
|
|
||||||
action.then(() => navigate('/admin/products'));
|
action.then(() => navigate('/admin/products'));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4 bg-white rounded shadow-md">
|
||||||
<h2 className="text-xl font-bold mb-4">{id ? 'Edit' : 'Create'} Product</h2>
|
<h2 className="text-2xl font-bold mb-4">{id ? 'Edit' : 'Create'} Product</h2>
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<input className="block w-full p-2 border" placeholder="Name" value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} />
|
<input className="block w-full p-2 border rounded" placeholder="Name" value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} />
|
||||||
<input className="block w-full p-2 border" placeholder="Price" value={formData.price} onChange={e => setFormData({...formData, price: e.target.value})} />
|
<input className="block w-full p-2 border rounded" placeholder="Price" value={formData.price} onChange={e => setFormData({...formData, price: e.target.value})} />
|
||||||
<textarea className="block w-full p-2 border" placeholder="Description" value={formData.description} onChange={e => setFormData({...formData, description: e.target.value})} />
|
<textarea className="block w-full p-2 border rounded" placeholder="Description" value={formData.description} onChange={e => setFormData({...formData, description: e.target.value})} />
|
||||||
<select className="block w-full p-2 border" value={formData.category} onChange={e => setFormData({...formData, category: e.target.value})}>
|
<select className="block w-full p-2 border rounded" value={formData.category} onChange={e => setFormData({...formData, category: e.target.value})}>
|
||||||
<option value="">Select Category</option>
|
<option value="">Select Category</option>
|
||||||
{categories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
{categories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" className="bg-blue-500 text-white p-2 rounded">Save</button>
|
<input type="file" onChange={e => setFormData({...formData, image: e.target.files ? e.target.files[0] : null})} className="block w-full p-2 border rounded" />
|
||||||
|
<button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">Save Product</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -10,13 +10,16 @@ interface Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ProductList: React.FC = () => {
|
const ProductList: React.FC = () => {
|
||||||
const [products, setProducts] = useState<Product[]>([]);
|
const [allProducts, setAllProducts] = useState<Product[]>([]);
|
||||||
|
const [filteredProducts, setFilteredProducts] = useState<Product[]>([]);
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
axios.get('http://127.0.0.1:8000/products/')
|
axios.get('http://127.0.0.1:8000/products/')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
setProducts(response.data);
|
setAllProducts(response.data);
|
||||||
|
setFilteredProducts(response.data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -25,13 +28,28 @@ const ProductList: React.FC = () => {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFilteredProducts(
|
||||||
|
allProducts.filter(product =>
|
||||||
|
product.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [searchTerm, allProducts]);
|
||||||
|
|
||||||
if (loading) return <div className="text-center p-4">Loading...</div>;
|
if (loading) return <div className="text-center p-4">Loading...</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto p-4">
|
<div className="container mx-auto p-4">
|
||||||
<h1 className="text-2xl font-bold mb-4">Products</h1>
|
<h1 className="text-2xl font-bold mb-4">Products</h1>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="w-full p-2 mb-4 border rounded"
|
||||||
|
placeholder="Search products..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={e => setSearchTerm(e.target.value)}
|
||||||
|
/>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
{products.map(product => (
|
{filteredProducts.map(product => (
|
||||||
<ProductCard key={product.id} name={product.name} price={product.price} />
|
<ProductCard key={product.id} name={product.name} price={product.price} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
35
frontend/src/SellerForm.tsx
Normal file
35
frontend/src/SellerForm.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
const SellerForm: React.FC = () => {
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [formData, setFormData] = useState({ name: '', contact_email: '' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id) {
|
||||||
|
axios.get(`http://127.0.0.1:8000/sellers/${id}/`)
|
||||||
|
.then(res => setFormData(res.data));
|
||||||
|
}
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const action = id ? axios.put(`http://127.0.0.1:8000/sellers/${id}/`, formData) : axios.post('http://127.0.0.1:8000/sellers/', formData);
|
||||||
|
action.then(() => navigate('/admin/sellers'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h2 className="text-xl font-bold mb-4">{id ? 'Edit' : 'Create'} Seller</h2>
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
|
<input className="block w-full p-2 border" placeholder="Name" value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} />
|
||||||
|
<input className="block w-full p-2 border" placeholder="Contact Email" value={formData.contact_email} onChange={e => setFormData({...formData, contact_email: e.target.value})} />
|
||||||
|
<button type="submit" className="bg-blue-500 text-white p-2 rounded">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SellerForm;
|
||||||
@ -1,10 +1,11 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
interface Seller {
|
interface Seller {
|
||||||
id: number;
|
id: number;
|
||||||
store_name: string;
|
name: string;
|
||||||
is_verified: boolean;
|
contact_email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SellerManagement: React.FC = () => {
|
const SellerManagement: React.FC = () => {
|
||||||
@ -39,13 +40,16 @@ const SellerManagement: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<h2 className="text-xl font-bold mb-4">Seller Management</h2>
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h2 className="text-xl font-bold">Seller Management</h2>
|
||||||
|
<Link to="/admin/sellers/create" className="bg-green-500 text-white px-4 py-2 rounded">Create Seller</Link>
|
||||||
|
</div>
|
||||||
<table className="min-w-full bg-white border border-gray-200">
|
<table className="min-w-full bg-white border border-gray-200">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="py-2 px-4 border-b">ID</th>
|
<th className="py-2 px-4 border-b">ID</th>
|
||||||
<th className="py-2 px-4 border-b">Store Name</th>
|
<th className="py-2 px-4 border-b">Name</th>
|
||||||
<th className="py-2 px-4 border-b">Verified</th>
|
<th className="py-2 px-4 border-b">Contact</th>
|
||||||
<th className="py-2 px-4 border-b">Actions</th>
|
<th className="py-2 px-4 border-b">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -53,9 +57,10 @@ const SellerManagement: React.FC = () => {
|
|||||||
{sellers.map(seller => (
|
{sellers.map(seller => (
|
||||||
<tr key={seller.id}>
|
<tr key={seller.id}>
|
||||||
<td className="py-2 px-4 border-b">{seller.id}</td>
|
<td className="py-2 px-4 border-b">{seller.id}</td>
|
||||||
<td className="py-2 px-4 border-b">{seller.store_name}</td>
|
<td className="py-2 px-4 border-b">{seller.name}</td>
|
||||||
<td className="py-2 px-4 border-b">{seller.is_verified ? 'Yes' : 'No'}</td>
|
<td className="py-2 px-4 border-b">{seller.contact_email}</td>
|
||||||
<td className="py-2 px-4 border-b">
|
<td className="py-2 px-4 border-b space-x-2">
|
||||||
|
<Link to={`/admin/sellers/edit/${seller.id}`} className="bg-blue-500 text-white px-2 py-1 rounded">Edit</Link>
|
||||||
<button
|
<button
|
||||||
onClick={() => deleteSeller(seller.id)}
|
onClick={() => deleteSeller(seller.id)}
|
||||||
className="bg-red-500 text-white px-2 py-1 rounded"
|
className="bg-red-500 text-white px-2 py-1 rounded"
|
||||||
|
|||||||
59
frontend/src/UserProfile.tsx
Normal file
59
frontend/src/UserProfile.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useAuth } from './AuthContext';
|
||||||
|
|
||||||
|
const UserProfile: React.FC = () => {
|
||||||
|
const { user, logout } = useAuth();
|
||||||
|
const [orders, setOrders] = useState<any[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user) {
|
||||||
|
axios.get(`http://127.0.0.1:8000/orders/?user=${user.id}`)
|
||||||
|
.then(res => setOrders(res.data));
|
||||||
|
}
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 max-w-4xl mx-auto">
|
||||||
|
<h2 className="text-3xl font-bold mb-6">User Profile</h2>
|
||||||
|
{user ? (
|
||||||
|
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
|
||||||
|
<p className="mb-2"><strong>Username:</strong> {user.username}</p>
|
||||||
|
<button onClick={logout} className="mt-4 bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700">Logout</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p>Please log in to see your profile.</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{user && (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-2xl font-bold mb-4">Order History</h3>
|
||||||
|
{orders.length > 0 ? (
|
||||||
|
<div className="bg-white p-4 rounded shadow-md">
|
||||||
|
<table className="w-full text-left">
|
||||||
|
<thead>
|
||||||
|
<tr className="border-b">
|
||||||
|
<th className="py-2">ID</th>
|
||||||
|
<th className="py-2">Date</th>
|
||||||
|
<th className="py-2">Total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{orders.map(o => (
|
||||||
|
<tr key={o.id} className="border-b">
|
||||||
|
<td className="py-2">{o.id}</td>
|
||||||
|
<td className="py-2">{new Date(o.created_at).toLocaleDateString()}</td>
|
||||||
|
<td className="py-2">${o.total_price}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : <p>No orders found.</p>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UserProfile;
|
||||||
@ -1 +1,18 @@
|
|||||||
@import "tailwindcss";
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
@apply bg-gray-50 text-gray-900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.btn-primary {
|
||||||
|
@apply bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition duration-200 shadow-sm;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
@apply bg-white p-6 rounded-xl shadow-lg border border-gray-100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user