feat: add direct-save CMS and About archive

This commit is contained in:
2026-09-28 20:29:00 +03:30
parent 94b32310af
commit 0f6220f72c
12 changed files with 957 additions and 41 deletions

View File

@@ -1,7 +1,9 @@
from django.http import HttpResponse
from django.utils import timezone
from ninja import Router
from ninja.errors import HttpError
from apps.events.models import Event
from apps.content.current import AboutContent, HomeContent, ListingContent, SiteContent
from apps.content.api.schemas import AboutPageOut, HomePageOut, ListingPageOut, PageContentOut, SiteContentOut
@@ -23,6 +25,27 @@ def _seo(request, obj):
}
def _about_member_out(request, member, year_label=None):
return {
"name": member.name,
"role": member.role,
"entry_year": year_label or member.entry_year,
"bio": member.bio,
"photo": _media(request, member.photo, member.photo_alt),
"position": member.position,
}
def _about_event_out(request, event):
return {
"id": event.pk,
"title": event.title,
"slug": event.slug,
"start_time": event.start_time,
"featured_image": _media(request, event.featured_image, f"پوستر {event.title}"),
}
@content_router.get("/site", response=SiteContentOut)
def get_site_content(request, response: HttpResponse):
site = SiteContent.objects.filter(pk=1).first()
@@ -50,6 +73,8 @@ def get_site_content(request, response: HttpResponse):
},
"address": site.address,
"footer_copy": site.footer_copy,
"enamad_image": _media(request, site.enamad_image, site.enamad_image_alt),
"enamad_url": site.enamad_url,
"seo": _seo(request, site),
}
@@ -66,32 +91,105 @@ def get_page_content(request, response: HttpResponse, slug: str):
"hero_heading": page.hero_heading, "hero_body": page.hero_body,
"hero_image": _media(request, page.hero_image, page.hero_image_alt),
"hero_action_label": page.hero_action_label,
"hero_action_url": page.hero_action_url,
"hero_secondary_action_label": page.hero_secondary_action_label,
"hero_secondary_action_url": page.hero_secondary_action_url,
"featured_events_heading": page.featured_events_heading,
"featured_events_intro": page.featured_events_intro,
"featured_events_link_label": page.featured_events_link_label,
"featured_events_url": page.featured_events_url,
"featured_event_action_label": page.featured_event_action_label,
"featured_posts_heading": page.featured_posts_heading,
"featured_posts_intro": page.featured_posts_intro,
"featured_posts_link_label": page.featured_posts_link_label,
"featured_posts_url": page.featured_posts_url,
"featured_post_action_label": page.featured_post_action_label,
"participation_heading": page.participation_heading,
"participation_body": page.participation_body,
"participation_image": _media(request, page.participation_image, page.participation_image_alt),
"participation_action_label": page.participation_action_label,
"participation_action_url": page.participation_action_url,
"participation_secondary_action_label": page.participation_secondary_action_label,
"participation_secondary_action_url": page.participation_secondary_action_url,
}
if slug == "about":
page = AboutContent.objects.filter(pk=1).first()
if page is None:
raise HttpError(404, "Page content not found")
event_statuses = (Event.StatusChoices.PUBLISHED, Event.StatusChoices.COMPLETED)
years_by_label = {}
for year in page.years.prefetch_related("members", "events").all():
label = year.year_label.strip() or "سال نامشخص"
events = year.events.filter(
is_deleted=False,
status__in=event_statuses,
start_time__lte=timezone.now(),
).order_by("-start_time", "-pk")
years_by_label[label] = {
"year_label": label,
"position": year.position,
"_pk": year.pk,
"members": [
_about_member_out(request, member, label)
for member in year.members.all()
],
"selected_events": [_about_event_out(request, event) for event in events],
}
legacy_members = page.members.filter(year_group__isnull=True).order_by("entry_year", "position", "pk")
for member in legacy_members:
label = member.entry_year.strip() or "سال نامشخص"
year_data = years_by_label.setdefault(
label,
{
"year_label": label,
"position": 0,
"_pk": 0,
"members": [],
"selected_events": [],
},
)
year_data["members"].append(_about_member_out(request, member, label))
years = sorted(
years_by_label.values(),
key=lambda year: (year["position"], year["_pk"], year["year_label"]),
)
all_members = [member for year in years for member in year["members"]]
return {
"slug": "about", "title": page.title, "intro": page.intro, "seo": _seo(request, page),
"identity_heading": page.identity_heading, "identity_body": page.identity_body,
"mission_heading": page.mission_heading, "mission_body": page.mission_body,
"vision_heading": page.vision_heading, "vision_body": page.vision_body,
"activities_heading": page.activities_heading, "activities_body": page.activities_body,
"values_heading": page.values_heading, "values_body": page.values_body,
"committees_heading": page.committees_heading, "committees_body": page.committees_body,
"affiliations_heading": page.affiliations_heading, "affiliations_body": page.affiliations_body,
"faq_heading": page.faq_heading, "contact_heading": page.contact_heading,
"items": list(page.items.values("kind", "title", "text", "position")),
"members_heading": page.members_heading, "members_intro": page.members_intro,
"gallery_heading": page.gallery_heading, "gallery_intro": page.gallery_intro,
"past_events_heading": page.past_events_heading,
"history_heading": page.history_heading,
"audience_heading": page.audience_heading,
"year_events_heading": page.year_events_heading,
"cta_heading": page.cta_heading,
"cta_body": page.cta_body,
"cta_action_label": page.cta_action_label,
"cta_action_url": page.cta_action_url,
"items": list(page.items.exclude(kind__in=("committees", "affiliations")).values("kind", "title", "text", "position")),
"members": all_members,
"gallery_images": [
{
"image": _media(request, item.image, item.image_alt),
"caption": item.caption, "position": item.position,
}
for item in page.gallery_images.all()
],
"audiences": list(page.audiences.values("title", "text", "icon", "position")),
"milestones": list(page.milestones.values("year_label", "title", "description", "position")),
"years": [
{key: value for key, value in year.items() if not key.startswith("_")}
for year in years
],
}
if slug in ("events", "blog"):