added usersetting for generative banner

This commit is contained in:
nvrl committed 2026-02-18 17:31:48 +01:00
1 parent c779ed36d0
commit a822eb95c9
8 files changed
+168 -18

No files matched your search

+4 -2
View File
@@ -60,7 +60,9 @@ const DynamicBanner: React.FC<DynamicBannerProps> = (props) => {
useEffect(() => {
const fetchActiveBanner = async () => {
try {
const res = await api.get('banners/active/');
const res = await api.get('banners/active/', {
params: { username: props.username }
});
setBanner(res.data);
} catch (err) {
console.error('Failed to fetch active banner:', err);
@@ -70,7 +72,7 @@ const DynamicBanner: React.FC<DynamicBannerProps> = (props) => {
};
fetchActiveBanner();
}, []);
}, [props.username]);
if (loading) return null;
+4
View File
@@ -52,6 +52,8 @@ const resources = {
"appearanceTitle": "Appearance & Language",
"themeLabel": "Theme",
"langLabel": "Language",
"bannerLabel": "Interactive Banner",
"bannerDefault": "Site Default",
"avatarTitle": "Profile Picture",
"avatarButton": "Upload New Avatar",
"avatarHint": "Max size: 2MB. JPG, PNG or WEBP.",
@@ -248,6 +250,8 @@ const resources = {
"appearanceTitle": "Erscheinungsbild & Sprache",
"themeLabel": "Design",
"langLabel": "Sprache",
"bannerLabel": "Interaktiver Banner",
"bannerDefault": "Seiten-Standard",
"avatarTitle": "Profilbild",
"avatarButton": "Neues Bild hochladen",
"avatarHint": "Max. Größe: 2MB. JPG, PNG oder WEBP.",
+23 -3
View File
@@ -49,6 +49,7 @@ interface Banner {
slug: string;
description: string;
is_active: boolean;
is_enabled: boolean;
settings: any;
schema: any[];
}
@@ -103,6 +104,15 @@ const AdminDashboard: React.FC = () => {
}
};
const handleToggleEnabled = async (id: number) => {
try {
await api.post(`banners/${id}/toggle_enabled/`);
fetchData();
} catch (err: any) {
alert(err.response?.data?.error || 'Failed to toggle visibility');
}
};
useEffect(() => {
fetchData();
}, []);
@@ -293,7 +303,10 @@ const AdminDashboard: React.FC = () => {
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{banner.name} (Slug: {banner.slug})</Typography>
{banner.is_active && <Chip label="Active" color="success" size="small" />}
<Stack direction="row" spacing={1}>
{!banner.is_enabled && <Chip label="Hidden from Users" color="warning" size="small" variant="outlined" />}
{banner.is_active && <Chip label="Global Default" color="success" size="small" />}
</Stack>
</Box>
<Typography variant="body2" sx={{ mb: 2 }}>{banner.description}</Typography>
@@ -302,7 +315,7 @@ const AdminDashboard: React.FC = () => {
<pre style={{ margin: 0, fontSize: '0.8rem' }}>{JSON.stringify(banner.settings, null, 2)}</pre>
</Box>
<Stack direction="row" spacing={2}>
<Stack direction="row" spacing={2} flexWrap="wrap" useFlexGap>
<Button
variant="outlined" size="small"
onClick={() => {
@@ -312,12 +325,19 @@ const AdminDashboard: React.FC = () => {
>
Edit Settings
</Button>
<Button
variant="outlined" size="small" color={banner.is_enabled ? "warning" : "success"}
onClick={() => handleToggleEnabled(banner.id)}
disabled={banner.is_active}
>
{banner.is_enabled ? "Disable for Users" : "Enable for Users"}
</Button>
{!banner.is_active && (
<Button
variant="contained" size="small"
onClick={() => handleActivateBanner(banner.id)}
>
Set as Active
Set as Default
</Button>
)}
</Stack>
+41 -7
View File
@@ -44,6 +44,8 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
const [deletionCode, setDeletionCode] = useState('');
const [isDeleting, setIsDeleting] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error', text: string } | null>(null);
const [banners, setBanners] = useState<any[]>([]);
const [selectedBannerId, setSelectedBannerId] = useState<number | string>("");
const handleGoBack = () => {
if (window.history.length > 1) {
@@ -56,14 +58,24 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
useEffect(() => {
const fetchUser = async () => {
try {
const res = await api.get('users/me/');
setUsername(res.data.username);
setEmail(res.data.email);
setCurrentEmail(res.data.email);
setAvatar(res.data.avatar);
if (res.data.language) {
setLanguage(res.data.language);
const [userRes, bannersRes] = await Promise.all([
api.get('users/me/'),
api.get('banners/')
]);
setUsername(userRes.data.username);
setEmail(userRes.data.email);
setCurrentEmail(userRes.data.email);
setAvatar(userRes.data.avatar);
setSelectedBannerId(userRes.data.selected_banner || "");
if (userRes.data.language) {
setLanguage(userRes.data.language);
}
// Handle both paginated and non-paginated
const bannersData = Array.isArray(bannersRes.data) ? bannersRes.data : (bannersRes.data.results || []);
setBanners(bannersData);
} catch (err) {
console.error(err);
}
@@ -245,6 +257,28 @@ const Settings: React.FC<SettingsProps> = ({ themeName, onThemeChange }) => {
</Select>
</FormControl>
<FormControl fullWidth margin="normal">
<InputLabel>{t('settings.bannerLabel')}</InputLabel>
<Select
value={selectedBannerId}
label={t('settings.bannerLabel')}
onChange={async (e) => {
const newId = e.target.value;
setSelectedBannerId(newId);
try {
await api.patch('users/update_preferences/', { selected_banner: newId });
} catch (err) {
console.error('Failed to update banner preference');
}
}}
>
<MenuItem value="">{t('settings.bannerDefault')}</MenuItem>
{banners.map(b => (
<MenuItem key={b.id} value={b.id}>{b.name}</MenuItem>
))}
</Select>
</FormControl>
<FormControl fullWidth margin="normal">
<InputLabel>{t('settings.langLabel')}</InputLabel>
<Select