Skip to content

Commit a243a71

Browse files
authored
Merge pull request #66 from zorexsalvo/feature/dynamic-ribbon-cta
Feature/dynamic ribbon cta
2 parents 66076c8 + 5af5e81 commit a243a71

File tree

6 files changed

+148
-10
lines changed

6 files changed

+148
-10
lines changed

app/announcements/admin.py

Lines changed: 27 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, ButtonCTA, RibbonCTA
44

55

66
@admin.register(Announcement)
@@ -15,3 +15,29 @@ 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+
)
31+
32+
33+
@admin.register(ButtonCTA)
34+
class ButtonCTAAdmin(admin.ModelAdmin):
35+
list_display = ("label", "url", "is_active", "created_at", "updated_at")
36+
list_filter = ("is_active", "created_at")
37+
search_fields = ("label", "url")
38+
readonly_fields = ("created_at", "updated_at")
39+
fieldsets = (
40+
("Content", {"fields": ("label", "url")}),
41+
("Status", {"fields": ("is_active",)}),
42+
("Metadata", {"fields": ("created_at", "updated_at")}),
43+
)
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+
]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 5.2.7 on 2026-03-18 03:07
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('announcements', '0003_ribboncta'),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name='ButtonCTA',
15+
fields=[
16+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17+
('label', models.CharField(max_length=100)),
18+
('url', models.URLField()),
19+
('is_active', models.BooleanField(default=True)),
20+
('created_at', models.DateTimeField(auto_now_add=True)),
21+
('updated_at', models.DateTimeField(auto_now=True)),
22+
],
23+
options={
24+
'verbose_name': 'Button CTA',
25+
'verbose_name_plural': 'Button CTAs',
26+
'ordering': ['-created_at'],
27+
},
28+
),
29+
]

app/announcements/models.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,46 @@ class Meta:
3333
ordering = ["-created_at"]
3434
verbose_name = "Announcement"
3535
verbose_name_plural = "Announcements"
36+
37+
38+
class ButtonCTA(models.Model):
39+
label = models.CharField(max_length=100)
40+
url = models.URLField()
41+
is_active = models.BooleanField(default=True)
42+
created_at = models.DateTimeField(auto_now_add=True)
43+
updated_at = models.DateTimeField(auto_now=True)
44+
45+
def __str__(self):
46+
return self.label
47+
48+
@classmethod
49+
def get_active(cls):
50+
"""Get the first active button CTA."""
51+
return cls.objects.filter(is_active=True).first()
52+
53+
class Meta:
54+
ordering = ["-created_at"]
55+
verbose_name = "Button CTA"
56+
verbose_name_plural = "Button CTAs"
57+
58+
59+
class RibbonCTA(models.Model):
60+
message = models.CharField(max_length=255)
61+
cta_text = models.CharField(max_length=100, default="LEARN MORE")
62+
cta_url = models.URLField()
63+
is_active = models.BooleanField(default=True)
64+
created_at = models.DateTimeField(auto_now_add=True)
65+
updated_at = models.DateTimeField(auto_now=True)
66+
67+
def __str__(self):
68+
return self.message
69+
70+
@classmethod
71+
def get_active(cls):
72+
"""Get the first active ribbon CTA."""
73+
return cls.objects.filter(is_active=True).first()
74+
75+
class Meta:
76+
ordering = ["-created_at"]
77+
verbose_name = "Ribbon CTA"
78+
verbose_name_plural = "Ribbon CTAs"

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

Lines changed: 12 additions & 6 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">
@@ -80,9 +82,11 @@
8082
</ul>
8183
</li>
8284
{% endfor %}
85+
{% if button_cta %}
8386
<li class="mt-2">
84-
<a href="https://ti.to/pythonph/pythonasia-2026/" target="_blank" class="btn btn-primary w-full text-center">Get Tickets</a>
87+
<a href="{{ button_cta.url }}" target="_blank" class="btn btn-primary w-full text-center">{{ button_cta.label }}</a>
8588
</li>
89+
{% endif %}
8690
</ul>
8791
</div>
8892
<!-- Header Links -->
@@ -130,11 +134,13 @@
130134
</ul>
131135
</div>
132136
<!-- Ticket Button (desktop only) -->
137+
{% if button_cta %}
133138
<div class="hidden lg:block">
134-
<a href="https://ti.to/pythonph/pythonasia-2026/" target="_blank" class="btn btn-primary btn-wide py-2 px-4 sm:py-3.5 sm:px-8 text-white">
135-
<span class="lg:inline font-bantayog text-base sm:text-xl">Get Tickets</span>
139+
<a href="{{ button_cta.url }}" target="_blank" class="btn btn-primary btn-wide py-2 px-4 sm:py-3.5 sm:px-8 text-white">
140+
<span class="lg:inline font-bantayog text-base sm:text-xl">{{ button_cta.label }}</span>
136141
</a>
137142
</div>
143+
{% endif %}
138144
</div>
139145

140146
</div>

config/context_processors.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
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 navbar CTAs into template context."""
3+
from app.announcements.models import Announcement, ButtonCTA, 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+
"button_cta": ButtonCTA.get_active(),
9+
}

0 commit comments

Comments
 (0)