diff --git a/app/pages/__init__.py b/app/pages/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/pages/admin.py b/app/pages/admin.py new file mode 100644 index 0000000..238fe2b --- /dev/null +++ b/app/pages/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin + +from .models import Page + +# Register your models here. +admin.site.register(Page) diff --git a/app/pages/apps.py b/app/pages/apps.py new file mode 100644 index 0000000..912892b --- /dev/null +++ b/app/pages/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PagesConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "app.pages" diff --git a/app/pages/migrations/0001_initial.py b/app/pages/migrations/0001_initial.py new file mode 100644 index 0000000..d738731 --- /dev/null +++ b/app/pages/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2025-11-30 07:16 + +import tinymce.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Page', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('slug', models.SlugField(max_length=255, unique=True)), + ('content', tinymce.models.HTMLField()), + ('is_published', models.BooleanField(default=False)), + ('meta_description', models.TextField(blank=True, null=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Page', + 'verbose_name_plural': 'Pages', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/app/pages/migrations/0002_alter_page_slug.py b/app/pages/migrations/0002_alter_page_slug.py new file mode 100644 index 0000000..efce1d7 --- /dev/null +++ b/app/pages/migrations/0002_alter_page_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-11-30 07:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pages', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='page', + name='slug', + field=models.SlugField(blank=True, max_length=255, unique=True), + ), + ] diff --git a/app/pages/migrations/__init__.py b/app/pages/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/pages/models.py b/app/pages/models.py new file mode 100644 index 0000000..b5b6abd --- /dev/null +++ b/app/pages/models.py @@ -0,0 +1,26 @@ +from django.db import models +from django.utils.text import slugify +from tinymce.models import HTMLField + + +class Page(models.Model): + title = models.CharField(max_length=255) + slug = models.SlugField(max_length=255, unique=True, blank=True) + content = HTMLField() + is_published = models.BooleanField(default=False) + meta_description = models.TextField(blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.title) + super().save(*args, **kwargs) + + def __str__(self): + return self.title + + class Meta: + ordering = ["-created_at"] + verbose_name = "Page" + verbose_name_plural = "Pages" diff --git a/app/pages/templates/pages/detail.html b/app/pages/templates/pages/detail.html new file mode 100644 index 0000000..9ef9124 --- /dev/null +++ b/app/pages/templates/pages/detail.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% load static %} +{% block content %} + + + + {% include "home/components/navbar.html" %} + + + + {{ page.title }} + + {{ page.content|safe }} + + + + + + {% include "home/sections/footer.html" %} + + +{% endblock %} diff --git a/app/pages/tests.py b/app/pages/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/app/pages/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/app/pages/urls.py b/app/pages/urls.py new file mode 100644 index 0000000..66f168c --- /dev/null +++ b/app/pages/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from app.pages.views import PageDetailView + +app_name = "pages" + +urlpatterns = [ + path("/", PageDetailView.as_view(), name="detail"), +] diff --git a/app/pages/views.py b/app/pages/views.py new file mode 100644 index 0000000..b96a01d --- /dev/null +++ b/app/pages/views.py @@ -0,0 +1,12 @@ +from django.views.generic import DetailView + +from app.pages.models import Page + + +class PageDetailView(DetailView): + model = Page + template_name = "pages/detail.html" + context_object_name = "page" + + def get_queryset(self): + return Page.objects.filter(is_published=True) diff --git a/config/settings.py b/config/settings.py index 0d10b9a..9aaadec 100644 --- a/config/settings.py +++ b/config/settings.py @@ -53,6 +53,7 @@ "corsheaders", "django_tailwind_cli", "bakery", + "tinymce", ] if settings.APP_ENV == "development": @@ -62,6 +63,7 @@ "app.home", "app.speakers", "app.sponsors", + "app.pages", ] INSTALLED_APPS = [ diff --git a/config/urls.py b/config/urls.py index e47a216..33bc9a0 100644 --- a/config/urls.py +++ b/config/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ path("admin/", admin.site.urls), path("", include("app.home.urls")), + path("pages/", include("app.pages.urls")), ] urlpatterns.append(path("__reload__/", include("django_browser_reload.urls")))