Compare commits
7 Commits
feat/creat
...
feat/add-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
72fca4228c
|
|||
|
9f779407af
|
|||
|
f2f594afb6
|
|||
|
95ab896e5f
|
|||
|
4f58cb0320
|
|||
|
294ea9a28b
|
|||
|
e56aff1a5c
|
25
home/migrations/0018_courseindexpage.py
Normal file
25
home/migrations/0018_courseindexpage.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-19 14:41
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0017_chatmessage'),
|
||||
('wagtailcore', '0096_referenceindex_referenceindex_source_object_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseIndexPage',
|
||||
fields=[
|
||||
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('wagtailcore.page',),
|
||||
),
|
||||
]
|
||||
18
home/migrations/0019_coursepage_description.py
Normal file
18
home/migrations/0019_coursepage_description.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 6.0.3 on 2026-03-19 15:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('home', '0018_courseindexpage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='coursepage',
|
||||
name='description',
|
||||
field=models.CharField(blank=True, max_length=255),
|
||||
),
|
||||
]
|
||||
@@ -25,8 +25,29 @@ class HomePage(Page):
|
||||
content_panels = Page.content_panels + ["body"]
|
||||
|
||||
|
||||
class CourseIndexPage(Page):
|
||||
subpage_types = ["home.CoursePage"]
|
||||
|
||||
def get_context(self, request):
|
||||
context = super().get_context(request)
|
||||
all_courses = self.get_children().live()
|
||||
purchased_courses = set()
|
||||
other_courses = set()
|
||||
|
||||
for course in all_courses:
|
||||
if course.specific._user_has_access(request.user):
|
||||
purchased_courses.add(course)
|
||||
else:
|
||||
other_courses.add(course)
|
||||
|
||||
context["purchased_courses"] = sorted(
|
||||
purchased_courses, key=lambda c: c.title.lower()
|
||||
)
|
||||
context["other_courses"] = sorted(other_courses, key=lambda c: c.title.lower())
|
||||
return context
|
||||
|
||||
|
||||
class CoursePage(Page):
|
||||
body = RichTextField(blank=True)
|
||||
course_image = models.ForeignKey(
|
||||
"wagtailimages.Image",
|
||||
null=True,
|
||||
@@ -34,6 +55,8 @@ class CoursePage(Page):
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
)
|
||||
description = models.CharField(max_length=255, blank=True)
|
||||
body = RichTextField(blank=True)
|
||||
allowed_groups = ParentalManyToManyField(
|
||||
Group,
|
||||
related_name="course_pages",
|
||||
@@ -53,9 +76,11 @@ class CoursePage(Page):
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
FieldPanel("course_image"),
|
||||
FieldPanel("description"),
|
||||
FieldPanel("body"),
|
||||
FieldPanel("allowed_groups", widget=CheckboxSelectMultiple),
|
||||
]
|
||||
parent_page_types = ["home.CourseIndexPage"]
|
||||
subpage_types = ["home.CourseModulePage"]
|
||||
|
||||
|
||||
|
||||
52
home/templates/home/course_index_page.html
Normal file
52
home/templates/home/course_index_page.html
Normal file
@@ -0,0 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static i18n wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
{% block title %}{% trans "Courses" %}{% endblock title %}
|
||||
|
||||
{% block body_class %}template-courseindex{% endblock body_class %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-3xl font-bold mb-6 text-center">{% trans "Courses" %}</h1>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-4">{% trans "Purchased Courses" %}</h2>
|
||||
<div class="flex flex-wrap -mx-4">
|
||||
{% for course in purchased_courses %}
|
||||
<div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
|
||||
<a href="{{ course.url }}" class="block bg-green-50 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
{% image course.specific.course_image original %}
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h2 class="text-xl font-semibold">{{ course.specific.title }}</h2>
|
||||
<div class="relative w-8 h-8 rounded-full bg-green-500">
|
||||
<i class="fi fi-br-lock-open-alt leading-0 absolute left-0 top-1/2 translate-x-1/2 -translate-y-1/2 text-white" title="{% trans "Purchased" %}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">{{ course.specific.description|truncatewords:20 }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-4">{% trans "Available Courses" %}</h2>
|
||||
<div class="flex flex-wrap -mx-4">
|
||||
{% for course in other_courses %}
|
||||
<div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
|
||||
<a href="{{ course.url }}" class="block bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
|
||||
{% image course.specific.course_image original %}
|
||||
<div class="p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h2 class="text-xl font-semibold">{{ course.specific.title }}</h2>
|
||||
<div class="relative w-8 h-8 rounded-full bg-gray-200">
|
||||
<i class="fi fi-br-shopping-basket leading-0 absolute left-0 top-1/2 translate-x-1/2 -translate-y-1/2 text-gray-700" title="{% trans "Not Purchased" %}"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">{{ course.specific.description|truncatewords:20 }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -17,9 +17,13 @@
|
||||
</h1>
|
||||
|
||||
{% if page.course_image %}
|
||||
{% image page.course_image original alt=page.title class="w-full h-auto rounded-lg mb-6" %}
|
||||
{% image page.course_image original alt=page.title class="w-full h-auto rounded-lg mb-4" %}
|
||||
{% endif %}
|
||||
|
||||
<p class="not-prose text-gray-600 mb-6 text-lg">
|
||||
{{ page.description }}
|
||||
</p>
|
||||
|
||||
{{ page.body|richtext }}
|
||||
|
||||
{% if user_has_access %}
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
<header class="bg-blue-900 text-white shadow-md relative">
|
||||
<div class="container mx-auto flex items-center justify-between py-4 px-6">
|
||||
{% wagtail_site as current_site %}
|
||||
<div class="flex items-center gap-4">
|
||||
<a class="text-xl font-bold" href="/">{{ current_site.site_name }}</a>
|
||||
<a href="{% slugurl 'courses' %}" class="hover:underline">{% trans "Courses" %}</a>
|
||||
<a href="{% url 'calendar' %}" class="hover:underline">{% trans "Course Calendar" %}</a>
|
||||
</div>
|
||||
|
||||
<nav class="flex items-center gap-4">
|
||||
<a href="{% url 'calendar' %}" class="hover:underline">{% trans "Course Calendar" %}</a>
|
||||
{% if user.is_authenticated %}
|
||||
<a href="{% url 'account_logout' %}" class="hover:underline">{% trans "Logout" %}</a>
|
||||
{% else %}
|
||||
|
||||
Reference in New Issue
Block a user