Skip to content

Commit aaa98de

Browse files
committed
feat: make ribbon CTA dynamic via admin
1 parent 66076c8 commit aaa98de

File tree

5 files changed

+77
-7
lines changed

5 files changed

+77
-7
lines changed

app/announcements/admin.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.contrib import admin
22

3-
from .models import Announcement
3+
from .models import Announcement, RibbonCTA
44

55

66
@admin.register(Announcement)
@@ -15,3 +15,16 @@ class AnnouncementAdmin(admin.ModelAdmin):
1515
("Status", {"fields": ("is_published",)}),
1616
("Metadata", {"fields": ("created_at", "updated_at")}),
1717
)
18+
19+
20+
@admin.register(RibbonCTA)
21+
class RibbonCTAAdmin(admin.ModelAdmin):
22+
list_display = ("message", "cta_text", "is_active", "created_at", "updated_at")
23+
list_filter = ("is_active", "created_at")
24+
search_fields = ("message", "cta_text")
25+
readonly_fields = ("created_at", "updated_at")
26+
fieldsets = (
27+
("Content", {"fields": ("message", "cta_text", "cta_url")}),
28+
("Status", {"fields": ("is_active",)}),
29+
("Metadata", {"fields": ("created_at", "updated_at")}),
30+
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 5.2.7 on 2026-03-18 02:58
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('announcements', '0002_announcement_modal_size'),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name='RibbonCTA',
15+
fields=[
16+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17+
('message', models.CharField(max_length=255)),
18+
('cta_text', models.CharField(default='LEARN MORE', max_length=100)),
19+
('cta_url', models.URLField()),
20+
('is_active', models.BooleanField(default=True)),
21+
('created_at', models.DateTimeField(auto_now_add=True)),
22+
('updated_at', models.DateTimeField(auto_now=True)),
23+
],
24+
options={
25+
'verbose_name': 'Ribbon CTA',
26+
'verbose_name_plural': 'Ribbon CTAs',
27+
'ordering': ['-created_at'],
28+
},
29+
),
30+
]

app/announcements/models.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,25 @@ class Meta:
3333
ordering = ["-created_at"]
3434
verbose_name = "Announcement"
3535
verbose_name_plural = "Announcements"
36+
37+
38+
class RibbonCTA(models.Model):
39+
message = models.CharField(max_length=255)
40+
cta_text = models.CharField(max_length=100, default="LEARN MORE")
41+
cta_url = models.URLField()
42+
is_active = models.BooleanField(default=True)
43+
created_at = models.DateTimeField(auto_now_add=True)
44+
updated_at = models.DateTimeField(auto_now=True)
45+
46+
def __str__(self):
47+
return self.message
48+
49+
@classmethod
50+
def get_active(cls):
51+
"""Get the first active ribbon CTA."""
52+
return cls.objects.filter(is_active=True).first()
53+
54+
class Meta:
55+
ordering = ["-created_at"]
56+
verbose_name = "Ribbon CTA"
57+
verbose_name_plural = "Ribbon CTAs"

app/home/templates/home/components/navbar.html

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@
2121
<nav class="absolute w-full z-[999] pointer-events-none">
2222
<div class="relative flex flex-col pointer-events-auto">
2323
<!-- Ribbon CTA -->
24+
{% if ribbon_cta %}
2425
<div class="bg-orange-1 py-1.5 flex items-center justify-center gap-2 flex-col sm:flex-row">
2526
<p class="text-white font-nunito text-sm font-semibold">
26-
👋 Registration for PythonAsia 2026 is now open!
27+
{{ ribbon_cta.message }}
2728
</p>
2829
<div class="w-[20px] h-[2px] bg-white hidden sm:block"></div>
2930
<div>
30-
<a href="https://ti.to/pythonph/pythonasia-2026/" target="_blank" class="text-white font-bantayog text-base flex gap-2 items-center">
31-
<p class="">RESERVE YOUR SEATS NOW</p>
31+
<a href="{{ ribbon_cta.cta_url }}" target="_blank" class="text-white font-bantayog text-base flex gap-2 items-center">
32+
<p class="">{{ ribbon_cta.cta_text }}</p>
3233
<img src="{% static 'img/vectors/ArrowCircleRight.svg' %}" alt="kliN" class="w-[24px] h-auto" width="24" height="24" />
3334
</a>
3435
</div>
3536
</div>
37+
{% endif %}
3638

3739

3840
<div class="bg-transparent w-full max-w-[1260px] mx-auto flex pt-8 px-3 md:px-5">

config/context_processors.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
def announcement_context(request):
2-
"""Inject latest published announcement into template context."""
3-
from app.announcements.models import Announcement
2+
"""Inject latest published announcement and active ribbon CTA into template context."""
3+
from app.announcements.models import Announcement, RibbonCTA
44

5-
return {"latest_announcement": Announcement.get_latest_published()}
5+
return {
6+
"latest_announcement": Announcement.get_latest_published(),
7+
"ribbon_cta": RibbonCTA.get_active(),
8+
}

0 commit comments

Comments
 (0)