From 19219ada57df357250ac1cba2b18d77a61b674fd Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Mon, 18 Mar 2024 16:24:13 -0300 Subject: [PATCH 01/28] feat(nossas): init publications app --- app/nossas/publications/__init__.py | 0 app/nossas/publications/admin.py | 16 +++++ app/nossas/publications/apps.py | 7 ++ app/nossas/publications/cms_apps.py | 13 ++++ app/nossas/publications/forms.py | 23 +++++++ .../publications/migrations/0001_initial.py | 65 +++++++++++++++++++ ...ssification_publication_parent_and_more.py | 27 ++++++++ .../migrations/0003_alter_publication_tags.py | 20 ++++++ ..._slug_alter_publication_unique_together.py | 24 +++++++ .../publications/migrations/__init__.py | 0 app/nossas/publications/models.py | 57 ++++++++++++++++ .../templates/nossas/publications/detail.html | 2 + app/nossas/publications/urls.py | 8 +++ app/nossas/publications/views.py | 8 +++ 14 files changed, 270 insertions(+) create mode 100644 app/nossas/publications/__init__.py create mode 100644 app/nossas/publications/admin.py create mode 100644 app/nossas/publications/apps.py create mode 100644 app/nossas/publications/cms_apps.py create mode 100644 app/nossas/publications/forms.py create mode 100644 app/nossas/publications/migrations/0001_initial.py create mode 100644 app/nossas/publications/migrations/0002_remove_publication_classification_publication_parent_and_more.py create mode 100644 app/nossas/publications/migrations/0003_alter_publication_tags.py create mode 100644 app/nossas/publications/migrations/0004_publication_slug_alter_publication_unique_together.py create mode 100644 app/nossas/publications/migrations/__init__.py create mode 100644 app/nossas/publications/models.py create mode 100644 app/nossas/publications/templates/nossas/publications/detail.html create mode 100644 app/nossas/publications/urls.py create mode 100644 app/nossas/publications/views.py diff --git a/app/nossas/publications/__init__.py b/app/nossas/publications/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/nossas/publications/admin.py b/app/nossas/publications/admin.py new file mode 100644 index 00000000..ccbabd62 --- /dev/null +++ b/app/nossas/publications/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin + +# TODO: Mover esses imports para base de aplicativos e plugins +from nossas.apps.baseadmin import OnSiteAdmin + +from .models import Publication +from .forms import PublicationForm + + +class PublicationAdmin(OnSiteAdmin): + list_display = ("title", "slug", "parent", "created_at", "updated_at") + form = PublicationForm + prepopulated_fields = {'slug': ('title_pt_br',), } + + +admin.site.register(Publication, PublicationAdmin) diff --git a/app/nossas/publications/apps.py b/app/nossas/publications/apps.py new file mode 100644 index 00000000..b9aaf24d --- /dev/null +++ b/app/nossas/publications/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class PublicationsConfig(AppConfig): + name = "nossas.publications" + verbose_name = _("Aplicativos | Publicações") \ No newline at end of file diff --git a/app/nossas/publications/cms_apps.py b/app/nossas/publications/cms_apps.py new file mode 100644 index 00000000..7257219f --- /dev/null +++ b/app/nossas/publications/cms_apps.py @@ -0,0 +1,13 @@ +from django.utils.translation import gettext_lazy as _ + +from cms.app_base import CMSApp +from cms.apphook_pool import apphook_pool + + +@apphook_pool.register +class PublicationsApphook(CMSApp): + app_name = "publications" + name = _("Publicações") + + def get_urls(self, page=None, language=None, **kwargs): + return ["nossas.publications.urls"] \ No newline at end of file diff --git a/app/nossas/publications/forms.py b/app/nossas/publications/forms.py new file mode 100644 index 00000000..571ed30e --- /dev/null +++ b/app/nossas/publications/forms.py @@ -0,0 +1,23 @@ +from django import forms +from django.utils.translation import gettext_lazy as _ + +from cms.models import Page +from nossas.design.fields import Select2PageSearchField + +from .models import Publication + + +class PublicationForm(forms.ModelForm): + parent = Select2PageSearchField( + label=_("Página Relacionada"), + required=False, + ) + + class Meta: + model = Publication + fields = "__all__" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.fields["parent"].queryset = Page.objects.drafts().on_site() diff --git a/app/nossas/publications/migrations/0001_initial.py b/app/nossas/publications/migrations/0001_initial.py new file mode 100644 index 00000000..8c664b0a --- /dev/null +++ b/app/nossas/publications/migrations/0001_initial.py @@ -0,0 +1,65 @@ +# Generated by Django 4.2 on 2024-03-18 17:37 + +import cms.models.fields +from django.conf import settings +import django.contrib.sites.managers +from django.db import migrations, models +import django.db.models.deletion +import filer.fields.image +import tag_fields.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('tag_fields', '0001_initial'), + migrations.swappable_dependency(settings.FILER_IMAGE_MODEL), + ('cms', '0022_auto_20180620_1551'), + ] + + operations = [ + migrations.CreateModel( + name='Classification', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title_pt_br', models.CharField(max_length=180, verbose_name='Título da Classificação')), + ('title_en', models.CharField(blank=True, max_length=180, verbose_name='Título da Classificação')), + ('internal_link', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.page', verbose_name='Link interno')), + ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.site')), + ], + options={ + 'verbose_name': 'Classificação', + 'verbose_name_plural': 'Classificações', + }, + managers=[ + ('on_site', django.contrib.sites.managers.CurrentSiteManager()), + ], + ), + migrations.CreateModel( + name='Publication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Data de criação')), + ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Última atualização')), + ('title_pt_br', models.CharField(max_length=180, verbose_name='Título')), + ('title_en', models.CharField(blank=True, max_length=180, verbose_name='Título')), + ('description_pt_br', models.TextField(verbose_name='Descrição')), + ('description_en', models.TextField(blank=True, verbose_name='Descrição')), + ('classification', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='publications.classification')), + ('content', cms.models.fields.PlaceholderField(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, slotname='publication_content', to='cms.placeholder')), + ('image_default', filer.fields.image.FilerImageField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.FILER_IMAGE_MODEL, verbose_name='Imagem Padrão')), + ('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.site')), + ('tags', tag_fields.managers.ModelTagsManager(help_text='A comma-separated list of tags.', through='tag_fields.ModelTagIntFk', to='tag_fields.ModelTag', verbose_name='Tags')), + ], + options={ + 'verbose_name': 'Publicação', + 'verbose_name_plural': 'Publicações', + }, + managers=[ + ('on_site', django.contrib.sites.managers.CurrentSiteManager()), + ], + ), + ] diff --git a/app/nossas/publications/migrations/0002_remove_publication_classification_publication_parent_and_more.py b/app/nossas/publications/migrations/0002_remove_publication_classification_publication_parent_and_more.py new file mode 100644 index 00000000..6d7bf95f --- /dev/null +++ b/app/nossas/publications/migrations/0002_remove_publication_classification_publication_parent_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2 on 2024-03-18 17:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0022_auto_20180620_1551'), + ('publications', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='publication', + name='classification', + ), + migrations.AddField( + model_name='publication', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.page', verbose_name='Página Relacionada'), + ), + migrations.DeleteModel( + name='Classification', + ), + ] diff --git a/app/nossas/publications/migrations/0003_alter_publication_tags.py b/app/nossas/publications/migrations/0003_alter_publication_tags.py new file mode 100644 index 00000000..885db3d9 --- /dev/null +++ b/app/nossas/publications/migrations/0003_alter_publication_tags.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2 on 2024-03-18 19:01 + +from django.db import migrations +import tag_fields.managers + + +class Migration(migrations.Migration): + + dependencies = [ + ('tag_fields', '0001_initial'), + ('publications', '0002_remove_publication_classification_publication_parent_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='publication', + name='tags', + field=tag_fields.managers.ModelTagsManager(blank=True, help_text='A comma-separated list of tags.', through='tag_fields.ModelTagIntFk', to='tag_fields.ModelTag', verbose_name='Tags'), + ), + ] diff --git a/app/nossas/publications/migrations/0004_publication_slug_alter_publication_unique_together.py b/app/nossas/publications/migrations/0004_publication_slug_alter_publication_unique_together.py new file mode 100644 index 00000000..dce16118 --- /dev/null +++ b/app/nossas/publications/migrations/0004_publication_slug_alter_publication_unique_together.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2 on 2024-03-18 19:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('cms', '0022_auto_20180620_1551'), + ('publications', '0003_alter_publication_tags'), + ] + + operations = [ + migrations.AddField( + model_name='publication', + name='slug', + field=models.SlugField(blank=True, null=True), + ), + migrations.AlterUniqueTogether( + name='publication', + unique_together={('slug', 'parent', 'site')}, + ), + ] diff --git a/app/nossas/publications/migrations/__init__.py b/app/nossas/publications/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/nossas/publications/models.py b/app/nossas/publications/models.py new file mode 100644 index 00000000..c01b117d --- /dev/null +++ b/app/nossas/publications/models.py @@ -0,0 +1,57 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ +from django.urls import reverse + +from cms.models import Page +from cms.models.fields import PlaceholderField +from filer.fields.image import FilerImageField +from tag_fields.managers import ModelTagsManager +from translated_fields import TranslatedField + +# TODO: Mover esses imports para base de aplicativos e plugins +from nossas.apps.basemodel import OnSiteBaseModel + + +class Publication(OnSiteBaseModel): + # Temporalidade + created_at = models.DateTimeField(_("Data de criação"), auto_now_add=True) + updated_at = models.DateTimeField(_("Última atualização"), auto_now=True) + + # Conteúdo + image_default = FilerImageField( + verbose_name=_("Imagem Padrão"), + on_delete=models.SET_NULL, + blank=True, + null=True, + ) + title = TranslatedField( + models.CharField(_("Título"), max_length=180), {"en": {"blank": True}} + ) + slug = models.SlugField(null=True, blank=True) + + description = TranslatedField( + models.TextField(_("Descrição")), {"en": {"blank": True}} + ) + content = PlaceholderField("publication_content") + + # Classifação / Agrupamento + parent = models.ForeignKey( + Page, + verbose_name=_("Página Relacionada"), + blank=True, + null=True, + on_delete=models.SET_NULL, + ) + tags = ModelTagsManager(blank=True) + + class Meta: + verbose_name = _("Publicação") + verbose_name_plural = _("Publicações") + unique_together = ("slug", "parent", "site") + + + def get_absolute_url(self): + if self.parent.application_namespace: + return reverse(self.parent.application_namespace + ":detail", kwargs={"slug": self.slug}) + return '' + diff --git a/app/nossas/publications/templates/nossas/publications/detail.html b/app/nossas/publications/templates/nossas/publications/detail.html new file mode 100644 index 00000000..a28fe8ac --- /dev/null +++ b/app/nossas/publications/templates/nossas/publications/detail.html @@ -0,0 +1,2 @@ +

{{ object.title }}

+

{{ object.description }}

\ No newline at end of file diff --git a/app/nossas/publications/urls.py b/app/nossas/publications/urls.py new file mode 100644 index 00000000..8af6e771 --- /dev/null +++ b/app/nossas/publications/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from .views import PublicationDetailView + + +urlpatterns = [ + path("/", PublicationDetailView.as_view(), name="detail"), +] \ No newline at end of file diff --git a/app/nossas/publications/views.py b/app/nossas/publications/views.py new file mode 100644 index 00000000..5650e49c --- /dev/null +++ b/app/nossas/publications/views.py @@ -0,0 +1,8 @@ +from django.views.generic.detail import DetailView + +from .models import Publication + + +class PublicationDetailView(DetailView): + model = Publication + template_name = "nossas/publications/detail.html" \ No newline at end of file From 643ef63e38194cef52bdf1c6a940af7855222fb8 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Mon, 18 Mar 2024 16:24:54 -0300 Subject: [PATCH 02/28] chore(nossas): refactor import to page select2 field --- app/nossas/design/fields.py | 46 +++++++++++++++++++++++++++- app/nossas/plugins/forms/cardform.py | 44 +------------------------- app/nossas/settings.py | 2 ++ 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/app/nossas/design/fields.py b/app/nossas/design/fields.py index 74205461..682b944c 100644 --- a/app/nossas/design/fields.py +++ b/app/nossas/design/fields.py @@ -1,6 +1,9 @@ from django import forms from django.db import models +from cms.models import Page +from django_select2.forms import ModelSelect2Widget + class GraphicElementRadioSelect(forms.RadioSelect): template_name = "design/fields/graphic_element_select.html" @@ -37,4 +40,45 @@ class GraphicIconChoices(models.TextChoices): hub = "hub", "Hub, Eixo" impulsionador = "impulsionador", "Impulsionador" impacto = "impacto", "Impacto" - empatico = "empatico", "Empático" \ No newline at end of file + empatico = "empatico", "Empático" + + +class Select2PageSearchFieldMixin: + search_fields = [ + 'title_set__title__icontains', + 'title_set__menu_title__icontains', + 'title_set__slug__icontains' + ] + + def label_from_instance(self, obj): + return obj.get_title() + + +class Select2PageSelectWidget(Select2PageSearchFieldMixin, ModelSelect2Widget): + site = None + + # show entries when clicking on it + def build_attrs(self, base_attrs, extra_attrs=None): + default_attrs = {"data-minimum-input-length": 0} + default_attrs.update(base_attrs) + attrs = super().build_attrs(default_attrs, extra_attrs=extra_attrs) + return attrs + + def get_queryset(self): + if self.site: + return Page.objects.drafts().on_site(self.site) + return Page.objects.drafts() + + # we need to implement jQuery ourselves, see #180 + class Media: + js = ( + "https://code.jquery.com/jquery-3.5.1.slim.min.js", + ) + + +class Select2PageSearchField(forms.ModelChoiceField): + widget = Select2PageSelectWidget(attrs={"classes": "customizacao"}) + + def __init__(self, *args, **kwargs): + kwargs['queryset'] = self.widget.get_queryset() + super().__init__(*args, **kwargs) \ No newline at end of file diff --git a/app/nossas/plugins/forms/cardform.py b/app/nossas/plugins/forms/cardform.py index 12271c2c..21943573 100644 --- a/app/nossas/plugins/forms/cardform.py +++ b/app/nossas/plugins/forms/cardform.py @@ -2,53 +2,11 @@ from django.utils.translation import gettext_lazy as _ # from cms.forms.fields import PageSelectFormField as PageSearchField -from cms.models import Page -from django_select2.forms import ModelSelect2Widget +from nossas.design.fields import Select2PageSearchField from ..models.cardmodel import Card -class Select2PageSearchFieldMixin: - search_fields = [ - 'title_set__title__icontains', - 'title_set__menu_title__icontains', - 'title_set__slug__icontains' - ] - - def label_from_instance(self, obj): - return obj.get_title() - - -class Select2PageSelectWidget(Select2PageSearchFieldMixin, ModelSelect2Widget): - site = None - - # show entries when clicking on it - def build_attrs(self, base_attrs, extra_attrs=None): - default_attrs = {"data-minimum-input-length": 0} - default_attrs.update(base_attrs) - attrs = super().build_attrs(default_attrs, extra_attrs=extra_attrs) - return attrs - - def get_queryset(self): - if self.site: - return Page.objects.drafts().on_site(self.site) - return Page.objects.drafts() - - # we need to implement jQuery ourselves, see #180 - class Media: - js = ( - "https://code.jquery.com/jquery-3.5.1.slim.min.js", - ) - - -class Select2PageSearchField(forms.ModelChoiceField): - widget = Select2PageSelectWidget(attrs={"classes": "customizacao"}) - - def __init__(self, *args, **kwargs): - kwargs['queryset'] = self.widget.get_queryset() - super().__init__(*args, **kwargs) - - class CardPluginForm(forms.ModelForm): internal_link = Select2PageSearchField( label=_('Link interno'), diff --git a/app/nossas/settings.py b/app/nossas/settings.py index 1128d632..37814448 100644 --- a/app/nossas/settings.py +++ b/app/nossas/settings.py @@ -29,6 +29,8 @@ "nossas", "nossas.apps", "nossas.plugins", + # + "nossas.publications", # Override HTMLs "djangocms_frontend", "djangocms_frontend.contrib.utilities", From 346d70b9b1df6f3a81d5bd8ef07c491c71c5a835 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Tue, 19 Mar 2024 09:54:18 -0300 Subject: [PATCH 03/28] feat(nossas): add page to detail publication --- app/nossas/publications/models.py | 3 +-- .../templates/nossas/publications/detail.html | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/nossas/publications/models.py b/app/nossas/publications/models.py index c01b117d..74b70ff3 100644 --- a/app/nossas/publications/models.py +++ b/app/nossas/publications/models.py @@ -28,10 +28,10 @@ class Publication(OnSiteBaseModel): models.CharField(_("Título"), max_length=180), {"en": {"blank": True}} ) slug = models.SlugField(null=True, blank=True) - description = TranslatedField( models.TextField(_("Descrição")), {"en": {"blank": True}} ) + content = PlaceholderField("publication_content") # Classifação / Agrupamento @@ -54,4 +54,3 @@ def get_absolute_url(self): if self.parent.application_namespace: return reverse(self.parent.application_namespace + ":detail", kwargs={"slug": self.slug}) return '' - diff --git a/app/nossas/publications/templates/nossas/publications/detail.html b/app/nossas/publications/templates/nossas/publications/detail.html index a28fe8ac..3c099455 100644 --- a/app/nossas/publications/templates/nossas/publications/detail.html +++ b/app/nossas/publications/templates/nossas/publications/detail.html @@ -1,2 +1,8 @@ -

{{ object.title }}

-

{{ object.description }}

\ No newline at end of file +{% extends "nossas/base.html" %} +{% load cms_tags %} + +{% block content %} +{% placeholder "nossas_page_navbar" %} +{% render_placeholder object.content %} +{% placeholder "nossas_page_footer" %} +{% endblock content %} \ No newline at end of file From 68fd077dda68356901be1ce01208afad72454276 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Tue, 19 Mar 2024 09:56:06 -0300 Subject: [PATCH 04/28] fix(nossas): change footer text --- app/nossas/plugins/templates/nossas/plugins/site_footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/nossas/plugins/templates/nossas/plugins/site_footer.html b/app/nossas/plugins/templates/nossas/plugins/site_footer.html index 0486a5ff..2914c39c 100644 --- a/app/nossas/plugins/templates/nossas/plugins/site_footer.html +++ b/app/nossas/plugins/templates/nossas/plugins/site_footer.html @@ -48,6 +48,6 @@ {% show_menu 0 100 100 100 "nossas/footer_menu.html" %} - + \ No newline at end of file From dbe86983148f4af43a15fa0c17aadc005d37f6ad Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 20 Mar 2024 10:23:15 -0300 Subject: [PATCH 05/28] feat(nossas): add haystack to implement search publications and campaigns --- app/nossas/apps/basemodel.py | 2 + app/nossas/apps/models/campaigns.py | 4 ++ app/nossas/apps/search_indexes.py | 15 +++++++ .../search/indexes/apps/campaign_text.txt | 4 ++ .../design/static/design/scss/nossas.scss | 11 +++++ app/nossas/design/templatetags/design_tags.py | 14 +++++++ .../templates/nossas/plugins/navbar.html | 2 +- app/nossas/publications/admin.py | 2 +- app/nossas/publications/cms_apps.py | 12 +++++- app/nossas/publications/models.py | 8 ++++ app/nossas/publications/search_indexes.py | 15 +++++++ .../indexes/publications/publication_text.txt | 3 ++ app/nossas/settings.py | 15 +++++-- app/nossas/templates/search/search.html | 42 +++++++++++++++++++ app/nossas/urls_search.py | 6 +++ app/requirements.txt | 1 + 16 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 app/nossas/apps/search_indexes.py create mode 100644 app/nossas/apps/templates/search/indexes/apps/campaign_text.txt create mode 100644 app/nossas/publications/search_indexes.py create mode 100644 app/nossas/publications/templates/search/indexes/publications/publication_text.txt create mode 100644 app/nossas/templates/search/search.html create mode 100644 app/nossas/urls_search.py diff --git a/app/nossas/apps/basemodel.py b/app/nossas/apps/basemodel.py index aabcd05a..be011e00 100644 --- a/app/nossas/apps/basemodel.py +++ b/app/nossas/apps/basemodel.py @@ -7,6 +7,8 @@ class OnSiteBaseModel(models.Model): site = models.ForeignKey(Site, on_delete=models.CASCADE) on_site = CurrentSiteManager() + # Force objects queryset + objects = CurrentSiteManager() class Meta: abstract = True diff --git a/app/nossas/apps/models/campaigns.py b/app/nossas/apps/models/campaigns.py index 1819b8ae..c4be2806 100644 --- a/app/nossas/apps/models/campaigns.py +++ b/app/nossas/apps/models/campaigns.py @@ -72,6 +72,10 @@ def __str__(self): def get_absolute_url(self): return reverse("campaigns:campaign-detail", kwargs={"pk": self.pk}) + @property + def get_pub_date(self): + return self.release_date + def create_default_page(self): language = "pt-br" diff --git a/app/nossas/apps/search_indexes.py b/app/nossas/apps/search_indexes.py new file mode 100644 index 00000000..d6cf63cb --- /dev/null +++ b/app/nossas/apps/search_indexes.py @@ -0,0 +1,15 @@ +from datetime import datetime +from haystack import indexes +from .models import Campaign + + +class CampaignIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, use_template=True) + pub_date = indexes.DateTimeField(model_attr="release_date") + + def get_model(self): + return Campaign + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().on_site.filter(release_date__lte=datetime.now()) diff --git a/app/nossas/apps/templates/search/indexes/apps/campaign_text.txt b/app/nossas/apps/templates/search/indexes/apps/campaign_text.txt new file mode 100644 index 00000000..52a50dd2 --- /dev/null +++ b/app/nossas/apps/templates/search/indexes/apps/campaign_text.txt @@ -0,0 +1,4 @@ +{{ object.name }} +{{ object.campaign_group.name }} +{{ object.description }} +{{ object.tags.all|join:" " }} \ No newline at end of file diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 0cc342f9..bb236be5 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -615,6 +615,17 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, +
diff --git a/app/nossas/publications/admin.py b/app/nossas/publications/admin.py index ccbabd62..55f48a04 100644 --- a/app/nossas/publications/admin.py +++ b/app/nossas/publications/admin.py @@ -8,7 +8,7 @@ class PublicationAdmin(OnSiteAdmin): - list_display = ("title", "slug", "parent", "created_at", "updated_at") + list_display = ("slug", "title", "parent", "created_at", "updated_at") form = PublicationForm prepopulated_fields = {'slug': ('title_pt_br',), } diff --git a/app/nossas/publications/cms_apps.py b/app/nossas/publications/cms_apps.py index 7257219f..29775daa 100644 --- a/app/nossas/publications/cms_apps.py +++ b/app/nossas/publications/cms_apps.py @@ -10,4 +10,14 @@ class PublicationsApphook(CMSApp): name = _("Publicações") def get_urls(self, page=None, language=None, **kwargs): - return ["nossas.publications.urls"] \ No newline at end of file + return ["nossas.publications.urls"] + + + +@apphook_pool.register +class SearchApphook(CMSApp): + app_name = "haystack" + name = _("Buscador") + + def get_urls(self, page=None, language=None, **kwargs): + return ["nossas.urls_search"] \ No newline at end of file diff --git a/app/nossas/publications/models.py b/app/nossas/publications/models.py index 74b70ff3..0e0fbec3 100644 --- a/app/nossas/publications/models.py +++ b/app/nossas/publications/models.py @@ -50,7 +50,15 @@ class Meta: unique_together = ("slug", "parent", "site") + def __str__(self): + return self.title + + def get_absolute_url(self): if self.parent.application_namespace: return reverse(self.parent.application_namespace + ":detail", kwargs={"slug": self.slug}) return '' + + @property + def get_pub_date(self): + return self.created_at \ No newline at end of file diff --git a/app/nossas/publications/search_indexes.py b/app/nossas/publications/search_indexes.py new file mode 100644 index 00000000..5b7e168a --- /dev/null +++ b/app/nossas/publications/search_indexes.py @@ -0,0 +1,15 @@ +from datetime import datetime +from haystack import indexes +from .models import Publication + + +class PublicationIndex(indexes.SearchIndex, indexes.Indexable): + text = indexes.CharField(document=True, use_template=True) + pub_date = indexes.DateTimeField(model_attr="updated_at") + + def get_model(self): + return Publication + + def index_queryset(self, using=None): + """Used when the entire index for model is updated.""" + return self.get_model().on_site.filter(updated_at__lte=datetime.now()) diff --git a/app/nossas/publications/templates/search/indexes/publications/publication_text.txt b/app/nossas/publications/templates/search/indexes/publications/publication_text.txt new file mode 100644 index 00000000..5b9df632 --- /dev/null +++ b/app/nossas/publications/templates/search/indexes/publications/publication_text.txt @@ -0,0 +1,3 @@ +{{ object.title }} +{{ object.description }} +{{ object.tags.all|join:" " }} \ No newline at end of file diff --git a/app/nossas/settings.py b/app/nossas/settings.py index 37814448..197fc91c 100644 --- a/app/nossas/settings.py +++ b/app/nossas/settings.py @@ -18,6 +18,7 @@ INSTALLED_APPS = ( [ "nossas.design", + "haystack", ] + INSTALLED_APPS + [ @@ -29,7 +30,7 @@ "nossas", "nossas.apps", "nossas.plugins", - # + # "nossas.publications", # Override HTMLs "djangocms_frontend", @@ -80,9 +81,7 @@ ("en", "Inglês"), ] -LOCALE_PATHS = ( - SITE_DIR / 'locale', -) +LOCALE_PATHS = (SITE_DIR / "locale",) # URLs @@ -224,3 +223,11 @@ ["NumberedList", "BulletedList"], ] } + +# Haystack Search Engine + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'haystack.backends.simple_backend.SimpleEngine', + }, +} \ No newline at end of file diff --git a/app/nossas/templates/search/search.html b/app/nossas/templates/search/search.html new file mode 100644 index 00000000..5d4bf152 --- /dev/null +++ b/app/nossas/templates/search/search.html @@ -0,0 +1,42 @@ +{% extends 'nossas/base.html' %} +{% load cms_tags i18n design_tags %} + +{% block content %} +{% include 'nossas/plugins/navbar.html' %} + +{% include 'nossas/plugins/site_footer.html' %} +{% endblock %} \ No newline at end of file diff --git a/app/nossas/urls_search.py b/app/nossas/urls_search.py new file mode 100644 index 00000000..7403d63f --- /dev/null +++ b/app/nossas/urls_search.py @@ -0,0 +1,6 @@ +from django.urls import path, include + + +urlpatterns = [ + path("search/", include("haystack.urls")), +] \ No newline at end of file diff --git a/app/requirements.txt b/app/requirements.txt index 8f6d135c..c7aa42fc 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -32,5 +32,6 @@ requests etcd3-py>=0.1.6 dnspython>=2.4.2 # Nossas +django-haystack==3.2.1 django-translated-fields>=0.12.0 git+https://github.com/nossas/djangocms-form-builder.git@feature/add-input-fields-on-form-plugin From 4a2cb5432c8b6f5bb6b04541d7e80e72119ba1b9 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 20 Mar 2024 11:51:37 -0300 Subject: [PATCH 06/28] chore: add badge to build image docker --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dffb2fea..b9a44b94 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Publish Bonde CMS docker image](https://github.com/nossas/cms/actions/workflows/publish.yml/badge.svg?branch=main)](https://github.com/nossas/cms/actions/workflows/publish.yml) + Bonde CMS ---------- From 0fac1b0bd0d29211a2a96b8c2c1aa1f7512d7d47 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Thu, 28 Mar 2024 13:16:31 -0300 Subject: [PATCH 07/28] feat(nossas): add external_link to publication --- ...tion_managers_publication_external_link.py | 26 +++++++++++++++++++ app/nossas/publications/models.py | 20 +++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 app/nossas/publications/migrations/0005_alter_publication_managers_publication_external_link.py diff --git a/app/nossas/publications/migrations/0005_alter_publication_managers_publication_external_link.py b/app/nossas/publications/migrations/0005_alter_publication_managers_publication_external_link.py new file mode 100644 index 00000000..22599e1a --- /dev/null +++ b/app/nossas/publications/migrations/0005_alter_publication_managers_publication_external_link.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2 on 2024-03-28 16:14 + +import django.contrib.sites.managers +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('publications', '0004_publication_slug_alter_publication_unique_together'), + ] + + operations = [ + migrations.AlterModelManagers( + name='publication', + managers=[ + ('on_site', django.contrib.sites.managers.CurrentSiteManager()), + ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ], + ), + migrations.AddField( + model_name='publication', + name='external_link', + field=models.URLField(blank=True, null=True, verbose_name='Link externo'), + ), + ] diff --git a/app/nossas/publications/models.py b/app/nossas/publications/models.py index 0e0fbec3..a5aab4ae 100644 --- a/app/nossas/publications/models.py +++ b/app/nossas/publications/models.py @@ -16,7 +16,7 @@ class Publication(OnSiteBaseModel): # Temporalidade created_at = models.DateTimeField(_("Data de criação"), auto_now_add=True) updated_at = models.DateTimeField(_("Última atualização"), auto_now=True) - + # Conteúdo image_default = FilerImageField( verbose_name=_("Imagem Padrão"), @@ -31,6 +31,9 @@ class Publication(OnSiteBaseModel): description = TranslatedField( models.TextField(_("Descrição")), {"en": {"blank": True}} ) + external_link = models.URLField( + verbose_name=_("Link externo"), null=True, blank=True + ) content = PlaceholderField("publication_content") @@ -48,17 +51,22 @@ class Meta: verbose_name = _("Publicação") verbose_name_plural = _("Publicações") unique_together = ("slug", "parent", "site") - def __str__(self): return self.title - def get_absolute_url(self): + if self.external_link: + return self.external_link + if self.parent.application_namespace: - return reverse(self.parent.application_namespace + ":detail", kwargs={"slug": self.slug}) - return '' + return reverse( + self.parent.application_namespace + ":detail", + kwargs={"slug": self.slug}, + ) + + return "" @property def get_pub_date(self): - return self.created_at \ No newline at end of file + return self.created_at From d46a3fd316a6ee6a13e594f5869c2619ca9f7d9f Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Thu, 28 Mar 2024 13:58:08 -0300 Subject: [PATCH 08/28] feat(nossas): add plugin to list publications by category/page --- app/nossas/publications/cms_plugins.py | 36 +++++++++++++++++++ app/nossas/publications/forms.py | 19 +++++++++- .../migrations/0006_publicationlist.py | 26 ++++++++++++++ app/nossas/publications/models.py | 13 ++++++- .../publications/publication_list_plugin.html | 19 ++++++++++ app/nossas/settings.py | 1 + 6 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 app/nossas/publications/cms_plugins.py create mode 100644 app/nossas/publications/migrations/0006_publicationlist.py create mode 100644 app/nossas/publications/templates/nossas/publications/publication_list_plugin.html diff --git a/app/nossas/publications/cms_plugins.py b/app/nossas/publications/cms_plugins.py new file mode 100644 index 00000000..b9db800c --- /dev/null +++ b/app/nossas/publications/cms_plugins.py @@ -0,0 +1,36 @@ +from django.utils.translation import gettext_lazy as _ + +from cms.cms_plugins import CMSPluginBase, plugin_pool + +from .forms import PublicationListForm +from .models import Publication, PublicationList + + +@plugin_pool.register_plugin +class PublicationListPlugin(CMSPluginBase): + name = _("Listagem de Publicações") + module = "NOSSAS" + model = PublicationList + form = PublicationListForm + render_template = "nossas/publications/publication_list_plugin.html" + + # def get_form(self, request, obj=None, change=False, **kwargs): + # form = super().get_form(request, obj, change, **kwargs) + + # import ipdb;ipdb.set_trace() + # if not change and request.current_page.application_namespace: + # form.fields["category"].initial = request.current_page + + # return form + + + def render(self, context, instance, placeholder): + context = super().render(context, instance, placeholder) + qs = Publication.on_site.all() + + if instance.category: + qs = qs.filter(parent=instance.category) + + context["publication_list"] = qs + + return context \ No newline at end of file diff --git a/app/nossas/publications/forms.py b/app/nossas/publications/forms.py index 571ed30e..1a726d43 100644 --- a/app/nossas/publications/forms.py +++ b/app/nossas/publications/forms.py @@ -4,7 +4,7 @@ from cms.models import Page from nossas.design.fields import Select2PageSearchField -from .models import Publication +from .models import Publication, PublicationList class PublicationForm(forms.ModelForm): @@ -21,3 +21,20 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["parent"].queryset = Page.objects.drafts().on_site() + + + +class PublicationListForm(forms.ModelForm): + category = Select2PageSearchField( + label=_("Página Relacionada"), + required=False, + ) + + class Meta: + model = PublicationList + fields = "__all__" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.fields["category"].queryset = Page.objects.drafts().on_site() \ No newline at end of file diff --git a/app/nossas/publications/migrations/0006_publicationlist.py b/app/nossas/publications/migrations/0006_publicationlist.py new file mode 100644 index 00000000..055a4e62 --- /dev/null +++ b/app/nossas/publications/migrations/0006_publicationlist.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2 on 2024-03-28 16:40 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '0022_auto_20180620_1551'), + ('publications', '0005_alter_publication_managers_publication_external_link'), + ] + + operations = [ + migrations.CreateModel( + name='PublicationList', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='%(app_label)s_%(class)s', serialize=False, to='cms.cmsplugin')), + ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.page', verbose_name='Página Relacionada')), + ], + options={ + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + ] diff --git a/app/nossas/publications/models.py b/app/nossas/publications/models.py index a5aab4ae..d37ca922 100644 --- a/app/nossas/publications/models.py +++ b/app/nossas/publications/models.py @@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _ from django.urls import reverse -from cms.models import Page +from cms.models import Page, CMSPlugin from cms.models.fields import PlaceholderField from filer.fields.image import FilerImageField from tag_fields.managers import ModelTagsManager @@ -70,3 +70,14 @@ def get_absolute_url(self): @property def get_pub_date(self): return self.created_at + + + +class PublicationList(CMSPlugin): + category = models.ForeignKey( + Page, + verbose_name=_("Página Relacionada"), + blank=True, + null=True, + on_delete=models.SET_NULL, + ) \ No newline at end of file diff --git a/app/nossas/publications/templates/nossas/publications/publication_list_plugin.html b/app/nossas/publications/templates/nossas/publications/publication_list_plugin.html new file mode 100644 index 00000000..0a551cd1 --- /dev/null +++ b/app/nossas/publications/templates/nossas/publications/publication_list_plugin.html @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/app/nossas/settings.py b/app/nossas/settings.py index 922e6c95..f217ed46 100644 --- a/app/nossas/settings.py +++ b/app/nossas/settings.py @@ -110,6 +110,7 @@ "TeamAccordionPlugin", "TextPlugin", "VideoPlayerPlugin", + "PublicationListPlugin" ] CMS_TEMPLATES = [ From d3c03e7ba4ac1fcc5d2b54e2bc55f7d0b2fcda7a Mon Sep 17 00:00:00 2001 From: miguelzinh3 Date: Mon, 11 Mar 2024 21:09:57 -0300 Subject: [PATCH 09/28] fix(nossas): add js for filtering in user's select order --- app/nossas/apps/forms/campaigns.py | 4 +- .../static/campaigns/js/campaign-select2.js | 54 +++++++++++++++++++ .../plugins/filter_campaign_list_plugin.html | 7 ++- .../design/static/design/scss/forms.scss | 17 ++++-- 4 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 app/nossas/apps/static/campaigns/js/campaign-select2.js diff --git a/app/nossas/apps/forms/campaigns.py b/app/nossas/apps/forms/campaigns.py index 4ddf5aa8..ee8f6ccf 100644 --- a/app/nossas/apps/forms/campaigns.py +++ b/app/nossas/apps/forms/campaigns.py @@ -33,12 +33,12 @@ class CampaignFilterForm(forms.Form): campaign_group_id = forms.ChoiceField( label=_("Cidade"), choices=lazy(get_campaign_groups, tuple)(), - widget=s2forms.Select2Widget, + widget=s2forms.Select2MultipleWidget, required=False, ) release_year = forms.ChoiceField( label=_("Ano"), choices=lazy(get_release_years, tuple)(), - widget=s2forms.Select2Widget, + widget=s2forms.Select2MultipleWidget, required=False, ) diff --git a/app/nossas/apps/static/campaigns/js/campaign-select2.js b/app/nossas/apps/static/campaigns/js/campaign-select2.js new file mode 100644 index 00000000..5ff35021 --- /dev/null +++ b/app/nossas/apps/static/campaigns/js/campaign-select2.js @@ -0,0 +1,54 @@ +$(document).ready(function() { + function initializeSelect2WithOrdering(selectId) { + var selectionOrder = []; + + // Inicializa o Select2 para o campo especificado + $('#' + selectId).select2(); + + $('#' + selectId).on("select2:select", function(e) { + selectionOrder.push(e.params.data.id); + reorderSelection(selectId, selectionOrder); + }); + + $('#' + selectId).on("select2:unselect", function(e) { + var index = selectionOrder.indexOf(e.params.data.id); + if (index !== -1) { + selectionOrder.splice(index, 1); + } + reorderSelection(selectId, selectionOrder); + }); + } + + // Função para converter hífens em espaços no attr value + function normalizeValue(value) { + return value.replace(/-/g, " "); + } + + function reorderSelection(selectId, selectionOrder) { + var $selectionContainer = $('#' + selectId).next('.select2').find('.select2-selection__rendered'); + var orderedItems = []; + + selectionOrder.forEach(function(value) { + var normalizedValue = normalizeValue(value); + var $item = $selectionContainer.find(".select2-selection__choice").filter(function() { + return $(this).attr('title') === normalizedValue; + }); + if ($item.length) { + orderedItems.push($item.detach()); + } + }); + + orderedItems.forEach(function($item) { + $selectionContainer.append($item); + }); + + // Move o campo de pesquisa para o final + var $searchField = $selectionContainer.find('.select2-search--inline'); + $searchField.detach().appendTo($selectionContainer); + } + + // Inicializa o Select2 nos campos do form de filtro de Campanhas + initializeSelect2WithOrdering('id_tags'); + initializeSelect2WithOrdering('id_campaign_group_id'); + initializeSelect2WithOrdering('id_release_date'); +}); diff --git a/app/nossas/apps/templates/plugins/filter_campaign_list_plugin.html b/app/nossas/apps/templates/plugins/filter_campaign_list_plugin.html index 1f6f3380..67e91ca5 100644 --- a/app/nossas/apps/templates/plugins/filter_campaign_list_plugin.html +++ b/app/nossas/apps/templates/plugins/filter_campaign_list_plugin.html @@ -1,7 +1,10 @@ -{% load sekizai_tags design_tags i18n %} +{% load sekizai_tags design_tags static i18n %} {% addtoblock "css" %}{{ form.media.css }}{% endaddtoblock %} -{% addtoblock "js" %}{{ form.media.js }}{% endaddtoblock %} +{% addtoblock "js" %} + {{ form.media.js }} + +{% endaddtoblock %}
{% trans 'ENCONTRE UMA CAMPANHA' %}
diff --git a/app/nossas/design/static/design/scss/forms.scss b/app/nossas/design/static/design/scss/forms.scss index 398669aa..bcea1b31 100644 --- a/app/nossas/design/static/design/scss/forms.scss +++ b/app/nossas/design/static/design/scss/forms.scss @@ -1,6 +1,7 @@ .filter-form { .filter-form-input-group { display: flex; + align-items: center; border-bottom: 2px solid var(--bs-preto-nossas); label { @@ -9,7 +10,6 @@ font-weight: bold; font-size: 18px; } - .select2-container--default .select2-selection--multiple, .select2-container--default .select2-selection--single { border: none; @@ -21,7 +21,7 @@ .select2-container--default .select2-selection--multiple .select2-selection__rendered li { font-family: "Bebas Neue Pro"; - font-size: 15px; + font-size: 18px; line-height: 22px; text-transform: uppercase; font-weight: bold; @@ -50,12 +50,20 @@ } } + .select2-container--default .select2-selection--multiple .select2-selection__rendered { + padding: 0 8px; + } + + .select2-container--default .select2-selection--multiple .select2-selection__clear { + margin-right: 0; + } + .select2-container .select2-selection--single .select2-selection__rendered { font-family: "Bebas Neue Pro"; background-color: var(--bs-verde-nossas); display: inline-flex; color: var(--bs-white); - font-size: 15px; + font-size: 18px; text-transform: uppercase; font-weight: bold; border-radius: 20px; @@ -86,7 +94,7 @@ .select2-results__option { font-family: "Bebas Neue Pro"; - font-size: 15px; + font-size: 18px; text-transform: uppercase; font-weight: bold; padding-left: 30px; @@ -99,6 +107,7 @@ .select2-container--default .select2-results__option--highlighted[aria-selected] { background-color: #E7E7E4 !important; color: var(--bs-preto-nossas); + font-size: 18px; } .select2-container--default .select2-results__option[aria-selected=true] { From fda2860342cb7edad08e9dd6f9b42d96675d7591 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Thu, 14 Mar 2024 18:01:05 -0300 Subject: [PATCH 10/28] fix(nossas): change group and release year to multiple on filter campaigns --- app/nossas/apps/cms_plugins/campaigns.py | 5 +++-- app/nossas/apps/forms/campaigns.py | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/nossas/apps/cms_plugins/campaigns.py b/app/nossas/apps/cms_plugins/campaigns.py index 33dae901..e1047044 100644 --- a/app/nossas/apps/cms_plugins/campaigns.py +++ b/app/nossas/apps/cms_plugins/campaigns.py @@ -28,9 +28,10 @@ def render(self, context, instance, placeholder): if form.is_valid(): filters = {} + release_year = form.cleaned_data.get("release_year") if release_year: - filters["release_date__year"] = release_year + filters["release_date__year__in"] = release_year tags = form.cleaned_data.get("tags") if tags: @@ -38,7 +39,7 @@ def render(self, context, instance, placeholder): campaign_group_id = form.cleaned_data.get("campaign_group_id") if campaign_group_id: - filters["campaign_group__id"] = campaign_group_id + filters["campaign_group__id__in"] = campaign_group_id if ( settings.DATABASES.get("default").get("ENGINE") diff --git a/app/nossas/apps/forms/campaigns.py b/app/nossas/apps/forms/campaigns.py index ee8f6ccf..e4bc4264 100644 --- a/app/nossas/apps/forms/campaigns.py +++ b/app/nossas/apps/forms/campaigns.py @@ -30,15 +30,15 @@ class CampaignFilterForm(forms.Form): widget=s2forms.Select2MultipleWidget(), required=False, ) - campaign_group_id = forms.ChoiceField( + campaign_group_id = forms.MultipleChoiceField( label=_("Cidade"), choices=lazy(get_campaign_groups, tuple)(), - widget=s2forms.Select2MultipleWidget, + widget=s2forms.Select2MultipleWidget(), required=False, ) - release_year = forms.ChoiceField( + release_year = forms.MultipleChoiceField( label=_("Ano"), choices=lazy(get_release_years, tuple)(), - widget=s2forms.Select2MultipleWidget, + widget=s2forms.Select2MultipleWidget(), required=False, ) From cc92aac960e9f0014cc5d5cbb33f6bfa06fafbc3 Mon Sep 17 00:00:00 2001 From: miguelzinh3 Date: Thu, 14 Mar 2024 17:12:38 -0300 Subject: [PATCH 11/28] fix(nossas): add css classes to footer plugin --- .../design/static/design/scss/nossas.scss | 159 ++++++++++++------ .../design/templates/nossas/footer_menu.html | 8 +- .../templates/nossas/plugins/site_footer.html | 18 +- 3 files changed, 119 insertions(+), 66 deletions(-) diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index bb236be5..24c4e404 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -328,45 +328,131 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, {% include "nossas/svg/brand-lg.svg" %} -

+

- -
+
-
\ No newline at end of file + From ade572875e45e037adcd7486c3dfd9dc648b9ec3 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Fri, 15 Mar 2024 15:35:56 -0300 Subject: [PATCH 12/28] fix(nossas): change max width to footer menu item --- app/nossas/design/static/design/scss/nossas.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 24c4e404..84a8fba7 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -435,6 +435,7 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, +

{{ event.title }}

+

{{ event.month }} {{ event.year }}

+

{{ event.description }}

+ {% if event.image %} + {{ event.title }} + {% endif %} + +{% endblock %} diff --git a/app/nossas/apps/templates/nossas/timeline/list.html b/app/nossas/apps/templates/nossas/timeline/list.html new file mode 100644 index 00000000..fd51e7ee --- /dev/null +++ b/app/nossas/apps/templates/nossas/timeline/list.html @@ -0,0 +1,12 @@ +{% extends 'nossas/home.html' %} + +{% load static sekizai_tags %} + +{% block content %} + {% for event in events %} +
+

{{ event.title }}

+

{{ event.month }} {{ event.year }}

+
+ {% endfor %} +{% endblock %} diff --git a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html new file mode 100644 index 00000000..a17c840c --- /dev/null +++ b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html @@ -0,0 +1,30 @@ +{% load static cms_tags %} + +
+ {% comment %} Headline {% endcomment %} +
+
+ icon +

LINHA DO TEMPO

+

Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae.

+ +
+
7 +
+ O MUNDO AO REDOR +
+
+ AÇÕES NO NOSSAS +
+
+
+ {% comment %} Eventos {% endcomment %} +
+ {% include "nossas/timeline/list.html" %} +
+
+ {% include "nossas/timeline/detail.html" %} +
+
diff --git a/app/nossas/apps/urls/timeline.py b/app/nossas/apps/urls/timeline.py new file mode 100644 index 00000000..1084ef5a --- /dev/null +++ b/app/nossas/apps/urls/timeline.py @@ -0,0 +1,7 @@ +from django.urls import path + +from ..views.timeline import TimelineDetailView + +urlpatterns = [ + path('event//', TimelineDetailView.as_view(), name='event_detail'), +] diff --git a/app/nossas/apps/views/timeline.py b/app/nossas/apps/views/timeline.py new file mode 100644 index 00000000..12b50127 --- /dev/null +++ b/app/nossas/apps/views/timeline.py @@ -0,0 +1,9 @@ +from django.views.generic.detail import DetailView + +from ..models.timeline import TimelineEvent + + +class TimelineDetailView(DetailView): + model = TimelineEvent + template_name = "nossas/timeline/detail.html" + context_object_name = "event" diff --git a/app/nossas/settings.py b/app/nossas/settings.py index f217ed46..e2e0c169 100644 --- a/app/nossas/settings.py +++ b/app/nossas/settings.py @@ -109,6 +109,7 @@ "SocialSharePlugin", "TeamAccordionPlugin", "TextPlugin", + "TimelinePlugin", "VideoPlayerPlugin", "PublicationListPlugin" ] From 49a91f25d466a0ebefb510953968f81c27dc86a9 Mon Sep 17 00:00:00 2001 From: miguelzinh3 Date: Wed, 20 Mar 2024 14:10:54 -0300 Subject: [PATCH 14/28] feat(nossas): add TimelineEventContext model --- app/nossas/apps/admin/timeline.py | 3 +- app/nossas/apps/cms_plugins/timeline.py | 2 +- ...vent_options_timelineevent_day_and_more.py | 42 +++++++++++++++++++ app/nossas/apps/models/timeline.py | 21 ++++++++-- .../timeline/plugins/timeline_plugin.html | 29 +++++++------ .../design/static/design/scss/nossas.scss | 7 ++++ 6 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 app/nossas/apps/migrations/0016_alter_timelineevent_options_timelineevent_day_and_more.py diff --git a/app/nossas/apps/admin/timeline.py b/app/nossas/apps/admin/timeline.py index 246fdb39..3fd5a7ab 100644 --- a/app/nossas/apps/admin/timeline.py +++ b/app/nossas/apps/admin/timeline.py @@ -7,4 +7,5 @@ class TimelineEventAdmin(OnSiteAdmin): list_display = ("title", "month", "year", "image") list_filter = ("year", "month") - search_fields = ("title", "description") \ No newline at end of file + search_fields = ("title", "description") + \ No newline at end of file diff --git a/app/nossas/apps/cms_plugins/timeline.py b/app/nossas/apps/cms_plugins/timeline.py index 0fdc46c8..60c5d37b 100644 --- a/app/nossas/apps/cms_plugins/timeline.py +++ b/app/nossas/apps/cms_plugins/timeline.py @@ -13,6 +13,6 @@ class TimelinePlugin(CMSPluginBase): # def render(self, context, instance, placeholder): # context = super().render(context, instance, placeholder) - # context.update({"event_list": TimelineEvent.on_site.filter(~Q(status=JobStatus.closed))}) + # context.update({"event_list": TimelineEvent.on_site.filter()}) # return context \ No newline at end of file diff --git a/app/nossas/apps/migrations/0016_alter_timelineevent_options_timelineevent_day_and_more.py b/app/nossas/apps/migrations/0016_alter_timelineevent_options_timelineevent_day_and_more.py new file mode 100644 index 00000000..a97e83af --- /dev/null +++ b/app/nossas/apps/migrations/0016_alter_timelineevent_options_timelineevent_day_and_more.py @@ -0,0 +1,42 @@ +# Generated by Django 4.2 on 2024-03-20 17:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apps', '0015_alter_navigatecampaigns_related_campaigns_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='timelineevent', + options={'ordering': ['-year', '-month', '-day'], 'verbose_name': 'Evento da Timeline', 'verbose_name_plural': 'Eventos da Timeline'}, + ), + migrations.AddField( + model_name='timelineevent', + name='day', + field=models.IntegerField(default=1, verbose_name='Dia'), + ), + migrations.AddField( + model_name='timelineevent', + name='event_context', + field=models.CharField(choices=[('mundo', 'Mundo'), ('nossas', 'Nossas')], default='mundo', max_length=6), + ), + migrations.AlterField( + model_name='timelineevent', + name='month', + field=models.IntegerField(default=1, verbose_name='Mês'), + ), + migrations.AlterField( + model_name='timelineevent', + name='title', + field=models.CharField(max_length=140, verbose_name='Título'), + ), + migrations.AlterField( + model_name='timelineevent', + name='year', + field=models.IntegerField(default=2024, verbose_name='Ano'), + ), + ] diff --git a/app/nossas/apps/models/timeline.py b/app/nossas/apps/models/timeline.py index 3bbe72c6..89027e6e 100644 --- a/app/nossas/apps/models/timeline.py +++ b/app/nossas/apps/models/timeline.py @@ -1,15 +1,24 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from datetime import date from filer.fields.image import FilerImageField from nossas.apps.basemodel import OnSiteBaseModel +class TimelineEventContext(models.TextChoices): + world = "mundo", _("Mundo") + nossas = "nossas", _("Nossas") + + class TimelineEvent(OnSiteBaseModel): - month = models.CharField(_("Mês"), max_length=20) - year = models.IntegerField(_("Ano")) - title = models.CharField(_("Título"), max_length=255) + event_context = models.CharField(choices=TimelineEventContext.choices, max_length=6, default=TimelineEventContext.world) + day = models.IntegerField(_("Dia"), default=1) + month = models.IntegerField(_("Mês"), default=1) + year = models.IntegerField(_("Ano"), default=2024) + title = models.CharField(_("Título"), max_length=140) + # TODO: Contador de caracteres description = models.TextField(_("Descrição")) image = FilerImageField( related_name="timeline_images", @@ -22,6 +31,10 @@ def __str__(self): return self.title class Meta: - ordering = ['-year', '-month'] + ordering = ["-year", "-month", "-day"] verbose_name = _("Evento da Timeline") verbose_name_plural = _("Eventos da Timeline") + + # TODO: python date parse + def get_event_date(self): + return date(self.year, self.month, self.day) diff --git a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html index a17c840c..79535a4c 100644 --- a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html +++ b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html @@ -1,30 +1,33 @@ {% load static cms_tags %} -
+
{% comment %} Headline {% endcomment %} -
+
- icon -

LINHA DO TEMPO

-

Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae.

- +
+ ícone +

Linha do Tempo

+
-
7 -
+ +

+ Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae. +

+ +
7 +
O MUNDO AO REDOR
-
+
AÇÕES NO NOSSAS
{% comment %} Eventos {% endcomment %} -
+
{% include "nossas/timeline/list.html" %}
-
+
{% include "nossas/timeline/detail.html" %}
diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 84a8fba7..0ae0da7f 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -682,6 +682,13 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, -

{{ event.title }}

-

{{ event.month }} {{ event.year }}

- - {% endfor %} -{% endblock %} +
+{% for event in events_world %} +
+

{{ event.get_event_date}}

+

{{ event.title }}

+

{{ event.description}}

+
+{% endfor %} +
+ +
+ +
+{% for event in events_nossas %} +
+

{{ event.get_event_date }}

+

{{ event.title }}

+

{{ event.description}}

+
+{% endfor %} +
diff --git a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html index 79535a4c..6ffad91b 100644 --- a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html +++ b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html @@ -1,8 +1,8 @@ {% load static cms_tags %} -
+
{% comment %} Headline {% endcomment %} -
+
ícone @@ -14,20 +14,20 @@

Linha do Tempo

Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae.

-
7 -
+
7 +
O MUNDO AO REDOR
-
+
AÇÕES NO NOSSAS
{% comment %} Eventos {% endcomment %} -
+
{% include "nossas/timeline/list.html" %}
-
+ {% comment %}
{% include "nossas/timeline/detail.html" %} -
+
{% endcomment %}
diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 0ae0da7f..3a5ff4c3 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -684,9 +684,27 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, Date: Mon, 25 Mar 2024 17:35:53 -0300 Subject: [PATCH 17/28] feat(nossas): add order by unique year-month func in timeline plugin --- app/nossas/apps/cms_plugins/timeline.py | 50 +++++++++++++++++-- .../apps/templates/nossas/timeline/list.html | 50 ++++++++++++------- .../design/static/design/scss/nossas.scss | 7 +++ 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/app/nossas/apps/cms_plugins/timeline.py b/app/nossas/apps/cms_plugins/timeline.py index 6adee1b9..26af9e91 100644 --- a/app/nossas/apps/cms_plugins/timeline.py +++ b/app/nossas/apps/cms_plugins/timeline.py @@ -1,3 +1,6 @@ +from itertools import zip_longest +from collections import defaultdict + from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool @@ -10,15 +13,54 @@ class TimelinePlugin(CMSPluginBase): module = "NOSSAS" render_template = "nossas/timeline/plugins/timeline_plugin.html" + def prepare_timeline_events(self, events_world, events_nossas): + # Organiza eventos por ano e mês + events_by_year_month_world = defaultdict(lambda: defaultdict(list)) + events_by_year_month_nossas = defaultdict(lambda: defaultdict(list)) + + for event in events_world: + events_by_year_month_world[event.year][event.month].append(event) + + for event in events_nossas: + events_by_year_month_nossas[event.year][event.month].append(event) + + unique_years_months = set(events_by_year_month_world.keys()) | set(events_by_year_month_nossas.keys()) + for year in unique_years_months: + months_world = set(events_by_year_month_world[year].keys()) + months_nossas = set(events_by_year_month_nossas[year].keys()) + all_months = months_world | months_nossas + + for month in sorted(all_months): + if month not in months_world: + events_by_year_month_world[year][month] = [] + if month not in months_nossas: + events_by_year_month_nossas[year][month] = [] + + sorted_events_world = sorted( + [(year, month, events_by_year_month_world[year][month]) for year in events_by_year_month_world for month in sorted(events_by_year_month_world[year])], + key=lambda x: (x[0], x[1]) + ) + sorted_events_nossas = sorted( + [(year, month, events_by_year_month_nossas[year][month]) for year in events_by_year_month_nossas for month in sorted(events_by_year_month_nossas[year])], + key=lambda x: (x[0], x[1]) + ) + + # Mantem os eventos alinhados entre as categorias + aligned_events_world, aligned_events_nossas = zip(*zip_longest(sorted_events_world, sorted_events_nossas, fillvalue=(None, None, []))) + + return aligned_events_world, aligned_events_nossas + def render(self, context, instance, placeholder): context = super().render(context, instance, placeholder) - events_world = TimelineEvent.on_site.filter(event_context="mundo") - events_nossas = TimelineEvent.on_site.filter(event_context="nossas") + events_world = TimelineEvent.on_site.filter(event_context="mundo").order_by('year', 'month', 'day') + events_nossas = TimelineEvent.on_site.filter(event_context="nossas").order_by('year', 'month', 'day') + + aligned_events_world, aligned_events_nossas = self.prepare_timeline_events(events_world, events_nossas) context.update({ - "events_world": events_world, - "events_nossas": events_nossas + 'aligned_events_world': aligned_events_world, + 'aligned_events_nossas': aligned_events_nossas, }) return context diff --git a/app/nossas/apps/templates/nossas/timeline/list.html b/app/nossas/apps/templates/nossas/timeline/list.html index 3ca7307e..816e4a1f 100644 --- a/app/nossas/apps/templates/nossas/timeline/list.html +++ b/app/nossas/apps/templates/nossas/timeline/list.html @@ -1,23 +1,39 @@ {% load static sekizai_tags %}
-{% for event in events_world %} -
- {{ event.get_event_date}} -

{{ event.title }}

-

{{ event.description}}

-
-{% endfor %} + {% for year, month, events in aligned_events_world %} + {% if events %} + {% for event in events %} +
+ {{ event.get_event_date }} + {% if event.image %} + {{ event.title }} + {% endif %} +

{{ event.title }}

+

{{ event.description }}

+
+ {% endfor %} + {% else %} +
+ {% endif %} + {% endfor %}
-
-
-{% for event in events_nossas %} -
- {{ event.get_event_date }} -

{{ event.title }}

-

{{ event.description}}

-
-{% endfor %} -
+ {% for year, month, events in aligned_events_nossas %} + {% if events %} + {% for event in events %} +
+ {{ event.get_event_date }} + {% if event.image %} + {{ event.title }} + {% endif %} +

{{ event.title }}

+

{{ event.description }}

+
+ {% endfor %} + {% else %} +
+ {% endif %} + {% endfor %} + \ No newline at end of file diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 4f3741f1..a669076e 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -794,6 +794,13 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml,{{ event.title }}
{% endif %} {% endfor %} - \ No newline at end of file + diff --git a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html index f43893da..0ac91a33 100644 --- a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html +++ b/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html @@ -1,4 +1,13 @@ -{% load static cms_tags %} +{% load static i18n sekizai_tags %} + +{% addtoblock "css" %} + +{% endaddtoblock %} +{% addtoblock "js" %} + + + +{% endaddtoblock %}
@@ -13,17 +22,23 @@

Linha do Tempo

Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae.

+ +
+ {{ form.year }} +
-
+
+
+
- O MUNDO AO REDOR + {% trans "O MUNDO AO REDOR" %}
- AÇÕES NO NOSSAS + {% trans "AÇÕES NO NOSSAS" %}
@@ -31,7 +46,4 @@

Linha do Tempo

{% include "nossas/timeline/list.html" %}
- {% comment %}
- {% include "nossas/timeline/detail.html" %} -
{% endcomment %}
diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index d95274bf..4c01202e 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -712,60 +712,106 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, div { + .headline { + margin-left: 24px; + margin-right: 24px; + } - .timeline-divider { - border-bottom: solid 2px var(--bs-branco-nossas); - height: 2px; - margin: 24px 0; - width: 100%; + & p { + margin-left: 24px; + margin-right: 24px; + } - &::after { - content: ""; - border-bottom: 2px solid white; - display: inline-block; - transform: translate(-4px, 0) rotate(-90deg); - vertical-align: top; - width: 8px; + .timeline-year-filter { + margin: 0 24px; + & > div.bootstrap-select.fit-width { + width: 100% !important; + } + button { + width: 100%; + } + } } } + } + .timeline-labels { + display: flex; + justify-content: right; - .timeline-label { - align-items: center; + .timeline-labels-items { + border-right: solid 2px var(--bs-branco-nossas); display: flex; - line-height: 25px; - min-height: 319px; - overflow: hidden; - width: 25px; - - .timeline-label-text { - display: inline-block; - transform-origin: 0 0; - transform: translate(0,100%) rotate(-90deg); - vertical-align: top; - white-space: nowrap; - - &:after { + flex-direction: column; + font-family: 'Bebas Neue Pro'; + font-size: 24px; + height: 100%; + max-width: 40px; + text-transform: uppercase; + grid-column: auto/span 1; + + .timeline-divider { + border-bottom: solid 2px var(--bs-branco-nossas); + height: 2px; + margin: 24px 0; + width: 100%; + + &::after { content: ""; - float: left; - margin-top: 100%; + border-bottom: 2px solid white; + display: inline-block; + transform: translate(-4px, 0) rotate(-90deg); + vertical-align: top; + width: 8px; + } + } + + .timeline-label { + align-items: center; + display: flex; + line-height: 25px; + min-height: 319px; + overflow: hidden; + width: 25px; + + .timeline-label-text { + display: inline-block; + transform-origin: 0 0; + transform: translate(0,100%) rotate(-90deg); + vertical-align: top; + white-space: nowrap; + + &:after { + content: ""; + float: left; + margin-top: 100%; + } } } } @@ -776,7 +822,7 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml,/', TimelineDetailView.as_view(), name='event_detail'), -] diff --git a/app/nossas/apps/views/timeline.py b/app/nossas/apps/views/timeline.py deleted file mode 100644 index 12b50127..00000000 --- a/app/nossas/apps/views/timeline.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.views.generic.detail import DetailView - -from ..models.timeline import TimelineEvent - - -class TimelineDetailView(DetailView): - model = TimelineEvent - template_name = "nossas/timeline/detail.html" - context_object_name = "event" From 8721890170f22730167e2c7eec89cc54d423cf93 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 3 Apr 2024 13:21:15 -0300 Subject: [PATCH 21/28] feat(nossas): add input widget to count chars --- .../static/charsleft-widget/css/charsleft.css | 17 +++ .../charsleft-widget/js/charsleft-textarea.js | 41 +++++++ .../static/charsleft-widget/js/charsleft.js | 41 +++++++ app/nossas/design/widgets.py | 110 ++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 app/nossas/design/static/charsleft-widget/css/charsleft.css create mode 100644 app/nossas/design/static/charsleft-widget/js/charsleft-textarea.js create mode 100644 app/nossas/design/static/charsleft-widget/js/charsleft.js create mode 100644 app/nossas/design/widgets.py diff --git a/app/nossas/design/static/charsleft-widget/css/charsleft.css b/app/nossas/design/static/charsleft-widget/css/charsleft.css new file mode 100644 index 00000000..bbf73f37 --- /dev/null +++ b/app/nossas/design/static/charsleft-widget/css/charsleft.css @@ -0,0 +1,17 @@ +.charsleft{ + width: 100%; +} +.charsleft > span{ + vertical-align: middle; + margin-left: 0.5em; + color: #ccc; + line-height: 23px; +} +.charsleft span .count{ + color: #333; +} +.charsleft .maxlength{ + display: none; +} +.charsleft span .orange{color:#EA1300 !important;} +.charsleft span .red{color: #D56F24 !important;} \ No newline at end of file diff --git a/app/nossas/design/static/charsleft-widget/js/charsleft-textarea.js b/app/nossas/design/static/charsleft-widget/js/charsleft-textarea.js new file mode 100644 index 00000000..475beaf1 --- /dev/null +++ b/app/nossas/design/static/charsleft-widget/js/charsleft-textarea.js @@ -0,0 +1,41 @@ +(function($){ + $.fn.charsLeft = function(options){ + var defaults = { + 'source':'textarea', + 'dest':'.count', + } + var options = $.extend(defaults, options); + + var calculate = function(source, dest, maxlength){ + var remaining = maxlength - source.val().length; + dest.html(remaining); + /* Over 50%, change colour to orange */ + p = (100 * remaining) / maxlength; + if(p < 25){ + dest.addClass('orange'); + }else if(p < 50){ + dest.addClass('red'); + }else{ + dest.removeClass('orange red'); + } + }; + + this.each(function(i, el) { + var maxlength = $(this).find('.maxlength').html(); + var dest = $(this).find(options.dest); + var source = $(this).find(options.source); + source.keyup(function(){ + calculate(source, dest, maxlength) + }); + source.change(function(){ + calculate(source, dest, maxlength) + }); + }); + }; + $(function() { // Added page ready wrapper + $(".charsleft-input").charsLeft({ + 'source':'textarea', + 'dest':".count", + }); + }); + })(django.jQuery); \ No newline at end of file diff --git a/app/nossas/design/static/charsleft-widget/js/charsleft.js b/app/nossas/design/static/charsleft-widget/js/charsleft.js new file mode 100644 index 00000000..4a6537ef --- /dev/null +++ b/app/nossas/design/static/charsleft-widget/js/charsleft.js @@ -0,0 +1,41 @@ +(function($){ + $.fn.charsLeft = function(options){ + var defaults = { + 'source':'input', + 'dest':'.count', + } + var options = $.extend(defaults, options); + + var calculate = function(source, dest, maxlength){ + var remaining = maxlength - source.val().length; + dest.html(remaining); + /* Over 50%, change colour to orange */ + p = (100 * remaining) / maxlength; + if(p < 25){ + dest.addClass('orange'); + }else if(p < 50){ + dest.addClass('red'); + }else{ + dest.removeClass('orange red'); + } + }; + + this.each(function(i, el) { + var maxlength = $(this).find('.maxlength').html(); + var dest = $(this).find(options.dest); + var source = $(this).find(options.source); + source.keyup(function(){ + calculate(source, dest, maxlength) + }); + source.change(function(){ + calculate(source, dest, maxlength) + }); + }); + }; + $(function() { // Added page ready wrapper + $(".charsleft-input").charsLeft({ + 'source':'input', + 'dest':".count", + }); + }); + })(django.jQuery); \ No newline at end of file diff --git a/app/nossas/design/widgets.py b/app/nossas/design/widgets.py new file mode 100644 index 00000000..ec179f11 --- /dev/null +++ b/app/nossas/design/widgets.py @@ -0,0 +1,110 @@ +from django import forms, VERSION +from django.utils.safestring import mark_safe +from django.forms.utils import flatatt +from django.utils.encoding import force_str +from django.utils.translation import gettext_lazy as _ + + +class CharsLeftTextInput(forms.TextInput): + def render(self, name, value, attrs=None, **kwargs): + if value is None: + value = "" + + extra_attrs = { + "type": self.input_type, + "name": name, + "maxlength": self.attrs.get("maxlength"), + } + + # Signature for build_attrs changed in 1.11 + # https://code.djangoproject.com/ticket/28095 + if VERSION < (1, 11): + final_attrs = self.build_attrs(attrs, **extra_attrs) + else: + final_attrs = self.build_attrs(attrs, extra_attrs=extra_attrs) + + if value != "": + final_attrs["value"] = force_str(value) + + maxlength = final_attrs.get("maxlength", False) + if not maxlength: + return mark_safe("" % flatatt(final_attrs)) + + current = force_str(int(maxlength) - len(value)) + html = """ + + + + %(current)s %(char_remain_str)s + %(maxlength)s + + """ % { + "attrs": flatatt(final_attrs), + "current": current, + "char_remain_str": _("caracteres restantes"), + "maxlength": int(maxlength), + } + return mark_safe(html) + + class Media: + css = { + "screen": ("charsleft-widget/css/charsleft.css",), + } + js = ( + "https://code.jquery.com/jquery-3.5.1.slim.min.js", + "charsleft-widget/js/charsleft.js", + ) + + +class CharsLeftTextarea(forms.Textarea): + def render(self, name, value, attrs=None, **kwargs): + if value is None: + value = "" + + extra_attrs = { + "name": name, + "maxlength": self.attrs.get("maxlength"), + } + + # Signature for build_attrs changed in 1.11 + # https://code.djangoproject.com/ticket/28095 + if VERSION < (1, 11): + final_attrs = self.build_attrs(attrs, **extra_attrs) + else: + final_attrs = self.build_attrs(attrs, extra_attrs=extra_attrs) + + if value != "": + value = force_str(value) + + maxlength = final_attrs.get("maxlength", False) + if not maxlength: + return mark_safe( + '' + % {"value": value, "attrs": flatatt(final_attrs)} + ) + + current = force_str(int(maxlength) - len(value)) + html = """ + + + + %(current)s %(char_remain_str)s + %(maxlength)s + + """ % { + "value": value, + "attrs": flatatt(final_attrs), + "current": current, + "char_remain_str": _("caracteres restantes"), + "maxlength": int(maxlength), + } + return mark_safe(html) + + class Media: + css = { + "screen": ("charsleft-widget/css/charsleft.css",), + } + js = ( + "https://code.jquery.com/jquery-3.5.1.slim.min.js", + "charsleft-widget/js/charsleft-textarea.js", + ) From 4401d6fc9d83d4df1b547c4f837731e70be24331 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 3 Apr 2024 13:23:31 -0300 Subject: [PATCH 22/28] fix(nossas): render empty lists and add limit to description chars --- app/nossas/apps/admin/timeline.py | 17 +++++++++++ app/nossas/apps/cms_plugins/timeline.py | 5 +++- app/nossas/apps/forms/timeline.py | 13 +++++++++ app/nossas/apps/models/timeline.py | 29 +++++++++++++++++-- .../apps/templates/nossas/timeline/list.html | 4 +-- 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/app/nossas/apps/admin/timeline.py b/app/nossas/apps/admin/timeline.py index f2ec00e8..dfbfd99d 100644 --- a/app/nossas/apps/admin/timeline.py +++ b/app/nossas/apps/admin/timeline.py @@ -2,9 +2,26 @@ from ..models.timeline import TimelineEvent from nossas.apps.baseadmin import OnSiteAdmin +from ..forms.timeline import TimelineEventForm + @admin.register(TimelineEvent) class TimelineEventAdmin(OnSiteAdmin): list_display = ("title", "month", "year", "image") list_filter = ("year", "month") search_fields = ("title", "description") + form = TimelineEventForm + fieldsets = ( + ( + None, + { + "fields": [ + "event_context", + ("day", "month", "year"), + "title", + "description", + "image", + ], + }, + ), + ) diff --git a/app/nossas/apps/cms_plugins/timeline.py b/app/nossas/apps/cms_plugins/timeline.py index dbb386de..a78999b5 100644 --- a/app/nossas/apps/cms_plugins/timeline.py +++ b/app/nossas/apps/cms_plugins/timeline.py @@ -65,7 +65,10 @@ def render(self, context, instance, placeholder): events_world = TimelineEvent.on_site.filter(event_context="mundo", **event_filter).order_by("year", "month", "day") events_nossas = TimelineEvent.on_site.filter(event_context="nossas", **event_filter).order_by("year", "month", "day") - aligned_events_world, aligned_events_nossas = self.prepare_timeline_events(events_world, events_nossas) + if len(events_nossas) > 0 and len(events_world) > 0: + aligned_events_world, aligned_events_nossas = self.prepare_timeline_events(events_world, events_nossas) + else: + aligned_events_world, aligned_events_nossas = [], [] context.update({ "aligned_events_world": aligned_events_world, diff --git a/app/nossas/apps/forms/timeline.py b/app/nossas/apps/forms/timeline.py index dfaff7a1..0c3a083f 100644 --- a/app/nossas/apps/forms/timeline.py +++ b/app/nossas/apps/forms/timeline.py @@ -2,6 +2,8 @@ from django.utils.functional import lazy from django.utils.translation import gettext_lazy as _ +from nossas.design.widgets import CharsLeftTextInput, CharsLeftTextarea + from ..models.timeline import TimelineEvent def get_events_year_choices(): @@ -20,3 +22,14 @@ class TimelineFilterForm(forms.Form): }), required=False, ) + + + +class TimelineEventForm(forms.ModelForm): + + class Meta: + model = TimelineEvent + fields = "__all__" + widgets = { + "description": CharsLeftTextarea(attrs={"maxlength": 200}) + } \ No newline at end of file diff --git a/app/nossas/apps/models/timeline.py b/app/nossas/apps/models/timeline.py index 18caedda..12b4eeda 100644 --- a/app/nossas/apps/models/timeline.py +++ b/app/nossas/apps/models/timeline.py @@ -12,10 +12,32 @@ class TimelineEventContext(models.TextChoices): nossas = "nossas", _("Nossas") +class TimelineMonth(models.IntegerChoices): + jan = 1, _("Janeiro") + feb = 2, _("Fevereiro") + mar = 3, _("Março") + apr = 4, _("Abril") + may = 5, _("Maio") + jun = 6, _("Junho") + jul = 7, _("Julho") + aug = 8, _("Agosto") + sep = 9, _("Setembro") + oct = 10, _("Outubro") + nov = 11, _("Novembro") + dec = 12, _("Dezembro") + + class TimelineEvent(OnSiteBaseModel): - event_context = models.CharField(choices=TimelineEventContext.choices, max_length=6, default=TimelineEventContext.world) + event_context = models.CharField( + _("Contexto"), + choices=TimelineEventContext.choices, + max_length=6, + default=TimelineEventContext.world, + ) day = models.IntegerField(_("Dia"), default=1) - month = models.IntegerField(_("Mês"), default=1) + month = models.IntegerField( + _("Mês"), default=TimelineMonth.jan, choices=TimelineMonth.choices + ) year = models.IntegerField(_("Ano"), default=2024) title = models.CharField(_("Título"), max_length=140) # TODO: Contador de caracteres @@ -25,7 +47,8 @@ class TimelineEvent(OnSiteBaseModel): on_delete=models.SET_NULL, null=True, blank=True, - verbose_name=_("Imagem")) + verbose_name=_("Imagem"), + ) def __str__(self): return self.title diff --git a/app/nossas/apps/templates/nossas/timeline/list.html b/app/nossas/apps/templates/nossas/timeline/list.html index e3281958..3f84b8d2 100644 --- a/app/nossas/apps/templates/nossas/timeline/list.html +++ b/app/nossas/apps/templates/nossas/timeline/list.html @@ -10,7 +10,7 @@ {{ event.title }} {% endif %}

{{ event.title }}

-

{{ event.description }}

+

{{ event.description|truncatechars:200 }}

{% endfor %} {% else %} @@ -31,7 +31,7 @@

{{ event.title }}

{{ event.title }} {% endif %}

{{ event.title }}

-

{{ event.description }}

+

{{ event.description|truncatechars:200 }}

{% endfor %} {% else %} From 938a0b2621a0215cad787954cd88d81722ed087d Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 3 Apr 2024 16:37:25 -0300 Subject: [PATCH 23/28] chore(nossas): change location to template plugins --- .../detail.html => plugins/timeline_detail.html} | 0 .../timeline/list.html => plugins/timeline_list.html} | 0 .../{nossas/timeline => }/plugins/timeline_plugin.html | 10 +++++----- 3 files changed, 5 insertions(+), 5 deletions(-) rename app/nossas/apps/templates/{nossas/timeline/detail.html => plugins/timeline_detail.html} (100%) rename app/nossas/apps/templates/{nossas/timeline/list.html => plugins/timeline_list.html} (100%) rename app/nossas/apps/templates/{nossas/timeline => }/plugins/timeline_plugin.html (75%) diff --git a/app/nossas/apps/templates/nossas/timeline/detail.html b/app/nossas/apps/templates/plugins/timeline_detail.html similarity index 100% rename from app/nossas/apps/templates/nossas/timeline/detail.html rename to app/nossas/apps/templates/plugins/timeline_detail.html diff --git a/app/nossas/apps/templates/nossas/timeline/list.html b/app/nossas/apps/templates/plugins/timeline_list.html similarity index 100% rename from app/nossas/apps/templates/nossas/timeline/list.html rename to app/nossas/apps/templates/plugins/timeline_list.html diff --git a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html b/app/nossas/apps/templates/plugins/timeline_plugin.html similarity index 75% rename from app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html rename to app/nossas/apps/templates/plugins/timeline_plugin.html index 0ac91a33..864c98b5 100644 --- a/app/nossas/apps/templates/nossas/timeline/plugins/timeline_plugin.html +++ b/app/nossas/apps/templates/plugins/timeline_plugin.html @@ -1,4 +1,4 @@ -{% load static i18n sekizai_tags %} +{% load static i18n cms_tags sekizai_tags %} {% addtoblock "css" %} @@ -19,9 +19,9 @@

Linha do Tempo

-

- Siga a linha do tempo e entenda a atuação do NOSSAS ao longo dos anos. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed qut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut a quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae. -

+ {% for plugin in instance.child_plugin_instances %} + {% render_plugin plugin %} + {% endfor %}
{{ form.year }} @@ -44,6 +44,6 @@

Linha do Tempo

- {% include "nossas/timeline/list.html" %} + {% include "plugins/timeline_list.html" %}
From 2e04cc822e6f91308ddb3407e1101647ed109dc8 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 3 Apr 2024 16:37:57 -0300 Subject: [PATCH 24/28] fix(nossas): size select filter timeline --- app/nossas/design/static/design/scss/nossas.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/nossas/design/static/design/scss/nossas.scss b/app/nossas/design/static/design/scss/nossas.scss index 4c01202e..fd9f016f 100644 --- a/app/nossas/design/static/design/scss/nossas.scss +++ b/app/nossas/design/static/design/scss/nossas.scss @@ -723,7 +723,7 @@ $carousel-control-next-icon-bg: url("data:image/svg+xml, 0 and len(events_world) > 0: - aligned_events_world, aligned_events_nossas = self.prepare_timeline_events(events_world, events_nossas) + aligned_events_world, aligned_events_nossas = self.prepare_timeline_events( + events_world, events_nossas + ) else: aligned_events_world, aligned_events_nossas = [], [] - context.update({ - "aligned_events_world": aligned_events_world, - "aligned_events_nossas": aligned_events_nossas, - "form": form, - }) + context.update( + { + "aligned_events_world": aligned_events_world, + "aligned_events_nossas": aligned_events_nossas, + "form": form, + } + ) - return context \ No newline at end of file + return context From d0861c88385151e0850cefd53725e7baa8a4df17 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Thu, 4 Apr 2024 10:29:50 -0300 Subject: [PATCH 26/28] feat(nossas): add column size to mobile grid --- .../0035_grid_grid_layout_mobile_and_more.py | 23 +++++++++++++++++++ app/nossas/plugins/models/gridmodel.py | 17 ++++++++++++++ .../templates/nossas/plugins/column.html | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 app/nossas/plugins/migrations/0035_grid_grid_layout_mobile_and_more.py diff --git a/app/nossas/plugins/migrations/0035_grid_grid_layout_mobile_and_more.py b/app/nossas/plugins/migrations/0035_grid_grid_layout_mobile_and_more.py new file mode 100644 index 00000000..0d76ccec --- /dev/null +++ b/app/nossas/plugins/migrations/0035_grid_grid_layout_mobile_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2 on 2024-04-04 13:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('plugins', '0034_fullpageslider_background_size'), + ] + + operations = [ + migrations.AddField( + model_name='grid', + name='grid_layout_mobile', + field=models.CharField(choices=[(' ', 'Auto'), ('g-col-12', '1 Coluna'), ('g-col-12 g-col-md-6', '2 Colunas'), ('g-col-12 g-col-md-4', '3 Colunas'), ('g-col-12 g-col-md-3', '4 Colunas'), ('g-col-12 g-col-md-2', '6 Colunas'), ('g-col-12 g-col-md-1', '12 Colunas')], default='g-col-12', max_length=80, verbose_name='Layout do Grid Mobile'), + ), + migrations.AlterField( + model_name='fullpageslider', + name='background_size', + field=models.CharField(choices=[('contain', 'Contain'), ('cover', 'Cover'), ('initial', 'Initial')], default='contain', max_length=8), + ), + ] diff --git a/app/nossas/plugins/models/gridmodel.py b/app/nossas/plugins/models/gridmodel.py index fd43e5a1..6e099e4a 100644 --- a/app/nossas/plugins/models/gridmodel.py +++ b/app/nossas/plugins/models/gridmodel.py @@ -35,6 +35,13 @@ class Grid(CMSPlugin): choices=GridLayoutChoices.choices, help_text="Escolha 'Auto' para um layout responsivo automático ou para selecionar o número de colunas manualmente.", ) + grid_layout_mobile = models.CharField( + "Layout do Grid Mobile", + max_length=80, + default=GridLayoutChoices.grid_1, + choices=GridLayoutChoices.choices, + # help_text="Escolha 'Auto' para um layout responsivo automático ou para selecionar o número de colunas manualmente.", + ) grid_gap = models.CharField( "Espaçamento do Grid", default=GridSpacingChoices.gap_0, @@ -43,6 +50,16 @@ class Grid(CMSPlugin): help_text="Selecione o espaço entre colunas do Grid.", ) + @property + def responsive_sizes(self): + if self.grid_layout_mobile == 'g-col-12': + classes = self.grid_layout_mobile + else: + classes = self.grid_layout_mobile.replace("g-col-12", "").replace("-md", "") + classes += self.grid_layout.replace("g-col-12", "") + + return classes + class ColumnChoices(models.TextChoices): auto = " ", "Auto" diff --git a/app/nossas/plugins/templates/nossas/plugins/column.html b/app/nossas/plugins/templates/nossas/plugins/column.html index 362ea18b..c9b5fa31 100644 --- a/app/nossas/plugins/templates/nossas/plugins/column.html +++ b/app/nossas/plugins/templates/nossas/plugins/column.html @@ -1,6 +1,6 @@ {% load cms_tags %} -
+
{% for plugin in instance.child_plugin_instances %} {% render_plugin plugin %} {% endfor %} From 1b746363ce852972ff85618ad2d63a2d056e926e Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Mon, 8 Apr 2024 11:16:02 -0300 Subject: [PATCH 27/28] fix(nossas): filter categories to add Publication admin --- app/nossas/design/fields.py | 2 +- app/nossas/publications/forms.py | 21 +++++++-------------- app/nossas/publications/widgets.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 app/nossas/publications/widgets.py diff --git a/app/nossas/design/fields.py b/app/nossas/design/fields.py index 682b944c..af6ebaa1 100644 --- a/app/nossas/design/fields.py +++ b/app/nossas/design/fields.py @@ -81,4 +81,4 @@ class Select2PageSearchField(forms.ModelChoiceField): def __init__(self, *args, **kwargs): kwargs['queryset'] = self.widget.get_queryset() - super().__init__(*args, **kwargs) \ No newline at end of file + super().__init__(*args, **kwargs) diff --git a/app/nossas/publications/forms.py b/app/nossas/publications/forms.py index 1a726d43..cbea5b28 100644 --- a/app/nossas/publications/forms.py +++ b/app/nossas/publications/forms.py @@ -5,36 +5,29 @@ from nossas.design.fields import Select2PageSearchField from .models import Publication, PublicationList +from .widgets import Select2CategorySelectWidget class PublicationForm(forms.ModelForm): parent = Select2PageSearchField( - label=_("Página Relacionada"), + label=_("Categoria"), required=False, + widget=Select2CategorySelectWidget(), + help_text=_( + "Atualmente, apenas as categorias pré-definidas estão disponíveis. Para adicionar uma nova categoria, entre em contato com o administrador do sistema através do canal de suporte." + ), ) class Meta: model = Publication fields = "__all__" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.fields["parent"].queryset = Page.objects.drafts().on_site() - - class PublicationListForm(forms.ModelForm): category = Select2PageSearchField( - label=_("Página Relacionada"), - required=False, + label=_("Categoria"), required=False, widget=Select2CategorySelectWidget() ) class Meta: model = PublicationList fields = "__all__" - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.fields["category"].queryset = Page.objects.drafts().on_site() \ No newline at end of file diff --git a/app/nossas/publications/widgets.py b/app/nossas/publications/widgets.py new file mode 100644 index 00000000..adf1f006 --- /dev/null +++ b/app/nossas/publications/widgets.py @@ -0,0 +1,30 @@ +# from django import forms +# from django.db import models + +from cms.models import Page +from django_select2.forms import ModelSelect2Widget +from nossas.design.fields import Select2PageSearchFieldMixin + + +class Select2CategorySelectWidget(Select2PageSearchFieldMixin, ModelSelect2Widget): + site = None + + # show entries when clicking on it + def build_attrs(self, base_attrs, extra_attrs=None): + default_attrs = {"data-minimum-input-length": 0} + default_attrs.update(base_attrs) + attrs = super().build_attrs(default_attrs, extra_attrs=extra_attrs) + return attrs + + def get_queryset(self): + qs = Page.objects.drafts() + if self.site: + qs = Page.objects.drafts().on_site(self.site) + + return qs.filter(application_namespace__isnull=False).filter( + application_namespace__icontains="publications" + ) + + # we need to implement jQuery ourselves, see #180 + class Media: + js = ("https://code.jquery.com/jquery-3.5.1.slim.min.js",) From 21b2a2316b5dc609808e6a64d5c071cc09520b35 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Tue, 9 Apr 2024 14:57:37 -0300 Subject: [PATCH 28/28] fix(nossas): change help texts to grid plugin --- app/nossas/plugins/models/gridmodel.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/nossas/plugins/models/gridmodel.py b/app/nossas/plugins/models/gridmodel.py index 6e099e4a..b913dd52 100644 --- a/app/nossas/plugins/models/gridmodel.py +++ b/app/nossas/plugins/models/gridmodel.py @@ -31,23 +31,23 @@ class Grid(CMSPlugin): grid_layout = models.CharField( "Layout do Grid", max_length=80, - default=GridLayoutChoices.grid_auto, + default=GridLayoutChoices.grid_4, choices=GridLayoutChoices.choices, - help_text="Escolha 'Auto' para um layout responsivo automático ou para selecionar o número de colunas manualmente.", + help_text="Defina o número de colunas para a visualização da página.", ) grid_layout_mobile = models.CharField( "Layout do Grid Mobile", max_length=80, default=GridLayoutChoices.grid_1, choices=GridLayoutChoices.choices, - # help_text="Escolha 'Auto' para um layout responsivo automático ou para selecionar o número de colunas manualmente.", + help_text="Selecione o número de colunas para exibição da página em dispositivos móveis.", ) grid_gap = models.CharField( "Espaçamento do Grid", - default=GridSpacingChoices.gap_0, + default=GridSpacingChoices.gap_4, choices=GridSpacingChoices.choices, max_length=15, - help_text="Selecione o espaço entre colunas do Grid.", + help_text="Selecione o espaço entre linhas e colunas do Grid.", ) @property