mirror of
https://github.com/narrrl/holy-flavor-rater.git
synced 2026-11-03 16:41:27 +01:00
Merge branch 'main' into narl.io
This commit is contained in:
commit
c39e9ba77d
4 files changed
+40
-54
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -69,7 +69,7 @@ ROOT_URLCONF = 'holy_backend.urls'
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': [os.path.join(BASE_DIR, '..', 'frontend', 'dist')],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
@@ -142,6 +142,7 @@ USE_TZ = True
|
|||||||
|
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
|
STATICFILES_DIRS = [os.path.join(BASE_DIR, '..', 'frontend', 'dist')]
|
||||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
|
||||||
WHITENOISE_INDEX_PAGE = True
|
WHITENOISE_INDEX_PAGE = True
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,14 @@ from rest_framework.authtoken.views import obtain_auth_token
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.views.static import serve
|
from django.views.static import serve
|
||||||
from django.views.static import serve
|
from django.views.generic.base import TemplateView
|
||||||
from django.conf import settings
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# Clean the admin path
|
# Clean the admin path
|
||||||
admin_url = os.environ.get('ADMIN_URL', 'admin').strip('/')
|
admin_url = os.environ.get('ADMIN_URL', 'admin').strip('/')
|
||||||
|
|
||||||
def serve_index(request, resource=None):
|
# Catch-all view for React
|
||||||
return serve(request, 'index.html', document_root=settings.STATIC_ROOT)
|
index_view = TemplateView.as_view(template_name="index.html")
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Robots.txt
|
# Robots.txt
|
||||||
@@ -30,6 +29,6 @@ urlpatterns = [
|
|||||||
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
|
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
|
||||||
|
|
||||||
# Catch-all for React Frontend
|
# Catch-all for React Frontend
|
||||||
path('', serve_index, name='index'),
|
path('', index_view, name='index'),
|
||||||
path('<path:resource>', serve_index),
|
path('<path:resource>', index_view),
|
||||||
]
|
]
|
||||||
@@ -11,7 +11,6 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|||||||
import SettingsIcon from '@mui/icons-material/Settings';
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
import PeopleIcon from '@mui/icons-material/People';
|
import PeopleIcon from '@mui/icons-material/People';
|
||||||
import WallpaperIcon from '@mui/icons-material/Wallpaper';
|
import WallpaperIcon from '@mui/icons-material/Wallpaper';
|
||||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import api from '../api';
|
import api from '../api';
|
||||||
@@ -273,7 +272,7 @@ const AdminDashboard: React.FC = () => {
|
|||||||
|
|
||||||
{currentTab === 2 && (
|
{currentTab === 2 && (
|
||||||
<Box>
|
<Box>
|
||||||
<Typography variant="h5" sx={{ fontWeight: 'bold', mb: 3 }}>{t('admin.banners')}</Typography>
|
<Typography variant="h5" sx={{ fontWeight: 'bold', mb: 3 }}>{t('admin.banners')} ({banners.length})</Typography>
|
||||||
|
|
||||||
{banners.length === 0 ? (
|
{banners.length === 0 ? (
|
||||||
<Card variant="outlined" sx={{ p: 4, textAlign: 'center', borderRadius: 3 }}>
|
<Card variant="outlined" sx={{ p: 4, textAlign: 'center', borderRadius: 3 }}>
|
||||||
@@ -287,57 +286,44 @@ const AdminDashboard: React.FC = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Grid container spacing={3}>
|
<Stack spacing={3}>
|
||||||
{banners.map((banner) => (
|
{banners.map((banner) => (
|
||||||
<Grid size={{ xs: 12, md: 4 }} key={banner.id}>
|
<Card key={banner.id} sx={{ borderRadius: 3, border: '1px solid', borderColor: 'divider' }}>
|
||||||
<Card sx={{ borderRadius: 3, position: 'relative', height: '100%', display: 'flex', flexDirection: 'column' }}>
|
<CardContent>
|
||||||
{banner.is_active && (
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
||||||
<Chip
|
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{banner.name} (Slug: {banner.slug})</Typography>
|
||||||
icon={<CheckCircleIcon />}
|
{banner.is_active && <Chip label="Active" color="success" size="small" />}
|
||||||
label="Active"
|
</Box>
|
||||||
color="success"
|
|
||||||
size="small"
|
<Typography variant="body2" sx={{ mb: 2 }}>{banner.description}</Typography>
|
||||||
sx={{ position: 'absolute', top: 12, right: 12, zIndex: 2 }}
|
|
||||||
/>
|
<Box sx={{ p: 2, bgcolor: 'action.hover', borderRadius: 2, mb: 2 }}>
|
||||||
)}
|
<pre style={{ margin: 0, fontSize: '0.8rem' }}>{JSON.stringify(banner.settings, null, 2)}</pre>
|
||||||
<CardContent sx={{ flexGrow: 1 }}>
|
</Box>
|
||||||
<Typography variant="h6" sx={{ fontWeight: 'bold', mb: 1 }}>{banner.name}</Typography>
|
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>{banner.description}</Typography>
|
<Stack direction="row" spacing={2}>
|
||||||
|
<Button
|
||||||
<Box sx={{ mt: 2, p: 2, bgcolor: 'action.hover', borderRadius: 2 }}>
|
variant="outlined" size="small"
|
||||||
<Typography variant="caption" sx={{ fontWeight: 'bold', display: 'block', mb: 1 }}>Settings:</Typography>
|
onClick={() => {
|
||||||
<pre style={{ margin: 0, fontSize: '0.7rem', overflow: 'auto' }}>
|
setSelectedBanner(banner);
|
||||||
{JSON.stringify(banner.settings, null, 2)}
|
setIsSettingsDialogOpen(true);
|
||||||
</pre>
|
}}
|
||||||
</Box>
|
>
|
||||||
</CardContent>
|
Edit Settings
|
||||||
<Box sx={{ p: 2, pt: 0 }}>
|
</Button>
|
||||||
<Stack direction="row" spacing={1}>
|
{!banner.is_active && (
|
||||||
<Button
|
<Button
|
||||||
fullWidth
|
variant="contained" size="small"
|
||||||
variant="outlined"
|
|
||||||
startIcon={<EditIcon />}
|
|
||||||
onClick={() => {
|
|
||||||
setSelectedBanner(banner);
|
|
||||||
setIsSettingsDialogOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Edit Settings
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
variant={banner.is_active ? "outlined" : "contained"}
|
|
||||||
disabled={banner.is_active}
|
|
||||||
onClick={() => handleActivateBanner(banner.id)}
|
onClick={() => handleActivateBanner(banner.id)}
|
||||||
>
|
>
|
||||||
{banner.is_active ? "Currently Active" : "Set as Active"}
|
Set as Active
|
||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
)}
|
||||||
</Box>
|
</Stack>
|
||||||
</Card>
|
</CardContent>
|
||||||
</Grid>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in new issue
Block a user