Merge branch 'main' into narl.io

This commit is contained in:
nvrl committed 2026-02-19 17:40:21 +01:00
commit 7aa3a5b434
4 files changed
+38 -7

No files matched your search

+1 -2
View File
@@ -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
+4 -1
View File
@@ -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:
+4 -4
View File
@@ -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),
}
})
@@ -86,6 +86,7 @@ const FlavorDetail: React.FC<FlavorDetailProps> = ({ 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<FlavorDetailProps> = ({ adminMode }) => {
}
};
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
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<FlavorDetailProps> = ({ adminMode }) => {
value={editFlavorData.shop_url}
onChange={(e) => setEditFlavorData({...editFlavorData, shop_url: e.target.value})}
/>
<Box>
<Button variant="outlined" component="label" size="small" fullWidth disabled={uploadingImage}>
{uploadingImage ? 'Uploading...' : 'Upload Custom Image'}
<input type="file" hidden accept="image/*" onChange={handleImageUpload} />
</Button>
</Box>
<FormControlLabel
control={<Switch checked={editFlavorData.is_available} onChange={(e) => setEditFlavorData({...editFlavorData, is_available: e.target.checked})} />}
label="In Stock"