updates
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
||||
# Add parent directory to path to import from src
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from sqlalchemy.orm import Session
|
||||
from src.shared.config.database import SessionLocal
|
||||
from src.models.page_content import PageContent, PageType
|
||||
from src.content.models.page_content import PageContent, PageType
|
||||
|
||||
def seed_homepage_content(db: Session):
|
||||
existing = db.query(PageContent).filter(PageContent.page_type == PageType.HOME).first()
|
||||
@@ -17,9 +18,10 @@ def seed_homepage_content(db: Session):
|
||||
partners = [{'name': 'Luxury Travel Group', 'logo': 'https://images.unsplash.com/photo-1599305445671-ac291c95aaa9?w=200', 'link': '#'}, {'name': 'Premium Airlines', 'logo': 'https://images.unsplash.com/photo-1436491865332-7a61a109cc05?w=200', 'link': '#'}, {'name': 'Exclusive Events', 'logo': 'https://images.unsplash.com/photo-1511578314322-379afb476865?w=200', 'link': '#'}, {'name': 'Fine Dining Network', 'logo': 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=200', 'link': '#'}]
|
||||
stats = [{'icon': 'Users', 'number': '50,000+', 'label': 'Happy Guests'}, {'icon': 'Award', 'number': '25+', 'label': 'Awards Won'}, {'icon': 'Star', 'number': '4.9', 'label': 'Average Rating'}, {'icon': 'Globe', 'number': '100+', 'label': 'Countries Served'}]
|
||||
amenities = [{'icon': 'Wifi', 'title': 'High-Speed WiFi', 'description': 'Complimentary high-speed internet throughout the property', 'image': ''}, {'icon': 'Coffee', 'title': '24/7 Room Service', 'description': 'Round-the-clock dining and beverage service', 'image': ''}, {'icon': 'Car', 'title': 'Valet Parking', 'description': 'Complimentary valet parking for all guests', 'image': ''}, {'icon': 'Plane', 'title': 'Airport Transfer', 'description': 'Luxury airport transfer service available', 'image': ''}]
|
||||
features = [{'icon': 'Shield', 'title': 'Secure & Safe', 'description': '24/7 security and state-of-the-art safety systems'}, {'icon': 'Wifi', 'title': 'Free WiFi', 'description': 'High-speed internet access throughout the property'}, {'icon': 'Coffee', 'title': 'Room Service', 'description': '24/7 room service available for your convenience'}, {'icon': 'Car', 'title': 'Parking', 'description': 'Complimentary valet parking for all guests'}, {'icon': 'UtensilsCrossed', 'title': 'Fine Dining', 'description': 'World-class restaurants and dining experiences'}, {'icon': 'Dumbbell', 'title': 'Fitness Center', 'description': 'State-of-the-art fitness facilities'}]
|
||||
testimonials = [{'name': 'Robert Martinez', 'role': 'CEO, Tech Corp', 'image': 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=200', 'rating': 5, 'comment': 'Exceptional service and attention to detail. The staff went above and beyond to make our stay memorable.'}, {'name': 'Lisa Anderson', 'role': 'Travel Blogger', 'image': 'https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?w=200', 'rating': 5, 'comment': "The most luxurious hotel experience I've ever had. Every detail was perfect."}, {'name': 'David Thompson', 'role': 'Investment Banker', 'image': 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=200', 'rating': 5, 'comment': 'Outstanding facilities and impeccable service. Highly recommend for business travelers.'}]
|
||||
gallery_images = ['https://images.unsplash.com/photo-1564501049412-61c2a3083791?w=800', 'https://images.unsplash.com/photo-1590490360182-c33d57733427?w=800', 'https://images.unsplash.com/photo-1551882547-ff40c63fe5fa?w=800', 'https://images.unsplash.com/photo-1571896349842-33c89424de2d?w=800', 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=800', 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=800']
|
||||
homepage_data = {'page_type': PageType.HOME, 'title': 'Luxury Hotel - Experience Unparalleled Elegance', 'subtitle': 'Where timeless luxury meets modern sophistication', 'description': 'Discover a world of refined elegance and exceptional service', 'hero_title': 'Welcome to Luxury', 'hero_subtitle': 'Experience the pinnacle of hospitality', 'hero_image': 'https://images.unsplash.com/photo-1564501049412-61c2a3083791?w=1200', 'luxury_section_title': 'Experience Unparalleled Luxury', 'luxury_section_subtitle': 'Where elegance meets comfort in every detail', 'luxury_section_image': 'https://images.unsplash.com/photo-1571896349842-33c89424de2d?w=1200', 'luxury_features': json.dumps(luxury_features), 'luxury_gallery_section_title': 'Our Luxury Gallery', 'luxury_gallery_section_subtitle': 'A glimpse into our world of elegance', 'luxury_gallery': json.dumps(luxury_gallery), 'luxury_testimonials_section_title': 'What Our Guests Say', 'luxury_testimonials_section_subtitle': 'Testimonials from our valued guests', 'luxury_testimonials': json.dumps(luxury_testimonials), 'luxury_services_section_title': 'Premium Services', 'luxury_services_section_subtitle': 'Indulge in our world-class amenities', 'luxury_services': json.dumps(luxury_services), 'luxury_experiences_section_title': 'Exclusive Experiences', 'luxury_experiences_section_subtitle': 'Create unforgettable memories', 'luxury_experiences': json.dumps(luxury_experiences), 'awards_section_title': 'Awards & Recognition', 'awards_section_subtitle': 'Recognized for excellence worldwide', 'awards': json.dumps(awards), 'partners_section_title': 'Our Partners', 'partners_section_subtitle': 'Trusted by leading brands', 'partners': json.dumps(partners), 'amenities_section_title': 'Premium Amenities', 'amenities_section_subtitle': 'Everything you need for a perfect stay', 'amenities': json.dumps(amenities), 'testimonials_section_title': 'Guest Reviews', 'testimonials_section_subtitle': 'Hear from our satisfied guests', 'testimonials': json.dumps(testimonials), 'gallery_section_title': 'Photo Gallery', 'gallery_section_subtitle': 'Explore our beautiful spaces', 'gallery_images': json.dumps(gallery_images), 'about_preview_title': 'About Our Luxury Hotel', 'about_preview_subtitle': 'A legacy of excellence', 'about_preview_content': 'Discover a world of refined elegance and exceptional service. Our hotel combines timeless luxury with modern amenities to create an unforgettable experience. With over 50,000 satisfied guests and numerous awards, we continue to set the standard for luxury hospitality.', 'about_preview_image': 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=800', 'stats': json.dumps(stats), 'cta_title': 'Ready to Experience Luxury?', 'cta_subtitle': 'Book your stay today and discover the difference', 'cta_button_text': 'Book Now', 'cta_button_link': '/rooms', 'cta_image': 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=1200', 'is_active': True}
|
||||
homepage_data = {'page_type': PageType.HOME, 'title': 'Luxury Hotel - Experience Unparalleled Elegance', 'subtitle': 'Where timeless luxury meets modern sophistication', 'description': 'Discover a world of refined elegance and exceptional service', 'hero_title': 'Welcome to Luxury', 'hero_subtitle': 'Experience the pinnacle of hospitality', 'hero_image': 'https://images.unsplash.com/photo-1564501049412-61c2a3083791?w=1200', 'features': json.dumps(features), 'luxury_section_title': 'Experience Unparalleled Luxury', 'luxury_section_subtitle': 'Where elegance meets comfort in every detail', 'luxury_section_image': 'https://images.unsplash.com/photo-1571896349842-33c89424de2d?w=1200', 'luxury_features': json.dumps(luxury_features), 'luxury_gallery_section_title': 'Our Luxury Gallery', 'luxury_gallery_section_subtitle': 'A glimpse into our world of elegance', 'luxury_gallery': json.dumps(luxury_gallery), 'luxury_testimonials_section_title': 'What Our Guests Say', 'luxury_testimonials_section_subtitle': 'Testimonials from our valued guests', 'luxury_testimonials': json.dumps(luxury_testimonials), 'luxury_services_section_title': 'Premium Services', 'luxury_services_section_subtitle': 'Indulge in our world-class amenities', 'luxury_services': json.dumps(luxury_services), 'luxury_experiences_section_title': 'Exclusive Experiences', 'luxury_experiences_section_subtitle': 'Create unforgettable memories', 'luxury_experiences': json.dumps(luxury_experiences), 'awards_section_title': 'Awards & Recognition', 'awards_section_subtitle': 'Recognized for excellence worldwide', 'awards': json.dumps(awards), 'partners_section_title': 'Our Partners', 'partners_section_subtitle': 'Trusted by leading brands', 'partners': json.dumps(partners), 'amenities_section_title': 'Premium Amenities', 'amenities_section_subtitle': 'Everything you need for a perfect stay', 'amenities': json.dumps(amenities), 'testimonials_section_title': 'Guest Reviews', 'testimonials_section_subtitle': 'Hear from our satisfied guests', 'testimonials': json.dumps(testimonials), 'gallery_section_title': 'Photo Gallery', 'gallery_section_subtitle': 'Explore our beautiful spaces', 'gallery_images': json.dumps(gallery_images), 'about_preview_title': 'About Our Luxury Hotel', 'about_preview_subtitle': 'A legacy of excellence', 'about_preview_content': 'Discover a world of refined elegance and exceptional service. Our hotel combines timeless luxury with modern amenities to create an unforgettable experience. With over 50,000 satisfied guests and numerous awards, we continue to set the standard for luxury hospitality.', 'about_preview_image': 'https://images.unsplash.com/photo-1582719478250-c89cae4dc85b?w=800', 'stats': json.dumps(stats), 'cta_title': 'Ready to Experience Luxury?', 'cta_subtitle': 'Book your stay today and discover the difference', 'cta_button_text': 'Book Now', 'cta_button_link': '/rooms', 'cta_image': 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=1200', 'is_active': True}
|
||||
if existing:
|
||||
for key, value in homepage_data.items():
|
||||
if key != 'page_type':
|
||||
|
||||
Binary file not shown.
@@ -8,7 +8,93 @@ logger = get_logger(__name__)
|
||||
router = APIRouter(prefix='/home', tags=['home'])
|
||||
|
||||
def serialize_page_content(content: PageContent) -> dict:
|
||||
return {'id': content.id, 'page_type': content.page_type.value, 'title': content.title, 'subtitle': content.subtitle, 'description': content.description, 'content': content.content, 'meta_title': content.meta_title, 'meta_description': content.meta_description, 'meta_keywords': content.meta_keywords, 'og_title': content.og_title, 'og_description': content.og_description, 'og_image': content.og_image, 'canonical_url': content.canonical_url, 'hero_title': content.hero_title, 'hero_subtitle': content.hero_subtitle, 'hero_image': content.hero_image, 'amenities_section_title': content.amenities_section_title, 'amenities_section_subtitle': content.amenities_section_subtitle, 'amenities': json.loads(content.amenities) if content.amenities else None, 'testimonials_section_title': content.testimonials_section_title, 'testimonials_section_subtitle': content.testimonials_section_subtitle, 'testimonials': json.loads(content.testimonials) if content.testimonials else None, 'gallery_section_title': content.gallery_section_title, 'gallery_section_subtitle': content.gallery_section_subtitle, 'gallery_images': json.loads(content.gallery_images) if content.gallery_images else None, 'luxury_section_title': content.luxury_section_title, 'luxury_section_subtitle': content.luxury_section_subtitle, 'luxury_section_image': content.luxury_section_image, 'luxury_features': json.loads(content.luxury_features) if content.luxury_features else None, 'luxury_gallery_section_title': content.luxury_gallery_section_title, 'luxury_gallery_section_subtitle': content.luxury_gallery_section_subtitle, 'luxury_gallery': json.loads(content.luxury_gallery) if content.luxury_gallery else None, 'luxury_testimonials_section_title': content.luxury_testimonials_section_title, 'luxury_testimonials_section_subtitle': content.luxury_testimonials_section_subtitle, 'luxury_testimonials': json.loads(content.luxury_testimonials) if content.luxury_testimonials else None, 'about_preview_title': content.about_preview_title, 'about_preview_subtitle': content.about_preview_subtitle, 'about_preview_content': content.about_preview_content, 'about_preview_image': content.about_preview_image, 'stats': json.loads(content.stats) if content.stats else None, 'luxury_services_section_title': content.luxury_services_section_title, 'luxury_services_section_subtitle': content.luxury_services_section_subtitle, 'luxury_services': json.loads(content.luxury_services) if content.luxury_services else None, 'luxury_experiences_section_title': content.luxury_experiences_section_title, 'luxury_experiences_section_subtitle': content.luxury_experiences_section_subtitle, 'luxury_experiences': json.loads(content.luxury_experiences) if content.luxury_experiences else None, 'awards_section_title': content.awards_section_title, 'awards_section_subtitle': content.awards_section_subtitle, 'awards': json.loads(content.awards) if content.awards else None, 'cta_title': content.cta_title, 'cta_subtitle': content.cta_subtitle, 'cta_button_text': content.cta_button_text, 'cta_button_link': content.cta_button_link, 'cta_image': content.cta_image, 'partners_section_title': content.partners_section_title, 'partners_section_subtitle': content.partners_section_subtitle, 'partners': json.loads(content.partners) if content.partners else None, 'is_active': content.is_active, 'created_at': content.created_at.isoformat() if content.created_at else None, 'updated_at': content.updated_at.isoformat() if content.updated_at else None}
|
||||
def safe_json_loads(value, default=None):
|
||||
"""Safely parse JSON string, return default if invalid or empty"""
|
||||
if not value:
|
||||
return default
|
||||
try:
|
||||
parsed = json.loads(value)
|
||||
return parsed if parsed else default
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
return default
|
||||
|
||||
return {
|
||||
'id': content.id,
|
||||
'page_type': content.page_type.value,
|
||||
'title': content.title,
|
||||
'subtitle': content.subtitle,
|
||||
'description': content.description,
|
||||
'content': content.content,
|
||||
'meta_title': content.meta_title,
|
||||
'meta_description': content.meta_description,
|
||||
'meta_keywords': content.meta_keywords,
|
||||
'og_title': content.og_title,
|
||||
'og_description': content.og_description,
|
||||
'og_image': content.og_image,
|
||||
'canonical_url': content.canonical_url,
|
||||
'contact_info': safe_json_loads(content.contact_info, {}),
|
||||
'map_url': content.map_url,
|
||||
'social_links': safe_json_loads(content.social_links, {}),
|
||||
'footer_links': safe_json_loads(content.footer_links, {}),
|
||||
'badges': safe_json_loads(content.badges, []),
|
||||
'copyright_text': content.copyright_text,
|
||||
'hero_title': content.hero_title,
|
||||
'hero_subtitle': content.hero_subtitle,
|
||||
'hero_image': content.hero_image,
|
||||
'story_content': content.story_content,
|
||||
'values': safe_json_loads(content.values, []),
|
||||
'features': safe_json_loads(content.features, []),
|
||||
'about_hero_image': content.about_hero_image,
|
||||
'mission': content.mission,
|
||||
'vision': content.vision,
|
||||
'team': safe_json_loads(content.team, []),
|
||||
'timeline': safe_json_loads(content.timeline, []),
|
||||
'achievements': safe_json_loads(content.achievements, []),
|
||||
'amenities_section_title': content.amenities_section_title,
|
||||
'amenities_section_subtitle': content.amenities_section_subtitle,
|
||||
'amenities': safe_json_loads(content.amenities, []),
|
||||
'testimonials_section_title': content.testimonials_section_title,
|
||||
'testimonials_section_subtitle': content.testimonials_section_subtitle,
|
||||
'testimonials': safe_json_loads(content.testimonials, []),
|
||||
'gallery_section_title': content.gallery_section_title,
|
||||
'gallery_section_subtitle': content.gallery_section_subtitle,
|
||||
'gallery_images': safe_json_loads(content.gallery_images, []),
|
||||
'luxury_section_title': content.luxury_section_title,
|
||||
'luxury_section_subtitle': content.luxury_section_subtitle,
|
||||
'luxury_section_image': content.luxury_section_image,
|
||||
'luxury_features': safe_json_loads(content.luxury_features, []),
|
||||
'luxury_gallery_section_title': content.luxury_gallery_section_title,
|
||||
'luxury_gallery_section_subtitle': content.luxury_gallery_section_subtitle,
|
||||
'luxury_gallery': safe_json_loads(content.luxury_gallery, []),
|
||||
'luxury_testimonials_section_title': content.luxury_testimonials_section_title,
|
||||
'luxury_testimonials_section_subtitle': content.luxury_testimonials_section_subtitle,
|
||||
'luxury_testimonials': safe_json_loads(content.luxury_testimonials, []),
|
||||
'about_preview_title': content.about_preview_title,
|
||||
'about_preview_subtitle': content.about_preview_subtitle,
|
||||
'about_preview_content': content.about_preview_content,
|
||||
'about_preview_image': content.about_preview_image,
|
||||
'stats': safe_json_loads(content.stats, []),
|
||||
'luxury_services_section_title': content.luxury_services_section_title,
|
||||
'luxury_services_section_subtitle': content.luxury_services_section_subtitle,
|
||||
'luxury_services': safe_json_loads(content.luxury_services, []),
|
||||
'luxury_experiences_section_title': content.luxury_experiences_section_title,
|
||||
'luxury_experiences_section_subtitle': content.luxury_experiences_section_subtitle,
|
||||
'luxury_experiences': safe_json_loads(content.luxury_experiences, []),
|
||||
'awards_section_title': content.awards_section_title,
|
||||
'awards_section_subtitle': content.awards_section_subtitle,
|
||||
'awards': safe_json_loads(content.awards, []),
|
||||
'cta_title': content.cta_title,
|
||||
'cta_subtitle': content.cta_subtitle,
|
||||
'cta_button_text': content.cta_button_text,
|
||||
'cta_button_link': content.cta_button_link,
|
||||
'cta_image': content.cta_image,
|
||||
'partners_section_title': content.partners_section_title,
|
||||
'partners_section_subtitle': content.partners_section_subtitle,
|
||||
'partners': safe_json_loads(content.partners, []),
|
||||
'is_active': content.is_active,
|
||||
'created_at': content.created_at.isoformat() if content.created_at else None,
|
||||
'updated_at': content.updated_at.isoformat() if content.updated_at else None
|
||||
}
|
||||
|
||||
@router.get('/')
|
||||
async def get_home_content(db: Session=Depends(get_db)):
|
||||
|
||||
@@ -105,48 +105,66 @@ const HomePage: React.FC = () => {
|
||||
const content = response.data.page_content;
|
||||
|
||||
|
||||
// Handle features - can be string, array, or null/undefined
|
||||
if (typeof content.features === 'string') {
|
||||
try {
|
||||
content.features = JSON.parse(content.features);
|
||||
} catch {
|
||||
content.features = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.features)) {
|
||||
content.features = content.features || [];
|
||||
}
|
||||
// Handle amenities - can be string, array, or null/undefined
|
||||
if (typeof content.amenities === 'string') {
|
||||
try {
|
||||
content.amenities = JSON.parse(content.amenities);
|
||||
} catch {
|
||||
content.amenities = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.amenities)) {
|
||||
content.amenities = content.amenities || [];
|
||||
}
|
||||
// Handle testimonials - can be string, array, or null/undefined
|
||||
if (typeof content.testimonials === 'string') {
|
||||
try {
|
||||
content.testimonials = JSON.parse(content.testimonials);
|
||||
} catch (e) {
|
||||
content.testimonials = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.testimonials)) {
|
||||
content.testimonials = content.testimonials || [];
|
||||
}
|
||||
// Handle gallery_images - can be string, array, or null/undefined
|
||||
if (typeof content.gallery_images === 'string') {
|
||||
try {
|
||||
content.gallery_images = JSON.parse(content.gallery_images);
|
||||
} catch (e) {
|
||||
content.gallery_images = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.gallery_images)) {
|
||||
content.gallery_images = content.gallery_images || [];
|
||||
}
|
||||
// Handle stats - can be string, array, or null/undefined
|
||||
if (typeof content.stats === 'string') {
|
||||
try {
|
||||
content.stats = JSON.parse(content.stats);
|
||||
} catch (e) {
|
||||
content.stats = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.stats)) {
|
||||
content.stats = content.stats || [];
|
||||
}
|
||||
|
||||
// Handle luxury_features - can be string, array, or null/undefined
|
||||
if (typeof content.luxury_features === 'string') {
|
||||
try {
|
||||
content.luxury_features = JSON.parse(content.luxury_features);
|
||||
} catch (e) {
|
||||
content.luxury_features = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.luxury_features)) {
|
||||
content.luxury_features = content.luxury_features || [];
|
||||
}
|
||||
if (typeof content.luxury_gallery === 'string') {
|
||||
try {
|
||||
@@ -162,40 +180,55 @@ const HomePage: React.FC = () => {
|
||||
} else {
|
||||
content.luxury_gallery = [];
|
||||
}
|
||||
// Handle luxury_testimonials - can be string, array, or null/undefined
|
||||
if (typeof content.luxury_testimonials === 'string') {
|
||||
try {
|
||||
content.luxury_testimonials = JSON.parse(content.luxury_testimonials);
|
||||
} catch (e) {
|
||||
content.luxury_testimonials = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.luxury_testimonials)) {
|
||||
content.luxury_testimonials = content.luxury_testimonials || [];
|
||||
}
|
||||
// Handle luxury_services - can be string, array, or null/undefined
|
||||
if (typeof content.luxury_services === 'string') {
|
||||
try {
|
||||
content.luxury_services = JSON.parse(content.luxury_services);
|
||||
} catch (e) {
|
||||
content.luxury_services = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.luxury_services)) {
|
||||
content.luxury_services = content.luxury_services || [];
|
||||
}
|
||||
// Handle luxury_experiences - can be string, array, or null/undefined
|
||||
if (typeof content.luxury_experiences === 'string') {
|
||||
try {
|
||||
content.luxury_experiences = JSON.parse(content.luxury_experiences);
|
||||
} catch (e) {
|
||||
content.luxury_experiences = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.luxury_experiences)) {
|
||||
content.luxury_experiences = content.luxury_experiences || [];
|
||||
}
|
||||
// Handle awards - can be string, array, or null/undefined
|
||||
if (typeof content.awards === 'string') {
|
||||
try {
|
||||
content.awards = JSON.parse(content.awards);
|
||||
} catch (e) {
|
||||
content.awards = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.awards)) {
|
||||
content.awards = content.awards || [];
|
||||
}
|
||||
// Handle partners - can be string, array, or null/undefined
|
||||
if (typeof content.partners === 'string') {
|
||||
try {
|
||||
content.partners = JSON.parse(content.partners);
|
||||
} catch (e) {
|
||||
content.partners = [];
|
||||
}
|
||||
} else if (!Array.isArray(content.partners)) {
|
||||
content.partners = content.partners || [];
|
||||
}
|
||||
|
||||
setPageContent(content);
|
||||
@@ -212,8 +245,6 @@ const HomePage: React.FC = () => {
|
||||
document.head.appendChild(metaDescription);
|
||||
}
|
||||
metaDescription.setAttribute('content', content.meta_description);
|
||||
} else {
|
||||
setPageContent(null);
|
||||
}
|
||||
} else {
|
||||
setPageContent(null);
|
||||
|
||||
Reference in New Issue
Block a user