Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 62 additions & 21 deletions frontend/src/components/layout/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,27 +40,68 @@ export default function Navbar() {
{darkMode ? '☀️' : '🌙'}
</button>

{isAuthenticated ? (
<>
{user?.role === 'admin' && (
<Link to="/admin" className="hover:text-gocart-orange text-sm">Admin</Link>
)}
<Link to="/orders" className="hover:text-gocart-orange text-sm hidden sm:inline">Orders</Link>
<Link to="/wishlist" className="hover:text-gocart-orange text-sm hidden sm:inline">Wishlist</Link>
<Link to="/profile" className="hover:text-gocart-orange text-sm hidden sm:inline">Profile</Link>
<Link to="/cart" className="relative hover:text-gocart-orange">
🛒
{itemCount > 0 && (
<span className="absolute -top-2 -right-2 bg-gocart-orange text-xs rounded-full w-5 h-5 flex items-center justify-center">
{itemCount}
</span>
)}
</Link>
<button onClick={() => dispatch(logout())} className="text-sm hover:text-gocart-orange">
Logout
</button>
</>
) : (
{isAuthenticated ? (
user?.role === 'admin' ? (
<>
<Link
to="/admin"
className="hover:text-gocart-orange text-sm"
>
Admin
</Link>

<button
onClick={() => dispatch(logout())}
className="text-sm hover:text-gocart-orange"
>
Logout
</button>
</>
) : (
<>
<Link
to="/orders"
className="hover:text-gocart-orange text-sm hidden sm:inline"
>
Orders
</Link>

<Link
to="/wishlist"
className="hover:text-gocart-orange text-sm hidden sm:inline"
>
Wishlist
</Link>

<Link
to="/profile"
className="hover:text-gocart-orange text-sm hidden sm:inline"
>
Profile
</Link>

<Link
to="/cart"
className="relative hover:text-gocart-orange"
>
🛒

{itemCount > 0 && (
<span className="absolute -top-2 -right-2 bg-gocart-orange text-xs rounded-full w-5 h-5 flex items-center justify-center">
{itemCount}
</span>
)}
</Link>

<button
onClick={() => dispatch(logout())}
className="text-sm hover:text-gocart-orange"
>
Logout
</button>
</>
)
) : (
<>
<Link to="/login" className="hover:text-gocart-orange text-sm">Login</Link>
<Link to="/register" className="btn-primary text-sm py-1.5 px-3">Sign Up</Link>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/products/ProductCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export default function ProductCard({ product }) {
<Link to={`/products/${product.slug}`} className="card overflow-hidden hover:shadow-lg transition-shadow group">
<div className="relative aspect-square bg-gray-100 dark:bg-gray-700">
{product.primary_image ? (
<img src={`http://localhost:8000${product.primary_image}`}
<img src={
product.primary_image?.startsWith('http')
? product.primary_image
: `http://localhost:8000${product.primary_image}`
}
alt={product.name}
className="w-full h-full object-cover"
loading="lazy"
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/pages/AdminDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line } from 'recharts'
import { analyticsAPI } from '../services/api'
import { Link } from 'react-router-dom'

export default function AdminDashboard() {
const [stats, setStats] = useState(null)
Expand Down Expand Up @@ -30,6 +31,48 @@ export default function AdminDashboard() {
<div className="max-w-7xl mx-auto px-4 py-8">
<h1 className="text-2xl font-bold mb-2">GoCart Admin Dashboard</h1>
<p className="text-gray-500 mb-8">Shop Smarter. Shop Faster.</p>
<div className="grid md:grid-cols-3 gap-4 mb-8">

<Link
to="/admin/products"
className="card p-6 hover:shadow-lg transition"
>
<h3 className="font-bold text-lg mb-2">
📦 Manage Products
</h3>

<p className="text-gray-500">
View, edit and delete products
</p>
</Link>

<Link
to="/admin/products/add"
className="card p-6 hover:shadow-lg transition"
>
<h3 className="font-bold text-lg mb-2">
➕ Add Product
</h3>

<p className="text-gray-500">
Create a new product
</p>
</Link>

<Link
to="/admin/products"
className="card p-6 hover:shadow-lg transition"
>
<h3 className="font-bold text-lg mb-2">
📊 Inventory
</h3>

<p className="text-gray-500">
Check stock levels and pricing
</p>
</Link>

</div>

<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4 mb-8">
{statCards.map((s) => (
Expand Down
34 changes: 24 additions & 10 deletions frontend/src/pages/admin/EditProduct.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,33 @@ export default function EditProduct() {
...formData,
category: Number(formData.category),
})
if (selectedImage) {
const { data: product } = await productsAPI.get(slug)
if (selectedImage) {
const { data: product } = await productsAPI.get(slug)

const imageData = new FormData()
console.log("EDIT PRODUCT:", product)
console.log("PRODUCT ID:", product.id)
console.log("PRODUCT IMAGES:", product.images)

imageData.append('product', product.id)
imageData.append('image', selectedImage)
imageData.append('alt_text', formData.name)
imageData.append('is_primary', true)
imageData.append('order', 1)
const imageData = new FormData()

await productImagesAPI.upload(imageData)
}
imageData.append('image', selectedImage)
imageData.append('alt_text', formData.name)
imageData.append('is_primary', true)
imageData.append('order', 1)

if (product.images?.length > 0) {
console.log("Updating image:", product.images[0].id)

await productImagesAPI.update(
product.images[0].id,
imageData
)
} else {
imageData.append('product', product.id)

await productImagesAPI.upload(imageData)
}
}

toast.success('Product updated successfully')

Expand Down
7 changes: 7 additions & 0 deletions frontend/src/services/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ export const productImagesAPI = {
'Content-Type': 'multipart/form-data',
},
}),

update: (imageId, formData) =>
api.patch(`/products/images/${imageId}/`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}),

remove: (imageId) =>
api.delete(`/products/images/${imageId}/`),
Expand Down
Loading