diff --git a/backend/api/management/commands/worker.py b/backend/api/management/commands/worker.py index 8491c00..1b9cc03 100644 --- a/backend/api/management/commands/worker.py +++ b/backend/api/management/commands/worker.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from django.core.management.base import BaseCommand from django.core.management import call_command from django.utils import timezone +from django.db import models from api.models import Job from io import StringIO @@ -70,5 +71,3 @@ class Command(BaseCommand): job.error_message = error job.save() self.stdout.write(self.style.SUCCESS(f"Job finished: {job.status}")) - -from django.db import models # Needed for the Q object inside handle diff --git a/backend/api/serializers.py b/backend/api/serializers.py index 91affe4..14193b2 100644 --- a/backend/api/serializers.py +++ b/backend/api/serializers.py @@ -75,7 +75,10 @@ class FlavorSerializer(serializers.ModelSerializer): class Meta: model = Flavor - fields = ['id', 'name', 'category', 'category_name', 'category_slug', 'description', 'average_rating', 'followed_average_rating', 'user_rating', 'ratings', 'image_url', 'is_available', 'is_legacy', 'shop_url'] + fields = ['id', 'name', 'category', 'category_name', 'category_slug', 'description', 'average_rating', 'followed_average_rating', 'user_rating', 'ratings', 'image_url', 'image', 'is_available', 'is_legacy', 'shop_url'] + extra_kwargs = { + 'image': {'write_only': True} + } def get_image_url(self, obj): if obj.image: diff --git a/backend/api/views.py b/backend/api/views.py index c5491e6..67743c0 100644 --- a/backend/api/views.py +++ b/backend/api/views.py @@ -8,12 +8,12 @@ from django_filters.rest_framework import DjangoFilterBackend from django_ratelimit.decorators import ratelimit from django.utils.decorators import method_decorator from rest_framework.pagination import PageNumberPagination -from .models import User, Flavor, Category, Rating, Reply, Notification, Ticket, TicketMessage, UserIP, ProfileComment, Banner +from .models import User, Flavor, Category, Rating, Reply, Notification, Ticket, TicketMessage, UserIP, ProfileComment, Banner, SystemConfig, Job from .serializers import ( UserSerializer, FlavorSerializer, CategorySerializer, RatingSerializer, ReplySerializer, NotificationSerializer, TicketSerializer, TicketMessageSerializer, AdminUserListSerializer, AdminUserDetailSerializer, - ProfileCommentSerializer, BannerSerializer + ProfileCommentSerializer, BannerSerializer, SystemConfigSerializer, JobSerializer ) def log_user_ip(user, request): @@ -870,8 +870,8 @@ class AdminViewSet(viewsets.ViewSet): 'debug': settings.DEBUG, 'allowed_hosts': settings.ALLOWED_HOSTS, 'frontend_url': getattr(settings, 'FRONTEND_URL', 'Not set'), - 'media_root': settings.MEDIA_ROOT, - 'static_root': settings.STATIC_ROOT, + 'media_root': str(settings.MEDIA_ROOT), + 'static_root': str(settings.STATIC_ROOT), } }) diff --git a/frontend/src/pages/flavors/FlavorDetail.tsx b/frontend/src/pages/flavors/FlavorDetail.tsx index 44b7160..71f4bd3 100644 --- a/frontend/src/pages/flavors/FlavorDetail.tsx +++ b/frontend/src/pages/flavors/FlavorDetail.tsx @@ -86,6 +86,7 @@ const FlavorDetail: React.FC = ({ adminMode }) => { // Product Edit state (Admin) const [isAdminEditing, setIsAdminEditing] = useState(false); + const [uploadingImage, setUploadingImage] = useState(false); const [editFlavorData, setEditFlavorData] = useState({ name: '', description: '', @@ -148,6 +149,26 @@ const FlavorDetail: React.FC = ({ adminMode }) => { } }; + const handleImageUpload = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + const formData = new FormData(); + formData.append('image', file); + + try { + setUploadingImage(true); + await api.patch(`flavors/${id}/`, formData, { + headers: { 'Content-Type': 'multipart/form-data' } + }); + fetchFlavor(); + } catch (err) { + alert('Image upload failed'); + } finally { + setUploadingImage(false); + } + }; + const handleRatingSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!newScore) { @@ -337,6 +358,14 @@ const FlavorDetail: React.FC = ({ adminMode }) => { value={editFlavorData.shop_url} onChange={(e) => setEditFlavorData({...editFlavorData, shop_url: e.target.value})} /> + + + + + setEditFlavorData({...editFlavorData, is_available: e.target.checked})} />} label="In Stock"