Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
LOCAL_APPS = [
"pages.citation",
"pages.data_management",
"pages.highlights",
"pages.home",
"pages.privacy",
"pages.topics",
Expand Down
75 changes: 75 additions & 0 deletions core/static/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,81 @@
--color-pp-almost-black: #1a1c1a;
}

/* Custom components for data highlights */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should discuss if we need all these component

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can discuss, maybe app-specific or maybe we can reduce these components once we will have all major apps created.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I mean among the components listed here, I think only topic badges will used in multiple pages/apps. Everything else is mostly specific to highlights (and maybe editorials). In that case I prefer to not have components and just use in-line styling using tailwind.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I would keep everything inline for now, we are not in the stage where we can really think about how to factor the styles and optimise the utility classes, so let's not bother and keep this for later when things will be more stable :)
For reference: https://tailwindcss.com/docs/styling-with-utility-classes#managing-duplication

@layer components {
/* Figure floating component for highlight detail pages */
.figure-float {
@apply float-none md:float-right mx-0 md:mx-2 mb-4 md:mb-0;
}

.figure-float img {
@apply w-96 h-auto rounded border border-pp-pale-grey float-right;
}

.figure-float figcaption {
@apply text-sm text-pp-dark-grey mt-1 italic;
}

/* Social media sharing buttons */
.share-button {
@apply inline-flex items-center justify-center w-8 h-8 text-pp-mid-blue hover:text-pp-mid-blue transition-colors duration-200 mr-2;
}

/* Data highlight card styling */
.bg-highlight {
@apply h-full px-2.5 py-3 rounded-sm;
background-color: #d8d8d8;
}

.bg-highlight:hover {
box-shadow: 0px 0px 3px #55585a;
}

.bg-highlight img {
@apply w-full h-48 object-cover rounded-t-sm;
}

.bg-highlight h6 {
@apply my-1;
}

.bg-highlight h6 a.dark-blue {
@apply text-pp-dark-blue hover:text-pp-teal transition-colors duration-200;
}

.bg-highlight small {
@apply text-pp-dark-grey text-sm;
}

.bg-highlight p {
@apply text-pp-dark-grey text-sm leading-relaxed mb-2;
}

/* Topic badge styling */
.topic_badge_small {
@apply inline-block text-white bg-pp-dark-blue px-1 py-1 text-xs font-bold leading-none text-center whitespace-nowrap align-baseline rounded transition-all duration-150 ease-in-out mr-1 mb-1;
}

.topic_badge_small a {
@apply text-white hover:text-white transition-all duration-150 ease-in-out;
}

/* Responsive image utility */
.img-fluid {
@apply max-w-full h-auto;
}

/* Rounded top border utility */
.rounded-top {
@apply rounded-t-sm;
}

/* Announcement message styling */
.announcement {
@apply bg-gray-200 border rounded py-2 px-4 my-3 italic;
}
}

/* Site's default element level styling */
@layer base {
/* Enables transition between absolute values and derived ones */
Expand Down
1 change: 1 addition & 0 deletions core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
path("", include("pages.home.urls")),
path("citation/", include("pages.citation.urls")),
path("data-management/", include("pages.data_management.urls")),
path("highlights/", include("pages.highlights.urls")),
path("privacy/", include("pages.privacy.urls")),
path("topics/", include("pages.topics.urls")),
]
Expand Down
1 change: 1 addition & 0 deletions pages/highlights/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Highlights app package."""
72 changes: 72 additions & 0 deletions pages/highlights/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from django.contrib import admin
from django.contrib import messages
from .models import DataHighlight


@admin.register(DataHighlight)
class DataHighlightAdmin(admin.ModelAdmin):
"""Admin interface for DataHighlight model.

Provides a comprehensive admin interface for managing data highlights
with organized fieldsets, search functionality, filtering, and bulk actions.

Features:
- List display with title, slug, status, and timestamps
- Filtering by active status, creation date, update date, and topics
- Search by title, summary, content, announcement, and tags
- Auto-populated slug field from title
- Horizontal filter widget for topic selection
- Bulk actions for activating/deactivating highlights
- Organized fieldsets for better UX
"""
list_display = ["title", "slug", "is_active", "created_at", "updated_at"]
list_filter = ["is_active", "created_at", "updated_at", "topics"]
search_fields = ["title", "summary", "content", "announcement", "tags"]
prepopulated_fields = {"slug": ("title",)}
readonly_fields = ["created_at", "updated_at"]
filter_horizontal = ["topics"]
actions = ["activate_highlights", "deactivate_highlights"]

fieldsets = (
("Basic Information", {
"fields": ("title", "slug", "summary")
}),
("Content", {
"fields": ("content", "announcement")
}),
("Categorization", {
"fields": ("topics", "tags")
}),
("Media", {
"fields": ("featured_image", "figure_caption")
}),
("Status", {
"fields": ("is_active",)
}),
("Timestamps", {
"fields": ("created_at", "updated_at"),
"classes": ("collapse",)
}),
)

def activate_highlights(self, request, queryset):
"""Activate selected highlights."""
updated = queryset.update(is_active=True)
self.message_user(
request,
f"Successfully activated {updated} highlight(s).",
messages.SUCCESS
)
activate_highlights.short_description = "Activate selected highlights"

def deactivate_highlights(self, request, queryset):
"""Deactivate selected highlights."""
updated = queryset.update(is_active=False)
self.message_user(
request,
f"Successfully deactivated {updated} highlight(s).",
messages.SUCCESS
)
deactivate_highlights.short_description = "Deactivate selected highlights"


12 changes: 12 additions & 0 deletions pages/highlights/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.apps import AppConfig


class HighlightsConfig(AppConfig):
"""Configuration for the highlights app.
This app manages data highlights for the Swedish Pathogens Portal,
providing a way to showcase important research findings and data insights.
"""
default_auto_field = "django.db.models.BigAutoField"
name = "pages.highlights"
verbose_name = "Data Highlights"
38 changes: 38 additions & 0 deletions pages/highlights/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 5.2.6 on 2025-10-13 14:23

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
('topics', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='DataHighlight',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(help_text='Title of the data highlight', max_length=255, unique=True)),
('slug', models.SlugField(help_text='URL-friendly version of the title (auto-generated from title)', max_length=255, unique=True)),
('summary', models.TextField(help_text='Brief summary of the highlight for display in cards')),
('content', models.TextField(help_text='Full content in markdown format displayed on detail page')),
('announcement', models.TextField(blank=True, help_text='Optional announcement message displayed at the top of the highlight')),
('tags', models.TextField(blank=True, help_text='Comma-separated tags for related content matching and search')),
('featured_image', models.ImageField(help_text='Featured image for the highlight', upload_to='highlights/images/')),
('figure_caption', models.TextField(blank=True, help_text='Caption for the featured image (optional)')),
('is_active', models.BooleanField(default=True, help_text='Whether this highlight is active and visible')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('topics', models.ManyToManyField(blank=True, help_text='Research topics associated with this highlight (optional)', to='topics.topic')),
],
options={
'verbose_name': 'Data Highlight',
'verbose_name_plural': 'Data Highlights',
'ordering': ['-created_at'],
},
),
]
Empty file.
Loading