fix button words

This commit is contained in:
mohamad
2026-05-31 12:45:05 +03:30
parent 7726b0b6ec
commit de2789bd99
14 changed files with 271 additions and 36 deletions
+2 -2
View File
@@ -63,10 +63,10 @@ class HomepageSectionAdmin(admin.ModelAdmin):
)
class AboutSectionItemInline(admin.StackedInline):
class AboutSectionItemInline(admin.TabularInline):
model = AboutSectionItem
extra = 1
fields = ("badge", "title", "content", "url", "image", "image_alt", "is_featured", "order")
fields = ("icon", "title", "content", "url", "image", "image_alt", "badge", "is_featured", "order")
ordering = ("order",)
@@ -0,0 +1,20 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pages", "0011_custompagesectionitem_page_aboutsectionitem_url"),
]
operations = [
migrations.AddField(
model_name="aboutsectionitem",
name="icon",
field=models.CharField(
blank=True,
help_text="Emoji or short symbol (e.g. ⚗️).",
max_length=20,
),
),
]
+10 -2
View File
@@ -98,6 +98,7 @@ class AboutSection(models.Model):
TYPE_GRID = "grid"
TYPE_HISTORY = "history"
TYPE_CUSTOM = "custom"
TYPE_SUPPORTERS = "supporters"
TYPE_CHOICES = [
(TYPE_HERO, "Hero"),
@@ -105,6 +106,7 @@ class AboutSection(models.Model):
(TYPE_GRID, "Grid Cards"),
(TYPE_HISTORY, "History Block"),
(TYPE_CUSTOM, "Custom Content"),
(TYPE_SUPPORTERS, "Supporters"),
]
section_type = models.CharField(max_length=30, choices=TYPE_CHOICES, default=TYPE_CUSTOM)
@@ -134,11 +136,17 @@ class AboutSection(models.Model):
class AboutSectionItem(models.Model):
section = models.ForeignKey(AboutSection, on_delete=models.CASCADE, related_name="items")
icon = models.CharField(max_length=20, blank=True, help_text="Emoji or short symbol (e.g. ⚗️).")
badge = models.CharField(max_length=100, blank=True)
title = models.CharField(max_length=300, blank=True)
content = models.TextField(blank=True, help_text="Description text or link label for History links.")
url = models.CharField(max_length=300, blank=True, help_text="Optional link; makes this item clickable.")
image = models.ImageField(upload_to="about/", blank=True, null=True)
image = models.ImageField(
upload_to="about/items/",
blank=True,
null=True,
help_text="Logo or image (used for Supporters cards).",
)
image_alt = models.CharField(max_length=200, blank=True)
is_featured = models.BooleanField(default=False, help_text="Mark as featured item (e.g. large screenshot).")
order = models.PositiveIntegerField(default=0)
@@ -149,7 +157,7 @@ class AboutSectionItem(models.Model):
verbose_name_plural = "About Section Items"
def __str__(self):
return f"{self.section} {self.title or self.badge or '(item)'}"
return f"{self.section} {self.title or self.icon or self.badge or '(item)'}"
class ContactSubmission(models.Model):
+27 -2
View File
@@ -83,7 +83,19 @@ class MainProductAdmin(admin.ModelAdmin):
list_editable = ("order", "is_active", "show_on_homepage", "homepage_order")
inlines = [MainProductArticleInline, MainProductVersionInline, SubProductInline]
fieldsets = (
(None, {"fields": ("name", "slug", "short_description", "distribution")}),
(
None,
{
"fields": (
"name",
"slug",
"short_description",
"distribution",
"package_resource_button_text",
"package_source_button_text",
)
},
),
("Description", {"fields": ("description_format", "description")}),
("Media", {"fields": ("image",)}),
("Homepage", {"fields": ("show_on_homepage", "homepage_order")}),
@@ -110,7 +122,20 @@ class SubProductAdmin(admin.ModelAdmin):
raw_id_fields = ("main_product",)
inlines = [SubProductArticleInline, SubProductVersionInline]
fieldsets = (
(None, {"fields": ("main_product", "name", "slug", "distribution", "short_description")}),
(
None,
{
"fields": (
"main_product",
"name",
"slug",
"distribution",
"short_description",
"package_resource_button_text",
"package_source_button_text",
)
},
),
("Description", {"fields": ("description_format", "description")}),
("Media", {"fields": ("image", "logo")}),
("Homepage", {"fields": ("show_on_homepage", "homepage_order")}),
@@ -0,0 +1,33 @@
# Generated by Django 5.0.2 on 2026-05-31 09:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0012_migrate_homepage_visibility_to_mainproduct'),
]
operations = [
migrations.AddField(
model_name='mainproduct',
name='package_resource_button_text',
field=models.CharField(default='PyPI', help_text='Label for the package resource button on package releases.', max_length=100),
),
migrations.AddField(
model_name='mainproduct',
name='package_source_button_text',
field=models.CharField(default='Source', help_text='Label for the source code button on package releases.', max_length=100),
),
migrations.AddField(
model_name='subproduct',
name='package_resource_button_text',
field=models.CharField(default='PyPI', help_text='Label for the package resource button on package releases.', max_length=100),
),
migrations.AddField(
model_name='subproduct',
name='package_source_button_text',
field=models.CharField(default='Source', help_text='Label for the source code button on package releases.', max_length=100),
),
]
+20
View File
@@ -36,6 +36,16 @@ class MainProduct(models.Model):
default=DISTRIBUTION_INSTALLABLE,
help_text="Only affects how releases appear on the public site.",
)
package_resource_button_text = models.CharField(
max_length=100,
default="PyPI",
help_text="Label for the package resource button on package releases.",
)
package_source_button_text = models.CharField(
max_length=100,
default="Source",
help_text="Label for the source code button on package releases.",
)
order = models.PositiveIntegerField(default=0)
is_active = models.BooleanField(default=True)
show_on_homepage = models.BooleanField(default=False, help_text="Display this product in the homepage Products section.")
@@ -99,6 +109,16 @@ class SubProduct(models.Model):
default=DISTRIBUTION_INSTALLABLE,
help_text="Only affects how releases appear on the public site.",
)
package_resource_button_text = models.CharField(
max_length=100,
default="PyPI",
help_text="Label for the package resource button on package releases.",
)
package_source_button_text = models.CharField(
max_length=100,
default="Source",
help_text="Label for the source code button on package releases.",
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
+16 -2
View File
@@ -1,5 +1,17 @@
from .models import DISTRIBUTION_INSTALLABLE, DISTRIBUTION_PACKAGE, INSTALLABLE_PLATFORM_SPECS
DEFAULT_PACKAGE_RESOURCE_BUTTON_TEXT = "PyPI"
DEFAULT_PACKAGE_SOURCE_BUTTON_TEXT = "Source"
def package_button_labels(product):
resource = (getattr(product, "package_resource_button_text", "") or "").strip()
source = (getattr(product, "package_source_button_text", "") or "").strip()
return {
"package_resource_button_text": resource or DEFAULT_PACKAGE_RESOURCE_BUTTON_TEXT,
"package_source_button_text": source or DEFAULT_PACKAGE_SOURCE_BUTTON_TEXT,
}
def featured_version(qs):
ordered = qs.order_by("order", "pk")
@@ -16,7 +28,7 @@ def install_specs_from_versions(version_list):
return tuple(s for s in INSTALLABLE_PLATFORM_SPECS if s[0] in present)
def build_release_context(distribution, active_versions):
def build_release_context(distribution, active_versions, product):
context = {
"distribution_installable": distribution == DISTRIBUTION_INSTALLABLE,
"distribution_package": distribution == DISTRIBUTION_PACKAGE,
@@ -25,6 +37,7 @@ def build_release_context(distribution, active_versions):
"featured_install_cells": [],
"show_older_versions_link": False,
"package_versions": [],
**package_button_labels(product),
}
if distribution == DISTRIBUTION_INSTALLABLE:
@@ -62,7 +75,7 @@ def build_release_context(distribution, active_versions):
return context
def build_archive_context(distribution, active_versions):
def build_archive_context(distribution, active_versions, product):
featured = featured_version(active_versions)
archive = list(
active_versions.exclude(pk=featured.pk).order_by("order", "pk")
@@ -72,6 +85,7 @@ def build_archive_context(distribution, active_versions):
context = {
"featured_version": featured,
"archive_versions": archive,
**package_button_labels(product),
}
if distribution == DISTRIBUTION_INSTALLABLE:
+8 -3
View File
@@ -31,6 +31,7 @@ class MainProductDetailView(DetailView):
release_context = build_release_context(
self.object.distribution,
self.object.versions.filter(is_active=True),
self.object,
)
release_context["versions_archive_url"] = self.object.get_versions_archive_url()
context.update(release_context)
@@ -52,7 +53,9 @@ class MainProductOlderVersionsView(TemplateView):
context["sub_product"] = None
context["product_detail_url"] = main_product.get_absolute_url()
context.update(
build_archive_context(main_product.distribution, active_versions)
build_archive_context(
main_product.distribution, active_versions, main_product
)
)
return context
@@ -85,7 +88,7 @@ class SubProductDetailView(TemplateView):
)
active_versions = sub_product.versions.filter(is_active=True)
release_context = build_release_context(
sub_product.distribution, active_versions
sub_product.distribution, active_versions, sub_product
)
release_context["versions_archive_url"] = sub_product.get_versions_archive_url()
context.update(release_context)
@@ -113,6 +116,8 @@ class SubProductOlderVersionsView(TemplateView):
context["sub_product"] = sub_product
context["product_detail_url"] = sub_product.get_absolute_url()
context.update(
build_archive_context(sub_product.distribution, active_versions)
build_archive_context(
sub_product.distribution, active_versions, sub_product
)
)
return context
+43
View File
@@ -839,6 +839,12 @@ h1, h2, h3, h4, h5, h6 {
font-size: 2rem;
margin-bottom: 1rem;
display: block;
line-height: 1;
}
.feature-icon--inline {
font-size: 1.25rem;
margin-bottom: 0.35rem;
}
.feature-title {
@@ -1727,6 +1733,43 @@ a.citation-count-badge:hover {
padding: 1.75rem;
}
.history-link-icon {
margin-right: 0.35rem;
}
.history-visual-image {
position: relative;
z-index: 2;
max-width: 100%;
max-height: 160px;
object-fit: contain;
border-radius: var(--radius-sm);
}
.about-hero-gallery {
margin-top: 2.5rem;
}
.about-section-screenshots {
margin-top: 1.5rem;
}
.about-hero-gallery:not(:has(.screenshot-item)) {
display: none;
}
.about-section-screenshots:not(:has(.screenshot-item)) {
display: none;
}
.standards-grid:not(:has(.standard-card)) {
display: none;
}
.history-visual:has(.history-visual-image) .history-year {
display: none;
}
.standard-badge {
display: inline-block;
font-size: 0.7rem;
+57 -19
View File
@@ -17,6 +17,23 @@
{% if section.subtitle %}<p class="page-hero-subtitle">{{ section.subtitle }}</p>{% endif %}
{% if section.content %}<div class="rich-content page-hero-body" data-readmore="160">{{ section.rendered_content }}</div>{% endif %}
</div>
{% with items=section.items.all %}
{% if items %}
<div class="about-screenshots-grid about-hero-gallery">
{% for item in items %}
{% if item.image %}
{% if item.is_featured %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="screenshot-item fade-in screenshot-item--featured" %}
{% else %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="screenshot-item fade-in" %}
{% endif %}
<img src="{{ item.image.url }}" alt="{{ item.image_alt|default:item.title }}" loading="lazy" />
{% include "partials/_linked_card_close.html" with url=item.url %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endwith %}
</div>
</section>
@@ -31,6 +48,19 @@
{% endif %}
</div>
</div>
{% with items=section.items.all %}
{% if items %}
<div class="standards-grid" style="margin-top: 1.5rem;">
{% for item in items %}
{% if item.is_featured and item.image %}
{% else %}
{% include "partials/_standard_item_card.html" with item=item %}
{% endif %}
{% endfor %}
</div>
{% include "partials/_section_featured_screenshots.html" with items=items %}
{% endif %}
{% endwith %}
</div>
</section>
@@ -48,13 +78,13 @@
{% if items %}
<div class="standards-grid">
{% for item in items %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="standard-card" %}
{% if item.badge %}<div class="standard-badge">{{ item.badge }}</div>{% endif %}
{% if item.title %}<h3 class="standard-title">{{ item.title }}</h3>{% endif %}
{% if item.content %}<p class="standard-desc" data-readmore="88">{{ item.content }}</p>{% endif %}
{% include "partials/_linked_card_close.html" with url=item.url %}
{% if item.is_featured and item.image %}
{% else %}
{% include "partials/_standard_item_card.html" with item=item %}
{% endif %}
{% endfor %}
</div>
{% include "partials/_section_featured_screenshots.html" with items=items %}
{% endif %}
{% endwith %}
</div>
@@ -64,27 +94,35 @@
<section class="section" aria-labelledby="history-{{ section.pk }}-heading">
<div class="container">
<div class="history-block glass-card fade-in">
{% with links=section.items.all %}
<div class="history-content">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 id="history-{{ section.pk }}-heading">{{ section.title }}</h2>{% endif %}
{% if section.content %}<div class="rich-content" data-readmore="200">{{ section.rendered_content }}</div>{% endif %}
{% with links=section.items.all %}
{% if links %}
<div class="history-links">
{% for link in links %}
{% if link.url %}
<a href="{{ link.url }}" class="btn-ghost" target="_blank" rel="noopener noreferrer">{{ link.title }}</a>
<a href="{{ link.url }}" class="btn-ghost" target="_blank" rel="noopener noreferrer">
{% if link.icon %}<span class="history-link-icon" aria-hidden="true">{{ link.icon }}</span>{% endif %}
{{ link.title }}
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endwith %}
</div>
<div class="history-visual" aria-hidden="true">
<div class="history-blob history-blob--1"></div>
<div class="history-blob history-blob--2"></div>
{% for link in links %}
{% if link.image %}
<img src="{{ link.image.url }}" alt="{{ link.image_alt|default:link.title }}" class="history-visual-image" loading="lazy" />
{% endif %}
{% endfor %}
{% if section.subtitle %}<div class="history-year">{{ section.subtitle }}</div>{% endif %}
</div>
{% endwith %}
</div>
</div>
</section>
@@ -107,13 +145,13 @@
{% if items %}
<div class="standards-grid" style="margin-top: 1.5rem;">
{% for item in items %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="standard-card" %}
{% if item.badge %}<div class="standard-badge">{{ item.badge }}</div>{% endif %}
{% if item.title %}<h3 class="standard-title">{{ item.title }}</h3>{% endif %}
{% if item.content %}<p class="standard-desc" data-readmore="88">{{ item.content }}</p>{% endif %}
{% include "partials/_linked_card_close.html" with url=item.url %}
{% if item.is_featured and item.image %}
{% else %}
{% include "partials/_standard_item_card.html" with item=item %}
{% endif %}
{% endfor %}
</div>
{% include "partials/_section_featured_screenshots.html" with items=items %}
{% endif %}
{% endwith %}
</div>
@@ -125,7 +163,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="features-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="features-grid">
{% for item in section.items.all %}
@@ -145,7 +183,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="screenshots-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="screenshots-grid">
{% for item in section.items.all %}
@@ -179,7 +217,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="products-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="products-grid">
{% for product in homepage_products %}
@@ -213,7 +251,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="products-catalog-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="products-overview-grid">
{% for product in homepage_products_catalog %}
@@ -259,7 +297,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="problems-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="problems-grid">
{% for item in section.items.all %}
@@ -279,7 +317,7 @@
<div class="section-header">
{% if section.badge %}<div class="section-badge">{{ section.badge }}</div>{% endif %}
{% if section.title %}<h2 class="section-title" id="supporters-heading-{{ section.pk }}">{{ section.title }}</h2>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% endif %}
{% if section.description %}<p class="section-desc">{{ section.description }}</p>{% elif section.content %}<div class="rich-content section-desc">{{ section.rendered_content }}</div>{% endif %}
</div>
<div class="supporters-grid">
{% for item in section.items.all %}
@@ -0,0 +1,9 @@
<div class="about-screenshots-grid about-section-screenshots">
{% for item in items %}
{% if item.is_featured and item.image %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="screenshot-item fade-in screenshot-item--featured" %}
<img src="{{ item.image.url }}" alt="{{ item.image_alt|default:item.title }}" loading="lazy" />
{% include "partials/_linked_card_close.html" with url=item.url %}
{% endif %}
{% endfor %}
</div>
@@ -0,0 +1,20 @@
{% if item.image %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="supporter-card" %}
<div class="supporter-logo">
<img src="{{ item.image.url }}" alt="{{ item.image_alt|default:item.title }} logo" loading="lazy" />
</div>
<div class="supporter-body">
{% if item.icon %}<div class="feature-icon feature-icon--inline" aria-hidden="true">{{ item.icon }}</div>{% endif %}
{% if item.badge %}<div class="standard-badge">{{ item.badge }}</div>{% endif %}
{% if item.title %}<h3 class="supporter-name">{{ item.title }}</h3>{% endif %}
{% if item.content %}<p class="supporter-desc" data-readmore="88">{{ item.content }}</p>{% endif %}
</div>
{% include "partials/_linked_card_close.html" with url=item.url %}
{% else %}
{% include "partials/_linked_card_open.html" with url=item.url card_class="feature-card" %}
{% if item.icon %}<div class="feature-icon" aria-hidden="true">{{ item.icon }}</div>{% endif %}
{% if item.badge %}<div class="standard-badge">{{ item.badge }}</div>{% endif %}
{% if item.title %}<h3 class="feature-title">{{ item.title }}</h3>{% endif %}
{% if item.content %}<p class="feature-desc" data-readmore="88">{{ item.content }}</p>{% endif %}
{% include "partials/_linked_card_close.html" with url=item.url %}
{% endif %}
+4 -4
View File
@@ -34,7 +34,7 @@
{% endif %}
</div>
<span class="sub-dl-platform">{{ cell.label }}</span>
<span class="download-version">v{{ featured_version.version }}</span>
<span class="download-version">{{ featured_version.version }}</span>
<a href="{{ cell.url }}" class="btn-primary btn--sm"{% if cell.label == "Source code" %} target="_blank" rel="noopener noreferrer"{% endif %}>{% if cell.label == "Source code" %}Source{% else %}Download{% endif %}</a>
</div>
{% endfor %}
@@ -72,7 +72,7 @@
{% for ver in package_versions %}
<li class="pkg-version-row">
<div class="pkg-version-meta">
<span class="download-version">v{{ ver.version }}</span>
<span class="download-version">{{ ver.version }}</span>
{% if ver.is_featured_stable %}
<span class="release-channel-label release-channel-label--stable pkg-stable-inline">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
@@ -84,10 +84,10 @@
</div>
<div class="pkg-version-actions">
{% if ver.package_resource_url %}
<a href="{{ ver.package_resource_url }}" class="btn-primary btn--sm" target="_blank" rel="noopener noreferrer">Open</a>
<a href="{{ ver.package_resource_url }}" class="btn-primary btn--sm" target="_blank" rel="noopener noreferrer">{{ package_resource_button_text }}</a>
{% endif %}
{% if ver.source_code_url %}
<a href="{{ ver.source_code_url }}" class="btn-ghost btn--sm" target="_blank" rel="noopener noreferrer">Source</a>
<a href="{{ ver.source_code_url }}" class="btn-ghost btn--sm" target="_blank" rel="noopener noreferrer">{{ package_source_button_text }}</a>
{% endif %}
</div>
{% if ver.release_notes %}
@@ -50,9 +50,9 @@
{% for ver in archive_versions %}
<li class="versions-package-card glass-card fade-in">
<div class="versions-package-head">
<span class="download-version">v{{ ver.version }}</span>
<span class="download-version">{{ ver.version }}</span>
{% if ver.package_resource_url %}
<a href="{{ ver.package_resource_url }}" class="btn-ghost btn--sm" target="_blank" rel="noopener noreferrer">Open</a>
<a href="{{ ver.package_resource_url }}" class="btn-ghost btn--sm" target="_blank" rel="noopener noreferrer">{{ package_resource_button_text }}</a>
{% endif %}
</div>
{% if ver.release_notes %}