feat: add destination page configurable modules
This commit adds full support for two new destination page modules: a hero banner section and categorized destination regions. Changes include: - New SQLAlchemy models and alembic migration for DestinationHero and DestinationRegion tables - Updated Pydantic SiteConfigPatchIn schema with new optional fields - Integrated admin CRUD support for the new modules - Added default seed data for Guizhou destination regions and hero content - Exposed active modules via public site config API - Added comprehensive test coverage for admin and public endpoints
This commit is contained in:
31
app/seed.py
31
app/seed.py
@@ -8,7 +8,7 @@ from urllib.parse import quote
|
||||
from sqlalchemy import delete, select
|
||||
from sqlalchemy.orm import Session
|
||||
from .auth import hash_password
|
||||
from .content import ALIASES, CAMPAIGNS, CTAS, DESTINATIONS, HERO_SLIDES, HOTEL_GROUPS, THEMES, VEHICLE_OPTIONS
|
||||
from .content import ALIASES, CAMPAIGNS, CTAS, DESTINATION_HERO, DESTINATION_REGIONS, DESTINATIONS, HERO_SLIDES, HOTEL_GROUPS, THEMES, VEHICLE_OPTIONS
|
||||
from .database import Base, SessionLocal, engine
|
||||
from .models import (
|
||||
AdminUser,
|
||||
@@ -17,6 +17,8 @@ from .models import (
|
||||
CtaBanner,
|
||||
Destination,
|
||||
DestinationAlias,
|
||||
DestinationHero,
|
||||
DestinationRegion,
|
||||
HeroSlide,
|
||||
HotelGroup,
|
||||
MediaAsset,
|
||||
@@ -102,7 +104,7 @@ def load_products() -> list[dict]:
|
||||
|
||||
def reset_guizhou_content(db: Session) -> dict:
|
||||
products = load_products()
|
||||
for model in [SiteVersion, CampaignProduct, RouteSectionProduct, Campaign, RouteSection, ProductImage, Product, VehicleOption, HotelGroup, CtaBanner, ThemeCard, HeroSlide, DestinationAlias, Destination, MediaAsset]:
|
||||
for model in [SiteVersion, CampaignProduct, RouteSectionProduct, Campaign, RouteSection, ProductImage, Product, VehicleOption, HotelGroup, CtaBanner, ThemeCard, DestinationRegion, DestinationHero, HeroSlide, DestinationAlias, Destination, MediaAsset]:
|
||||
db.execute(delete(model))
|
||||
db.flush()
|
||||
|
||||
@@ -130,6 +132,27 @@ def reset_guizhou_content(db: Session) -> dict:
|
||||
for alias in ALIASES.get(name, []):
|
||||
db.add(DestinationAlias(destinationId=destination.id, alias=alias))
|
||||
|
||||
for index, item in enumerate(DESTINATION_HERO):
|
||||
create_media(db, item.get("image"), "destination-hero", item["title"])
|
||||
db.add(
|
||||
DestinationHero(
|
||||
title=item["title"],
|
||||
kicker=item.get("kicker"),
|
||||
image=item.get("image"),
|
||||
sortOrder=index,
|
||||
)
|
||||
)
|
||||
|
||||
for index, item in enumerate(DESTINATION_REGIONS):
|
||||
db.add(
|
||||
DestinationRegion(
|
||||
label=item["label"],
|
||||
keyword=item.get("keyword"),
|
||||
spots=item.get("spots"),
|
||||
sortOrder=index,
|
||||
)
|
||||
)
|
||||
|
||||
for index, (label, image) in enumerate(THEMES):
|
||||
create_media(db, image, "theme", label)
|
||||
db.add(ThemeCard(label=label, image=image, targetType="search", targetValue=label, sortOrder=index))
|
||||
@@ -229,6 +252,8 @@ def reset_guizhou_content(db: Session) -> dict:
|
||||
snapshot={
|
||||
"heroSlides": len(HERO_SLIDES),
|
||||
"destinations": len(DESTINATIONS),
|
||||
"destinationHero": len(DESTINATION_HERO),
|
||||
"destinationRegions": len(DESTINATION_REGIONS),
|
||||
"themeCards": len(THEMES),
|
||||
"products": len(products),
|
||||
"routeSections": len(ROUTE_SECTION_DEFAULTS),
|
||||
@@ -241,6 +266,8 @@ def reset_guizhou_content(db: Session) -> dict:
|
||||
return {
|
||||
"heroSlides": len(HERO_SLIDES),
|
||||
"destinations": len(DESTINATIONS),
|
||||
"destinationHero": len(DESTINATION_HERO),
|
||||
"destinationRegions": len(DESTINATION_REGIONS),
|
||||
"themes": len(THEMES),
|
||||
"ctaBanners": len(CTAS),
|
||||
"products": len(products),
|
||||
|
||||
Reference in New Issue
Block a user