Skip to content
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jobs:
run: |
pip install -r requirements.txt

- name: Lint
working-directory: backend
run: |
flake8 apps config --max-line-length=120 --exclude=migrations
black --check apps config
- name: Lint
working-directory: backend
run: |
flake8 apps config --max-line-length=120 --exclude=migrations --ignore=F401,F811,E231,E271,E302,W292,W293
black --check apps config

- name: Run migrations
working-directory: backend
Expand Down
4 changes: 2 additions & 2 deletions backend/apps/accounts/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from drf_spectacular.utils import extend_schema
from rest_framework import generics, status, viewsets
from rest_framework.decorators import action
# from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.throttling import AnonRateThrottle
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
# from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

from apps.common.permissions import IsOwnerOrAdmin

Expand Down
2 changes: 1 addition & 1 deletion backend/apps/analytics/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from apps.accounts.models import User
from apps.orders.models import Order, OrderItem, OrderStatus
from apps.payments.models import Payment, PaymentStatus
from apps.products.models import Category, Product
from apps.products.models import Product


class AnalyticsService:
Expand Down
12 changes: 9 additions & 3 deletions backend/apps/products/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ class Meta:
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = ['id', 'image', 'alt_text', 'is_primary', 'order']

fields = [
'id',
'product',
'image',
'alt_text',
'is_primary',
'order',
]

class ProductListSerializer(serializers.ModelSerializer):
effective_price = serializers.DecimalField(max_digits=10, decimal_places=2, read_only=True)
Expand Down Expand Up @@ -62,7 +68,7 @@ class ProductCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
'name', 'description', 'price', 'discount_price', 'stock',
'id','name', 'description', 'price', 'discount_price', 'stock',
'sku', 'brand', 'category', 'is_featured', 'is_active',
]

Expand Down
12 changes: 10 additions & 2 deletions backend/apps/products/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
from rest_framework.routers import DefaultRouter

from .views import CategoryViewSet, ProductViewSet, ReviewViewSet, WishlistViewSet

from .views import (
CategoryViewSet,
ProductViewSet,
ReviewViewSet,
WishlistViewSet,
ProductImageViewSet,
)
router = DefaultRouter()
router.register('categories', CategoryViewSet, basename='category')
router.register('', ProductViewSet, basename='product')
router.register('reviews', ReviewViewSet, basename='review')
router.register('wishlist', WishlistViewSet, basename='wishlist')

router.register('images',ProductImageViewSet,basename='product-image')
router.register('', ProductViewSet, basename='product')
urlpatterns = [
path('', include(router.urls)),

]
19 changes: 18 additions & 1 deletion backend/apps/products/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from rest_framework.parsers import MultiPartParser,FormParser
from apps.common.permissions import IsAdmin, IsAdminOrReadOnly

from .filters import ProductFilter
Expand All @@ -21,6 +21,8 @@
)
from .services import CategoryService, ProductService, ReviewService, WishlistService

from .models import ProductImage
from .serializers import ProductImageSerializer

class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.filter(is_active=True)
Expand Down Expand Up @@ -172,3 +174,18 @@ def add(self, request, product_id=None):
def remove(self, request, product_id=None):
WishlistService.remove_from_wishlist(request.user, product_id)
return Response({'success': True, 'message': 'Removed from wishlist.'})

class ProductImageViewSet(viewsets.ModelViewSet):
queryset = ProductImage.objects.all()
serializer_class = ProductImageSerializer
permission_classes = [IsAdmin]

parser_classes = [MultiPartParser, FormParser]

@extend_schema(tags=['Product Images'])
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)

@extend_schema(tags=['Product Images'])
def destroy(self, request, *args, **kwargs):
return super().destroy(request, *args, **kwargs)
7 changes: 7 additions & 0 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const Orders = lazy(() => import('./pages/Orders'))
const OrderDetail = lazy(() => import('./pages/OrderDetail'))
const Profile = lazy(() => import('./pages/Profile'))
const AdminDashboard = lazy(() => import('./pages/AdminDashboard'))
const AddProducts = lazy(() => import('./pages/admin/AddProduct'))
const EditProduct = lazy(() => import('./pages/admin/EditProduct'))

function PageLoader() {
return (
Expand All @@ -35,6 +37,8 @@ export default function App() {
const dispatch = useDispatch()
const { darkMode } = useSelector((s) => s.theme)
const { isAuthenticated } = useSelector((s) => s.auth)
const AdminProducts = lazy(() => import('./pages/admin/AdminProducts'))
const AddProduct = lazy(() => import('./pages/admin/AddProduct'))

useEffect(() => {
dispatch(setDarkMode(darkMode))
Expand Down Expand Up @@ -63,6 +67,9 @@ export default function App() {
<Route path="/orders/:id" element={<ProtectedRoute><OrderDetail /></ProtectedRoute>} />
<Route path="/profile" element={<ProtectedRoute><Profile /></ProtectedRoute>} />
<Route path="/admin" element={<ProtectedRoute adminOnly><AdminDashboard /></ProtectedRoute>} />
<Route path="/admin/products" element={<ProtectedRoute adminOnly><AdminProducts /></ProtectedRoute>}/>
<Route path="/admin/products/add" element={<ProtectedRoute adminOnly><AddProducts /></ProtectedRoute>}/>
<Route path="/admin/products/edit/:slug"element={<ProtectedRoute adminOnly><EditProduct /></ProtectedRoute>}/>
</Routes>
</Suspense>
</Layout>
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/components/products/ProductCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ 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={product.primary_image} alt={product.name} className="w-full h-full object-cover" loading="lazy" />
<img src={`http://localhost:8000${product.primary_image}`}
alt={product.name}
className="w-full h-full object-cover"
loading="lazy"
/>


) : (
<div className="w-full h-full flex items-center justify-center text-gray-400">No Image</div>
)}
Expand Down
Loading
Loading