diff --git a/backend/customization/migrations/0007_designsettings_body_font_family_and_more.py b/backend/customization/migrations/0007_designsettings_body_font_family_and_more.py new file mode 100644 index 0000000..b6971d0 --- /dev/null +++ b/backend/customization/migrations/0007_designsettings_body_font_family_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.2.8 on 2025-11-09 01:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('customization', '0006_designsettings_cover_overlay_color_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='designsettings', + name='body_font_family', + field=models.CharField(blank=True, default='', help_text='Шрифт для основного текста', max_length=100), + ), + migrations.AddField( + model_name='designsettings', + name='group_description_text_color', + field=models.CharField(default='#666666', help_text='Цвет текста описаний групп (hex)', max_length=7), + ), + migrations.AddField( + model_name='designsettings', + name='group_overlay_color', + field=models.CharField(default='#000000', help_text='Цвет перекрытия групп (hex)', max_length=7), + ), + migrations.AddField( + model_name='designsettings', + name='group_overlay_enabled', + field=models.BooleanField(default=False, help_text='Включить цветовое перекрытие групп'), + ), + migrations.AddField( + model_name='designsettings', + name='group_overlay_opacity', + field=models.FloatField(default=0.3, help_text='Прозрачность перекрытия групп (0.0 - 1.0)'), + ), + migrations.AddField( + model_name='designsettings', + name='heading_font_family', + field=models.CharField(blank=True, default='', help_text='Шрифт для заголовков', max_length=100), + ), + migrations.AddField( + model_name='designsettings', + name='show_groups_title', + field=models.BooleanField(default=True, help_text='Показывать заголовок "Группы ссылок"'), + ), + ] diff --git a/backend/customization/models.py b/backend/customization/models.py index 0c64c3a..bb58ba8 100644 --- a/backend/customization/models.py +++ b/backend/customization/models.py @@ -102,6 +102,44 @@ class DesignSettings(models.Model): help_text='Прозрачность перекрытия (0.0 - 1.0)' ) + # Новые поля для кастомизации групп + group_overlay_enabled = models.BooleanField( + default=False, + help_text='Включить цветовое перекрытие групп' + ) + group_overlay_color = models.CharField( + max_length=7, + default='#000000', + help_text='Цвет перекрытия групп (hex)' + ) + group_overlay_opacity = models.FloatField( + default=0.3, + help_text='Прозрачность перекрытия групп (0.0 - 1.0)' + ) + show_groups_title = models.BooleanField( + default=True, + help_text='Показывать заголовок "Группы ссылок"' + ) + group_description_text_color = models.CharField( + max_length=7, + default='#666666', + help_text='Цвет текста описаний групп (hex)' + ) + + # Новые поля для шрифтов + body_font_family = models.CharField( + max_length=100, + default='', + blank=True, + help_text='Шрифт для основного текста' + ) + heading_font_family = models.CharField( + max_length=100, + default='', + blank=True, + help_text='Шрифт для заголовков' + ) + updated_at = models.DateTimeField( auto_now=True, help_text='Дата и время последнего изменения' diff --git a/backend/customization/serializers.py b/backend/customization/serializers.py index c832666..a7db70c 100644 --- a/backend/customization/serializers.py +++ b/backend/customization/serializers.py @@ -28,6 +28,13 @@ class DesignSettingsSerializer(serializers.ModelSerializer): 'cover_overlay_enabled', 'cover_overlay_color', 'cover_overlay_opacity', + 'group_overlay_enabled', + 'group_overlay_color', + 'group_overlay_opacity', + 'show_groups_title', + 'group_description_text_color', + 'body_font_family', + 'heading_font_family', 'updated_at' ] read_only_fields = ['id', 'updated_at', 'background_image_url'] @@ -197,6 +204,54 @@ class DesignSettingsSerializer(serializers.ModelSerializer): raise serializers.ValidationError('Прозрачность должна быть между 0.0 и 1.0') return value + def validate_group_overlay_color(self, value): + """ + Валидация цвета перекрытия групп + """ + if not value.startswith('#') or len(value) != 7: + raise serializers.ValidationError('Цвет должен быть в формате #RRGGBB') + try: + int(value[1:], 16) + except ValueError: + raise serializers.ValidationError('Некорректный hex цвет') + return value + + def validate_group_overlay_opacity(self, value): + """ + Валидация прозрачности перекрытия групп + """ + if not 0.0 <= value <= 1.0: + raise serializers.ValidationError('Прозрачность должна быть между 0.0 и 1.0') + return value + + def validate_group_description_text_color(self, value): + """ + Валидация цвета описаний групп + """ + if not value.startswith('#') or len(value) != 7: + raise serializers.ValidationError('Цвет должен быть в формате #RRGGBB') + try: + int(value[1:], 16) + except ValueError: + raise serializers.ValidationError('Некорректный hex цвет') + return value + + def validate_body_font_family(self, value): + """ + Валидация шрифта основного текста + """ + if value and len(value) > 100: + raise serializers.ValidationError('Название шрифта слишком длинное') + return value + + def validate_heading_font_family(self, value): + """ + Валидация шрифта заголовков + """ + if value and len(value) > 100: + raise serializers.ValidationError('Название шрифта слишком длинное') + return value + class PublicDesignSettingsSerializer(serializers.ModelSerializer): """ diff --git a/frontend/linktree-frontend/src/app/(protected)/dashboard/DashboardClient.tsx b/frontend/linktree-frontend/src/app/(protected)/dashboard/DashboardClient.tsx index 8f921b8..0b81ef4 100644 --- a/frontend/linktree-frontend/src/app/(protected)/dashboard/DashboardClient.tsx +++ b/frontend/linktree-frontend/src/app/(protected)/dashboard/DashboardClient.tsx @@ -61,6 +61,14 @@ interface DesignSettings { cover_overlay_enabled?: boolean cover_overlay_color?: string cover_overlay_opacity?: number + // Новые опции кастомизации + group_overlay_enabled?: boolean + group_overlay_color?: string + group_overlay_opacity?: number + show_groups_title?: boolean + group_description_text_color?: string + body_font_family?: string + heading_font_family?: string } export default function DashboardClient() { diff --git a/frontend/linktree-frontend/src/app/components/CustomizationPanel.tsx b/frontend/linktree-frontend/src/app/components/CustomizationPanel.tsx index 739cd69..2d04848 100644 --- a/frontend/linktree-frontend/src/app/components/CustomizationPanel.tsx +++ b/frontend/linktree-frontend/src/app/components/CustomizationPanel.tsx @@ -20,6 +20,14 @@ interface DesignSettings { cover_overlay_enabled?: boolean cover_overlay_color?: string cover_overlay_opacity?: number + // Новые опции кастомизации + group_overlay_enabled?: boolean + group_overlay_color?: string + group_overlay_opacity?: number + show_groups_title?: boolean + group_description_text_color?: string + body_font_family?: string + heading_font_family?: string } interface CustomizationPanelProps { @@ -96,6 +104,13 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate }: Custom formData.append('cover_overlay_enabled', (settings.cover_overlay_enabled || false).toString()) formData.append('cover_overlay_color', settings.cover_overlay_color || '#000000') formData.append('cover_overlay_opacity', (settings.cover_overlay_opacity || 0.3).toString()) + formData.append('group_overlay_enabled', (settings.group_overlay_enabled || false).toString()) + formData.append('group_overlay_color', settings.group_overlay_color || '#000000') + formData.append('group_overlay_opacity', (settings.group_overlay_opacity || 0.3).toString()) + formData.append('show_groups_title', (settings.show_groups_title !== false).toString()) + formData.append('group_description_text_color', settings.group_description_text_color || '#666666') + formData.append('body_font_family', settings.body_font_family || 'sans-serif') + formData.append('heading_font_family', settings.heading_font_family || 'sans-serif') formData.append('background_image', backgroundImageFile) const response = await fetch(`${API}/api/customization/settings/`, { @@ -132,7 +147,14 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate }: Custom link_text_color: settings.link_text_color || '#666666', cover_overlay_enabled: settings.cover_overlay_enabled || false, cover_overlay_color: settings.cover_overlay_color || '#000000', - cover_overlay_opacity: settings.cover_overlay_opacity || 0.3 + cover_overlay_opacity: settings.cover_overlay_opacity || 0.3, + group_overlay_enabled: settings.group_overlay_enabled || false, + group_overlay_color: settings.group_overlay_color || '#000000', + group_overlay_opacity: settings.group_overlay_opacity || 0.3, + show_groups_title: settings.show_groups_title !== false, + group_description_text_color: settings.group_description_text_color || '#666666', + body_font_family: settings.body_font_family || 'sans-serif', + heading_font_family: settings.heading_font_family || 'sans-serif' } const response = await fetch(`${API}/api/customization/settings/`, { @@ -505,6 +527,127 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate }: Custom + + {/* Новые настройки */} +
+
+ handleChange('show_groups_title', e.target.checked)} + /> + +
+
+ +
+ +
+ handleChange('group_description_text_color', e.target.value)} + /> + handleChange('group_description_text_color', e.target.value)} + /> +
+
+ + {/* Перекрытие групп цветом */} +
+
+
+
Цветовое перекрытие групп
+
+
+
+ handleChange('group_overlay_enabled', e.target.checked)} + /> + +
+ + {settings.group_overlay_enabled && ( + <> +
+
+ +
+ handleChange('group_overlay_color', e.target.value)} + title="Выберите цвет перекрытия" + /> + handleChange('group_overlay_color', e.target.value)} + placeholder="#000000" + title="Hex код цвета" + /> +
+
+
+ + handleChange('group_overlay_opacity', parseFloat(e.target.value))} + title="Настройка прозрачности перекрытия" + /> +
+
+ + {/* Preview */} +
+ +
+
+ Пример группы +
+
+
+
+ + )} +
+
+
+
@@ -520,8 +663,11 @@ export function CustomizationPanel({ isOpen, onClose, onSettingsUpdate }: Custom {activeTab === 'advanced' && (
-
- +
+
Настройки шрифтов
+
+
+
+
+ + +
+
+ + +
+ +
+
+
Дополнительные настройки
+
+