From 891e1fb731dd81f44d78dbf6f44c7ab2fbfa3ac4 Mon Sep 17 00:00:00 2001 From: Mario Sergio Date: Wed, 14 Aug 2024 13:49:29 -0300 Subject: [PATCH 01/19] feat(votepeloclima): Created candidatures search filters view --- .../votepeloclima/candidature/choices.py | 10 +-- .../candidature/candidature_search.html | 78 +++++++++++++++++++ .../votepeloclima/candidature/views.py | 69 +++++++++++++++- app/org_eleicoes/votepeloclima/urls.py | 3 +- 4 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html diff --git a/app/org_eleicoes/votepeloclima/candidature/choices.py b/app/org_eleicoes/votepeloclima/candidature/choices.py index 72610806..f8a2fcd0 100644 --- a/app/org_eleicoes/votepeloclima/candidature/choices.py +++ b/app/org_eleicoes/votepeloclima/candidature/choices.py @@ -16,13 +16,13 @@ class CandidatureFlowStatus(models.TextChoices): class IntendedPosition(models.TextChoices): - empty = "", "Selecione uma opção" + empty = "", "Cargo" prefeitura = "prefeitura", "Prefeitura" vereacao = "vereacao", "Vereação" class PoliticalParty(models.TextChoices): - empty = "", "Selecione seu partido" + empty = "", "Partido político" pdt = "pdt", "PDT" novo = "novo", "Novo" psdb= "psdb", "PSDB" @@ -58,7 +58,7 @@ class PoliticalParty(models.TextChoices): class Gender(models.TextChoices): - empty = "", "Selecione uma opção" + empty = "", "Gênero" mulher_cis = "mulher_cis", "Mulher cis" mulher_trans = "mulher_trans", "Mulher trans" homem_cis = "homem_cis", "Homem cis" @@ -71,7 +71,7 @@ class Gender(models.TextChoices): class Sexuality(models.TextChoices): - empty = "", "Selecione uma opção" + empty = "", "Sexualidade" heterossexual = "heterossexual", "Heterossexual" pansexual = "pansexual", "Pansexual" assexual = "assexual", "Assexual" @@ -83,7 +83,7 @@ class Sexuality(models.TextChoices): class Color(models.TextChoices): - empty = "", "Selecione uma opção" + empty = "", "Raça" preta = "preta", "Preta" parda = "parda", "Parda" indigena = "indigena", "Indígena" diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html new file mode 100644 index 00000000..5ae02301 --- /dev/null +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -0,0 +1,78 @@ +{% load crispy_forms_filters %} + +
+ + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/views.py b/app/org_eleicoes/votepeloclima/candidature/views.py index 8aec9c79..ade1fc23 100644 --- a/app/org_eleicoes/votepeloclima/candidature/views.py +++ b/app/org_eleicoes/votepeloclima/candidature/views.py @@ -6,7 +6,7 @@ from django.http import JsonResponse from django.views import View from django.contrib.auth.models import User -from django.views.generic import TemplateView +from django.views.generic import TemplateView, ListView from django.http import HttpResponseForbidden from django.urls import reverse_lazy, reverse from django.core.files.storage import default_storage @@ -16,7 +16,14 @@ from contrib.oauth.utils import send_confirmation_email from .models import CandidatureFlow, CandidatureFlowStatus, Candidature from .forms import register_form_list, ProposeForm, AppointmentForm -from .locations_utils import get_choices +from .locations_utils import get_ufs, get_choices +from .choices import ( + PoliticalParty, + IntendedPosition, + Color, + Gender, + Sexuality, +) initial_step_name = register_form_list[2][0] @@ -342,3 +349,61 @@ def get(self, request, *args, **kwargs): return JsonResponse( [{"code": code, "name": name} for code, name in cities], safe=False ) + + +class CandidatureSearchView(ListView): + model = Candidature + template_name = "candidature/candidature_search.html" + context_object_name = "candidatures" + + def get_queryset(self): + queryset = super().get_queryset() + + ballot_name = self.request.GET.get('ballot_name') + if ballot_name: + queryset = queryset.filter(ballot_name__icontains=ballot_name) + + intended_position = self.request.GET.get('intended_position') + if intended_position: + queryset = queryset.filter(intended_position__icontains=intended_position) + + political_party = self.request.GET.get('political_party') + if political_party: + queryset = queryset.filter(political_party__icontains=political_party) + + state = self.request.GET.get('state') + if state: + queryset = queryset.filter(state__icontains=state) + + city = self.request.GET.get('city') + if city: + queryset = queryset.filter(city__icontains=city) + + gender = self.request.GET.get('gender') + if gender: + queryset = queryset.filter(gender__icontains=gender) + + color = self.request.GET.get('color') + if color: + queryset = queryset.filter(color__icontains=color) + + sexuality = self.request.GET.get('sexuality') + if sexuality: + queryset = queryset.filter(sexuality__icontains=sexuality) + + return queryset + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['sexuality_choices'] = Sexuality.choices + context['gender_choices'] = Gender.choices + context['color_choices'] = Color.choices + context['intended_position_choices'] = IntendedPosition.choices + context['political_party_choices'] = PoliticalParty.choices + context['states'] = get_ufs() + selected_state = self.request.GET.get('state') + if selected_state: + context['cities'] = get_choices(selected_state) + else: + context['cities'] = [] + return context diff --git a/app/org_eleicoes/votepeloclima/urls.py b/app/org_eleicoes/votepeloclima/urls.py index d64520a9..81b7d10a 100644 --- a/app/org_eleicoes/votepeloclima/urls.py +++ b/app/org_eleicoes/votepeloclima/urls.py @@ -20,7 +20,7 @@ from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.urls import path, include, re_path -from .candidature.views import RegisterView, EditRegisterView, DashboardView, AddressView +from .candidature.views import RegisterView, EditRegisterView, DashboardView, AddressView, CandidatureSearchView register_view = RegisterView.as_view(url_name="register_step", done_step_name="concluir") register_edit_view = EditRegisterView.as_view(url_name="register_edit_step", done_step_name="concluir") @@ -40,6 +40,7 @@ ), path("register/", register_view, name="register"), path("area-restrita/", DashboardView.as_view(), name="dashboard"), + path('candidaturas/busca/', CandidatureSearchView.as_view(), name='candidature_search'), path("admin/", admin.site.urls), path("select2/", include("django_select2.urls")), path('address/', AddressView.as_view(), name='address'), From 9fd72538d3fd73c14866d7f80ec884b4c5ec573a Mon Sep 17 00:00:00 2001 From: Mario Sergio Date: Thu, 15 Aug 2024 18:21:56 -0300 Subject: [PATCH 02/19] feat(votepeloclima): Implemented keyword and is_colletive_mandate filters --- .../votepeloclima/candidature/choices.py | 1 - .../templates/candidature/candidature_search.html | 3 +++ .../votepeloclima/candidature/views.py | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/org_eleicoes/votepeloclima/candidature/choices.py b/app/org_eleicoes/votepeloclima/candidature/choices.py index f8a2fcd0..45e83817 100644 --- a/app/org_eleicoes/votepeloclima/candidature/choices.py +++ b/app/org_eleicoes/votepeloclima/candidature/choices.py @@ -66,7 +66,6 @@ class Gender(models.TextChoices): pessoa_nao_binaria = "pessoa_nao_binaria", "Pessoa não binária" travesti = "travesti", "Travesti" queer = "queer", "Queer" - mandato_coletivo = "mandato_coletivo", "Mandato Coletivo" nao_declarado = "nao_declarado", "Não declarado" diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 5ae02301..24888f2c 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -2,6 +2,9 @@
+ + + - - - - - - - + {% for value, label in intended_position_choices %} + {% endfor %} - + {% for value, label in political_party_choices %} + {% endfor %} + +
- +{% if initial_search == 'true' %} + +{% endif %} + + +{% for candidature in candidatures %} +

{{ candidature.legal_name }} - {{ candidature.intended_position }} - {{ candidature.political_party }}

+

{{ candidature.short_description }}

+ {% if candidature.is_collective_mandate %} +

Candidatura Coletiva

+ {% endif %} +{% endfor %} \ No newline at end of file + +{% endblock %} \ No newline at end of file From f8acec44b05a416219e3f53fa78bedf4cf322c66 Mon Sep 17 00:00:00 2001 From: Mario Sergio Date: Tue, 20 Aug 2024 11:10:59 -0300 Subject: [PATCH 05/19] feat(votepeloclima): Update secundary forms --- .../candidature/candidature_search.html | 216 +++++++++++++----- app/org_eleicoes/votepeloclima/urls.py | 2 +- 2 files changed, 156 insertions(+), 62 deletions(-) diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 44458d2f..61c69faf 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -1,71 +1,121 @@ {% load crispy_forms_filters %} -
- - - - +

Conheça candidaturas da sua cidade

+Praesent sit amet turpis vel lacus volutpat scelerisque. Vivamus dapibus, nisi at feugiat tempor, erat lorem lacinia nulla, at consequat massa dolor id odio. - - -
+
+
+
+ + + + + + + +
+
+
{% if initial_search == 'true' %} - + +
+ {% for candidature in candidatures %} +

{{ candidature.legal_name }} - {{ candidature.intended_position }} - {{ candidature.political_party }}

+

{{ candidature.short_description }}

+ {% if candidature.is_collective_mandate %} +

Candidatura Coletiva

+ {% endif %} + {% endfor %} +
+ + + +{% endif %} \ No newline at end of file + + // Adiciona eventos de clique aos botões de filtro + document.querySelectorAll('.filter-button').forEach((button) => { + button.addEventListener('click', function () { + // Remove a classe 'selected' de todos os botões do mesmo grupo + let name = this.getAttribute('data-name'); + document.querySelectorAll('.button-group[data-name="'+name+'"] .filter-button').forEach((btn) => { + btn.classList.remove('selected'); + }); + + // Adiciona a classe 'selected' ao botão clicado + this.classList.add('selected'); + + // Atualiza o valor do input hidden ou cria um novo input hidden se necessário + let value = this.getAttribute('data-value'); + let input = document.querySelector('input[name="' + name + '"]'); + if (!input) { + input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + document.getElementById('sidebar-filter-form').appendChild(input); + } + input.value = value; + }); + }); + + + \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/urls.py b/app/org_eleicoes/votepeloclima/urls.py index c9501d5b..84c1e6f6 100644 --- a/app/org_eleicoes/votepeloclima/urls.py +++ b/app/org_eleicoes/votepeloclima/urls.py @@ -32,8 +32,8 @@ # Public Routers re_path(r"^candidatura/cadastro/(?P.+)/$", register_view, name="register_step",), path("candidatura/cadastro/", register_view, name="register"), + path('candidatura/busca/', CandidatureSearchView.as_view(), name='candidature_search'), re_path(r"^c(andidatura)*/(?P.+)/$", PublicCandidatureView.as_view(), name='candidate_profile'), - path('candidaturas/busca/', CandidatureSearchView.as_view(), name='candidature_search'), # API path('api/candidatura/buscar-endereco/', AddressView.as_view(), name='address'), path("api/candidatura/validar/", UpdateCandidatureStatusView.as_view()), From d3dd38e2b00b960e3629ebe91cd91d64a00b9952 Mon Sep 17 00:00:00 2001 From: Mario Sergio Date: Tue, 20 Aug 2024 12:02:23 -0300 Subject: [PATCH 06/19] feat(votepeloclima): Adjusted card candidate template --- .../templates/candidature/candidature_search.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 7b54c825..a6960919 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -36,7 +36,6 @@

Conheça candidaturas da sua cidade

- {% if initial_search == 'true' %}
-{% if initial_search == 'true' %} -
+
@@ -114,18 +43,18 @@

Conheça candidaturas da sua cidade

+{% endblock %} -{% endif %} - +{% block footer_js %} - - \ No newline at end of file +{% endblock %} diff --git a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py index 6c7d6da9..f176a809 100644 --- a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py +++ b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py @@ -17,7 +17,7 @@ from contrib.oauth.utils import send_confirmation_email from ..models import CandidatureFlow, CandidatureFlowStatus, Candidature -from ..forms import register_form_list, ProposeForm, AppointmentForm +from ..forms import CandidatureSearchForm, register_form_list, ProposeForm, AppointmentForm from ..locations_utils import get_ufs, get_choices from ..choices import ( PoliticalParty, @@ -366,20 +366,12 @@ class CandidatureSearchView(ListView): def get_queryset(self): queryset = super().get_queryset() + queryset = queryset.filter(candidatureflow__status__in=[CandidatureFlowStatus.is_valid, CandidatureFlowStatus.editing]) - # Filtros principais - for field in ['state', 'city', 'intended_position', 'political_party']: + for field in ['state', 'city', 'intended_position', 'political_party', 'gender', 'color', 'sexuality', 'ballot_name']: value = self.request.GET.get(field) if value: - queryset = queryset.filter(**{f"{field}__icontains": value}) - - # Verificar se a busca inicial foi feita - if self.request.GET.get('initial_search') == 'true': - #Filtros secundários - for field in ['gender', 'color', 'sexuality', 'ballot_name']: - value = self.request.GET.get(field) - if value: - queryset = queryset.filter(**{f"{field}__icontains": value}) + queryset = queryset.filter(**{field: value}) keyword = self.request.GET.get('keyword') if keyword: @@ -398,20 +390,15 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['sexuality_choices'] = Sexuality.choices - context['gender_choices'] = Gender.choices - context['color_choices'] = Color.choices - context['intended_position_choices'] = IntendedPosition.choices - context['political_party_choices'] = PoliticalParty.choices - - context['states'] = get_ufs() - selected_state = self.request.GET.get('state') - if selected_state: - context['cities'] = get_choices(selected_state) - else: - context['cities'] = [] - - context['initial_search'] = self.request.GET.get('initial_search', 'false') + + form = CandidatureSearchForm(self.request.GET or None) + + # Atualizar as cidades com base no estado selecionado + state = self.request.GET.get('state') + if state: + form.update_city_choices(state) + + context['form'] = form return context From f3988c7f1cf2791b2be36f11b3bf99aa0f61f06a Mon Sep 17 00:00:00 2001 From: miguelzinh3 Date: Tue, 20 Aug 2024 18:58:02 -0300 Subject: [PATCH 08/19] feat(votepeloclima): add proposesmixin view and add card styles --- .../candidature/static/css/card.css | 26 ++++++++++ .../candidature/static/css/icons.css | 9 ++++ .../candidature/candidature_search.html | 49 +++++++++++++------ .../candidature/views/__init__.py | 35 ++++++++----- 4 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 app/org_eleicoes/votepeloclima/candidature/static/css/card.css diff --git a/app/org_eleicoes/votepeloclima/candidature/static/css/card.css b/app/org_eleicoes/votepeloclima/candidature/static/css/card.css new file mode 100644 index 00000000..d001056a --- /dev/null +++ b/app/org_eleicoes/votepeloclima/candidature/static/css/card.css @@ -0,0 +1,26 @@ +.card { + transition: border-color 0.3s ease; + +} + +.card:hover { + border-color: #1F8523; +} + +.card .card-title { + transition: color 0.3s ease; + +} + +.card:hover .card-title { + color: #1F8523; +} + +.card .btn-card { + transition: background-color 0.3s ease, border-color 0.3s ease; +} + +.card:hover .btn-card { + background-color: #1F8523; + border-color: #1F8523; +} diff --git a/app/org_eleicoes/votepeloclima/candidature/static/css/icons.css b/app/org_eleicoes/votepeloclima/candidature/static/css/icons.css index 0703a8bf..c49a9a64 100644 --- a/app/org_eleicoes/votepeloclima/candidature/static/css/icons.css +++ b/app/org_eleicoes/votepeloclima/candidature/static/css/icons.css @@ -74,3 +74,12 @@ display: inline-block; background-image: url('data:image/svg+xml,') } + +.ds-icon-chevron-right { + width: 16px; + height: 16px; + background-size: contain; + background-repeat: no-repeat; + display: inline-block; + background-image: url('data:image/svg+xml,') +} \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index a6960919..3512cac2 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -1,12 +1,19 @@ {% extends "votepeloclima/base.html" %} - {% load static crispy_forms_filters %} +{% block head_css %} +{{ block.super }} + + +{% endblock %} + {% block content %} -

Conheça candidaturas da sua cidade

-Praesent sit amet turpis vel lacus volutpat scelerisque. Vivamus dapibus, nisi at feugiat tempor, erat lorem lacinia nulla, at consequat massa dolor id odio. +
+
+

Conheça candidaturas
da sua cidade

+

Praesent sit amet turpis vel lacus volutpat scelerisque. Vivamus dapibus, nisi at feugiat tempor, erat lorem lacinia nulla, at consequat massa dolor id odio. -

+
+ + + {% endfor %} +{% endfor %} \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py index f176a809..a55de1cd 100644 --- a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py +++ b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py @@ -17,7 +17,7 @@ from contrib.oauth.utils import send_confirmation_email from ..models import CandidatureFlow, CandidatureFlowStatus, Candidature -from ..forms import CandidatureSearchForm, register_form_list, ProposeForm, AppointmentForm +from ..forms import CandidatureSearchSideForm, CandidatureSearchTopForm, register_form_list, ProposeForm, AppointmentForm from ..locations_utils import get_ufs, get_choices from ..choices import ( PoliticalParty, @@ -368,10 +368,19 @@ def get_queryset(self): queryset = super().get_queryset() queryset = queryset.filter(candidatureflow__status__in=[CandidatureFlowStatus.is_valid, CandidatureFlowStatus.editing]) - for field in ['state', 'city', 'intended_position', 'political_party', 'gender', 'color', 'sexuality', 'ballot_name']: - value = self.request.GET.get(field) - if value: - queryset = queryset.filter(**{field: value}) + form_top = CandidatureSearchTopForm(self.request.GET or None) + form_side = CandidatureSearchSideForm(self.request.GET or None) + + if form_top.is_valid() and form_side.is_valid(): + cleaned_data = {**form_top.cleaned_data, **form_side.cleaned_data} + + for field in ['state', 'city', 'intended_position', 'political_party', 'gender', 'color', 'sexuality', 'ballot_name']: + values = cleaned_data.get(field) + if values: + if isinstance(values, list): + queryset = queryset.filter(**{f"{field}__in": values}) + else: + queryset = queryset.filter(**{field: values}) keyword = self.request.GET.get('keyword') if keyword: @@ -391,14 +400,16 @@ def get_queryset(self): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - form = CandidatureSearchForm(self.request.GET or None) + form_top = CandidatureSearchTopForm(self.request.GET or None) + form_side = CandidatureSearchSideForm(self.request.GET or None) # Atualizar as cidades com base no estado selecionado state = self.request.GET.get('state') if state: - form.update_city_choices(state) + form_top.update_city_choices(state) - context['form'] = form + context['form_top'] = form_top + context['form_side'] = form_side return context From 8bc1e6c8ee7346606a9ef2f030174a4fa2726837 Mon Sep 17 00:00:00 2001 From: Mario Sergio Date: Wed, 21 Aug 2024 17:26:46 -0300 Subject: [PATCH 10/19] feat(votepeloclima): created multiple select widget --- app/org_eleicoes/votepeloclima/candidature/fields.py | 5 ++++- app/org_eleicoes/votepeloclima/candidature/forms.py | 1 + app/org_eleicoes/votepeloclima/candidature/views/__init__.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/org_eleicoes/votepeloclima/candidature/fields.py b/app/org_eleicoes/votepeloclima/candidature/fields.py index 6f47591b..c0fcbd4a 100644 --- a/app/org_eleicoes/votepeloclima/candidature/fields.py +++ b/app/org_eleicoes/votepeloclima/candidature/fields.py @@ -460,10 +460,13 @@ class CheckboxSelectMultipleWidget(forms.CheckboxSelectMultiple): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + print("Widget customizado inicializado") def get_context(self, name, value, attrs): + print("Custom widget template being used") context = super().get_context(name, value, attrs) - context["widget"].update({"attrs": {**context["widget"].get("attrs", {}), "class": "btn-check"}}) + # context["widget"].update({"attrs": {**context["widget"].get("attrs", {}), "class": "btn-check"}}) + context["widget"]["optgroups"] = self.optgroups(name, context["widget"]["value"], attrs) return context \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/forms.py b/app/org_eleicoes/votepeloclima/candidature/forms.py index 2e99073b..b547aa6a 100644 --- a/app/org_eleicoes/votepeloclima/candidature/forms.py +++ b/app/org_eleicoes/votepeloclima/candidature/forms.py @@ -559,6 +559,7 @@ class CandidatureSearchSideForm(forms.Form): widget=CheckboxSelectMultipleWidget(), label='Sexualidade' ) + # proposes = forms.MultipleChoiceField( ballot_name = forms.CharField(required=False) keyword = forms.CharField(required=False) is_collective_mandate = forms.BooleanField(required=False) diff --git a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py index c4958368..0fe1ea3f 100644 --- a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py +++ b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py @@ -401,7 +401,7 @@ def get_queryset(self): queryset = queryset.filter( Q(short_description__icontains=keyword) | Q(milestones__icontains=keyword) | - Q(flags__icontains=keyword) | + Q(proposes__icontains=keyword) | Q(appointments__icontains=keyword) ) From b06d360ef858b8fe6ae05bf1f097c60511ef9ff3 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 28 Aug 2024 12:51:43 -0300 Subject: [PATCH 11/19] feat(vote): filters works to list candidatures --- .../candidature/filters/forms.py | 18 ++-- .../candidature/filters/views.py | 92 +++++++++++-------- .../votepeloclima/candidature/forms.py | 49 +--------- .../votepeloclima/candidature/models.py | 25 ++++- .../candidature/candidature_search.html | 5 +- .../candidature/views/__init__.py | 62 ------------- 6 files changed, 90 insertions(+), 161 deletions(-) diff --git a/app/org_eleicoes/votepeloclima/candidature/filters/forms.py b/app/org_eleicoes/votepeloclima/candidature/filters/forms.py index 16b234f7..4d203cd5 100644 --- a/app/org_eleicoes/votepeloclima/candidature/filters/forms.py +++ b/app/org_eleicoes/votepeloclima/candidature/filters/forms.py @@ -1,6 +1,7 @@ from django import forms from django.utils.functional import lazy +from crispy_forms.helper import FormHelper # from django_select2.forms import Select2Widget from ..choices import Gender, Color @@ -15,12 +16,16 @@ class RemoveRequiredMixin: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_tag = False + self.helper.disable_csrf = True + for field_name in self.fields: self.fields[field_name].required = False class FilterFormHeader(RemoveRequiredMixin, forms.ModelForm): - name_or_themes = forms.CharField( + keyword = forms.CharField( label="Buscar por temas ou nomes", widget=forms.TextInput(attrs={"placeholder": "Digite um tema ou nome"}), ) @@ -41,7 +46,7 @@ class Meta: "city", "intended_position", "political_party", - "name_or_themes", + "keyword", ] widgets = { # "political_party": Select2Widget() @@ -55,14 +60,13 @@ class FilterFormSidebar(RemoveRequiredMixin, forms.ModelForm): proposes = forms.MultipleChoiceField( label="Propostas", widget=forms.CheckboxSelectMultiple ) - is_collective_mandate = forms.TypedChoiceField( + mandate_type = forms.ChoiceField( label="Tipo de mandato", - coerce=lambda x: x == "True", - choices=((False, "Individual"), (True, "Mandato coletivo")), + choices=(("", "Todos"), ("individual", "Individual"), ("coletivo", "Mandato coletivo")), widget=forms.RadioSelect, ) gender = forms.MultipleChoiceField( - label="Gênero", choices=Gender.choices[1:], widget=forms.CheckboxSelectMultiple + label="Gênero", choices=Gender.choices, widget=forms.CheckboxSelectMultiple ) color = forms.MultipleChoiceField( label="Raça", choices=Color.choices[1:], widget=forms.CheckboxSelectMultiple @@ -70,7 +74,7 @@ class FilterFormSidebar(RemoveRequiredMixin, forms.ModelForm): class Meta: model = Candidature - fields = ["proposes", "is_collective_mandate", "gender", "color"] + fields = ["proposes", "mandate_type", "gender", "color"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/app/org_eleicoes/votepeloclima/candidature/filters/views.py b/app/org_eleicoes/votepeloclima/candidature/filters/views.py index 0dd130d5..8357c092 100644 --- a/app/org_eleicoes/votepeloclima/candidature/filters/views.py +++ b/app/org_eleicoes/votepeloclima/candidature/filters/views.py @@ -2,8 +2,6 @@ from django.views.generic import ListView from ..models import CandidatureFlowStatus, Candidature -# from ..forms import CandidatureSearchSideForm, CandidatureSearchTopForm - from .forms import FilterFactoryForm @@ -12,35 +10,71 @@ class CandidatureSearchView(ListView): template_name = "candidature/candidature_search.html" context_object_name = "candidatures" + search_filter_fields = [ + "legal_name", + "ballot_name", + "proposes", + "milestones", + "short_description", + ] + unique_filter_fields = ["political_party", "state", "city", "intended_position"] + multiple_filter_fields = ["gender", "color", "proposes"] + def get_queryset(self): queryset = super().get_queryset() - queryset = queryset.filter(candidatureflow__status__in=[CandidatureFlowStatus.is_valid, CandidatureFlowStatus.editing]) + # Retorna apenas Candidaturas que já tiveram status valido em algum momento do preenchimento + queryset = queryset.filter( + candidatureflow__status__in=[ + CandidatureFlowStatus.is_valid, + CandidatureFlowStatus.editing, + ] + ) + + # Filtra por valores selecionado pelo usuário form = FilterFactoryForm(data=self.request.GET or None) if form.is_valid(): cleaned_data = form.cleaned_data - for field in ['state', 'city', 'intended_position', 'political_party', 'gender', 'color', 'sexuality', 'ballot_name']: - values = cleaned_data.get(field) - if values: - if isinstance(values, list): - queryset = queryset.filter(**{f"{field}__in": values}) + # Filtros AND + for field_name in self.unique_filter_fields: + value = cleaned_data.get(field_name) + if value: + queryset = queryset.filter(**{field_name: value}) + + # Filters OR with Text search ICONTAINS + query = Q() + for field_name in self.search_filter_fields: + value = cleaned_data.get("keyword") + if value: + query |= Q(**{f"{field_name}__icontains": value}) + + queryset = queryset.filter(query) + + self.multiple_filter_fields + + # Filters OR with Multiple Choice + for field_name in self.multiple_filter_fields: + query = Q() + values = cleaned_data.get(field_name) + for value in values: + if field_name == "proposes": + query |= ~Q(**{f"{field_name}__{value}__exact": ""}) else: - queryset = queryset.filter(**{field: values}) - - keyword = self.request.GET.get('keyword') - if keyword: + query |= Q(**{f"{field_name}__exact": value}) + + # Filter is concatenate with AND by field multiple value + queryset = queryset.filter(query) + + # Filtra apenas mandato coletivo + mandate_type = self.request.GET.get("mandate_type") + if mandate_type: queryset = queryset.filter( - Q(short_description__icontains=keyword) | - Q(milestones__icontains=keyword) | - Q(proposes__icontains=keyword) | - Q(appointments__icontains=keyword) + is_collective_mandate=( + True if mandate_type == "coletivo" else False + ) ) - - is_collective_mandate = self.request.GET.get('is_collective_mandate') - if is_collective_mandate: - queryset = queryset.filter(is_collective_mandate=True) return queryset @@ -51,21 +85,3 @@ def get_context_data(self, **kwargs): context.update({"form": form}) return context - - - # form_top = CandidatureSearchTopForm(self.request.GET or None) - # form_side = CandidatureSearchSideForm(self.request.GET or None) - - # # Atualizar as cidades com base no estado selecionado - # state = self.request.GET.get('state') - # if state: - # form_top.update_city_choices(state) - - # context['form_top'] = form_top - # context['form_side'] = form_side - - # candidature = self.get_queryset().first() - # if candidature: - # context['proposes'] = self.get_proposes(candidature) - - # return context \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/forms.py b/app/org_eleicoes/votepeloclima/candidature/forms.py index c58c665a..d2a91024 100644 --- a/app/org_eleicoes/votepeloclima/candidature/forms.py +++ b/app/org_eleicoes/votepeloclima/candidature/forms.py @@ -20,7 +20,6 @@ ) from .models import CandidatureFlow from .fields import ( - CheckboxSelectMultipleWidget, ValidateOnceReCaptchaField, CheckboxTextField, InlineArrayField, @@ -601,50 +600,4 @@ class Meta: ("sobre-sua-trajetoria", TrackForm), ("complemente-seu-perfil", ProfileForm), ("checkout", CheckoutForm), -] - -class CandidatureSearchTopForm(forms.Form): - state = forms.ChoiceField(choices=[('', 'Estado')] + get_ufs(), required=False) - city = forms.ChoiceField(choices=[('', 'Cidade')], required=False) - intended_position = forms.ChoiceField(choices=IntendedPosition.choices, required=False) - political_party = forms.ChoiceField(choices=PoliticalParty.choices, required=False) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.helper = FormHelper() - self.helper.form_tag = False - - def update_city_choices(self, state): - if state: - self.fields['city'].choices = get_choices(state) - else: - self.fields['city'].choices = [('', 'Cidade')] - -class CandidatureSearchSideForm(forms.Form): - gender = forms.MultipleChoiceField( - choices=Gender.choices[1:], - required=False, - widget=CheckboxSelectMultipleWidget(), - label='Gênero' - ) - color = forms.MultipleChoiceField( - choices=Color.choices[1:], - required=False, - widget=CheckboxSelectMultipleWidget(), - label='Raça' - ) - sexuality = forms.MultipleChoiceField( - choices=Sexuality.choices[1:], - required=False, - widget=CheckboxSelectMultipleWidget(), - label='Sexualidade' - ) - # proposes = forms.MultipleChoiceField( - ballot_name = forms.CharField(required=False) - keyword = forms.CharField(required=False) - is_collective_mandate = forms.BooleanField(required=False) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.helper = FormHelper() - self.helper.form_tag = False \ No newline at end of file +] \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/models.py b/app/org_eleicoes/votepeloclima/candidature/models.py index 746a20ae..5573f57b 100644 --- a/app/org_eleicoes/votepeloclima/candidature/models.py +++ b/app/org_eleicoes/votepeloclima/candidature/models.py @@ -28,8 +28,8 @@ class Candidature(models.Model): political_party = models.CharField(verbose_name="Partido político", max_length=60, choices=PoliticalParty.choices) video = models.FileField(upload_to="candidatures/videos/", null=True, blank=True) photo = models.FileField(upload_to="candidatures/photos/", null=True, blank=True) - gender = models.CharField(max_length=30, choices=Gender.choices) - color = models.CharField(max_length=30, choices=Color.choices) + gender = models.CharField(max_length=30) + color = models.CharField(max_length=30) sexuality = models.CharField(max_length=30, null=True, blank=True, choices=Sexuality.choices) social_media = models.JSONField(blank=True, null=True, default=list) education = models.CharField(max_length=50, null=True, blank=True, choices=Education.choices) @@ -61,6 +61,27 @@ def get_state_display(self): def get_city_display(self): cities = dict(get_choices(self.state)) return cities.get(self.city, "") + + @property + def get_color_display(self): + return dict(Color.choices).get(self.color) + + @property + def get_gender_display(self): + return dict(Gender.choices).get(self.gender) + + @property + def get_proposes_display(self): + from org_eleicoes.votepeloclima.candidature.forms import ProposeForm + + form = ProposeForm() + proposes = [] + for field_name, value in self.proposes.items(): + if value: + proposes.append(form[field_name].checkbox_label) + + return proposes + def save(self, *args, **kwargs): if not self.slug: diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 55a3b0e7..c81f3a9a 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -45,10 +45,7 @@
{{ candidature.legal_name }}
{{ candidature.get_intended_position_display }} | {{ candidature.get_political_party_display }} | {{ candidature.number_id }}

- Valor 1 / Valor 2 / Valor 3 - + {{ candidature.get_proposes_display|join:" / "|escape }}

{% if candidature.is_collective_mandate %} Candidatura Coletiva diff --git a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py index 19d4e057..9468f8d2 100644 --- a/app/org_eleicoes/votepeloclima/candidature/views/__init__.py +++ b/app/org_eleicoes/votepeloclima/candidature/views/__init__.py @@ -1,10 +1,7 @@ from django.shortcuts import get_object_or_404, render from django.views import View -from django.db.models import Q -from django.views.generic import ListView from ..models import CandidatureFlowStatus, Candidature -from ..forms import CandidatureSearchSideForm, CandidatureSearchTopForm, ProposeForm from ..models import CandidatureFlowStatus, Candidature from ..forms import ProposeForm @@ -24,65 +21,6 @@ def get_proposes(self, candidature): return proposes_list -class CandidatureSearchView(ListView, ProposesMixin): - model = Candidature - template_name = "candidature/candidature_search.html" - context_object_name = "candidatures" - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.filter(candidatureflow__status__in=[CandidatureFlowStatus.is_valid, CandidatureFlowStatus.editing]) - - form_top = CandidatureSearchTopForm(self.request.GET or None) - form_side = CandidatureSearchSideForm(self.request.GET or None) - - if form_top.is_valid() and form_side.is_valid(): - cleaned_data = {**form_top.cleaned_data, **form_side.cleaned_data} - - for field in ['state', 'city', 'intended_position', 'political_party', 'gender', 'color', 'sexuality', 'ballot_name']: - values = cleaned_data.get(field) - if values: - if isinstance(values, list): - queryset = queryset.filter(**{f"{field}__in": values}) - else: - queryset = queryset.filter(**{field: values}) - - keyword = self.request.GET.get('keyword') - if keyword: - queryset = queryset.filter( - Q(short_description__icontains=keyword) | - Q(milestones__icontains=keyword) | - Q(proposes__icontains=keyword) | - Q(appointments__icontains=keyword) - ) - - is_collective_mandate = self.request.GET.get('is_collective_mandate') - if is_collective_mandate: - queryset = queryset.filter(is_collective_mandate=True) - - return queryset - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - form_top = CandidatureSearchTopForm(self.request.GET or None) - form_side = CandidatureSearchSideForm(self.request.GET or None) - - # Atualizar as cidades com base no estado selecionado - state = self.request.GET.get('state') - if state: - form_top.update_city_choices(state) - - context['form_top'] = form_top - context['form_side'] = form_side - - candidature = self.get_queryset().first() - if candidature: - context['proposes'] = self.get_proposes(candidature) - - return context - - class PublicCandidatureView(View, ProposesMixin): template_name = "candidature/candidate_profile.html" From 4f2c2461bfedfb99ba176e190648b68d09786c37 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 28 Aug 2024 13:16:34 -0300 Subject: [PATCH 12/19] fix(vote): adjusts styles --- .../static/scss/candidaturesearch.scss | 31 +++++++++++++++++++ .../candidature/candidature_search.html | 14 ++++++--- .../votepeloclima/static/scss/icons.scss | 9 ++++++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss diff --git a/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss new file mode 100644 index 00000000..749800ad --- /dev/null +++ b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss @@ -0,0 +1,31 @@ +fieldset { + legend.form-label { + color: var(--bs-primary); + text-transform: uppercase; + font-weight: 600; + font-size: 16px; + } +} + +.search-header { + // d-flex flex-column flex-md-row align-items-md-center + display: flex; + flex-direction: column; + gap: 0; + + .btn { + display: flex; + align-items: center; + padding: 8px 10px; + } + + @media (min-width: 768px) { + align-items: center; + flex-direction: row; + gap: 1rem; + } +} + +.form-check-input:checked { + background-color: var(--bs-primary); +} \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index c81f3a9a..7d527c02 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -1,9 +1,14 @@ {% extends "votepeloclima/base.html" %} -{% load static crispy_forms_filters %} +{% load static crispy_forms_filters compress %} {% block head_css %} {{ block.super }} +{% compress css %} + +{% endcompress %} {% endblock %} {% block content %} @@ -16,17 +21,16 @@

Conheça candidaturas da sua cidade

-
+
{% crispy form.header %} - +
-
diff --git a/app/org_eleicoes/votepeloclima/static/scss/icons.scss b/app/org_eleicoes/votepeloclima/static/scss/icons.scss index c8501adb..55c54018 100644 --- a/app/org_eleicoes/votepeloclima/static/scss/icons.scss +++ b/app/org_eleicoes/votepeloclima/static/scss/icons.scss @@ -255,4 +255,13 @@ background-repeat: no-repeat; display: inline-block; background-image: url('data:image/svg+xml,'); +} + +.ds-icon-search { + width: 22px; + height: 22px; + background-size: contain; + background-repeat: no-repeat; + display: inline-block; + background-image: url('data:image/svg+xml,'); } \ No newline at end of file From 0818e409a625c5dd5893bf9711f645d136dbecc6 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 28 Aug 2024 15:26:25 -0300 Subject: [PATCH 13/19] feat(vote): add custom styles to filters sidebar --- .../votepeloclima/candidature/fields.py | 16 +++++++-- .../candidature/filters/forms.py | 19 +++++++--- .../static/scss/candidaturesearch.scss | 35 +++++++++++++++---- .../candidature/candidature_search.html | 6 ++-- .../forms/widgets/button_input_option.html | 2 ++ .../forms/widgets/button_input_select.html | 5 +++ 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 app/org_eleicoes/votepeloclima/candidature/templates/forms/widgets/button_input_option.html create mode 100644 app/org_eleicoes/votepeloclima/candidature/templates/forms/widgets/button_input_select.html diff --git a/app/org_eleicoes/votepeloclima/candidature/fields.py b/app/org_eleicoes/votepeloclima/candidature/fields.py index 82171df7..046623b3 100644 --- a/app/org_eleicoes/votepeloclima/candidature/fields.py +++ b/app/org_eleicoes/votepeloclima/candidature/fields.py @@ -388,9 +388,9 @@ def get_bound_field(self, form, field_name): class ToogleButtonInput(forms.CheckboxInput): template_name = "forms/widgets/toggle_button.html" - @property - def media(self): - return forms.Media(css={"screen": ["css/icons.css"]}) + # @property + # def media(self): + # return forms.Media(css={"screen": ["css/icons.css"]}) def __init__(self, text_html, icon_name=None, *args, **kwargs): self.text_html = text_html @@ -469,3 +469,13 @@ def get_bound_field(self, form, field_name): bound_field.label = mark_safe(bound_field.label) return bound_field + + +class ButtonCheckboxSelectMultiple(forms.CheckboxSelectMultiple): + template_name = "forms/widgets/button_input_select.html" + option_template_name = "forms/widgets/button_input_option.html" + + +class ButtonRadioSelect(forms.RadioSelect): + template_name = "forms/widgets/button_input_select.html" + option_template_name = "forms/widgets/button_input_option.html" \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/filters/forms.py b/app/org_eleicoes/votepeloclima/candidature/filters/forms.py index 4d203cd5..aae778ca 100644 --- a/app/org_eleicoes/votepeloclima/candidature/filters/forms.py +++ b/app/org_eleicoes/votepeloclima/candidature/filters/forms.py @@ -1,11 +1,13 @@ from django import forms from django.utils.functional import lazy +from crispy_forms.layout import Layout, Div from crispy_forms.helper import FormHelper # from django_select2.forms import Select2Widget +from ..layout import NoCrispyField from ..choices import Gender, Color -from ..fields import CepField +from ..fields import CepField, ButtonCheckboxSelectMultiple, ButtonRadioSelect from ..forms import ProposeForm from ..locations_utils import get_ufs from ..models import Candidature @@ -58,18 +60,18 @@ class Media: class FilterFormSidebar(RemoveRequiredMixin, forms.ModelForm): proposes = forms.MultipleChoiceField( - label="Propostas", widget=forms.CheckboxSelectMultiple + label="Propostas", widget=ButtonCheckboxSelectMultiple ) mandate_type = forms.ChoiceField( label="Tipo de mandato", choices=(("", "Todos"), ("individual", "Individual"), ("coletivo", "Mandato coletivo")), - widget=forms.RadioSelect, + widget=ButtonRadioSelect, ) gender = forms.MultipleChoiceField( - label="Gênero", choices=Gender.choices, widget=forms.CheckboxSelectMultiple + label="Gênero", choices=Gender.choices[1:], widget=ButtonCheckboxSelectMultiple ) color = forms.MultipleChoiceField( - label="Raça", choices=Color.choices[1:], widget=forms.CheckboxSelectMultiple + label="Raça", choices=Color.choices[1:], widget=ButtonCheckboxSelectMultiple ) class Meta: @@ -81,6 +83,13 @@ def __init__(self, *args, **kwargs): self.fields["proposes"].choices = self.get_proposes_choices() + self.helper.layout = Layout( + NoCrispyField("proposes"), + NoCrispyField("mandate_type"), + NoCrispyField("gender"), + NoCrispyField("color"), + ) + def get_proposes_choices(self): form = ProposeForm() choices = [] diff --git a/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss index 749800ad..b400f9e8 100644 --- a/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss +++ b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss @@ -1,10 +1,8 @@ -fieldset { - legend.form-label { - color: var(--bs-primary); - text-transform: uppercase; - font-weight: 600; - font-size: 16px; - } +legend.form-label { + color: var(--bs-primary); + text-transform: uppercase; + font-weight: 600; + font-size: 16px; } .search-header { @@ -28,4 +26,27 @@ fieldset { .form-check-input:checked { background-color: var(--bs-primary); +} + +.btn-check + .btn { + padding: 8px 12px; + font-size: 14px; + + &.btn-outline-dark { + // background-color: var(--bs-gray-100); + --bs-btn-active-bg: var(--bs-primary); + --bs-btn-active-border-color: var(--bs-primary); + --bs-btn-hover-color: var(--bs-primary); + --bs-btn-hover-border-color: var(--bs-primary); + } +} + +.btn-check:not(:checked) + .btn { + &.btn-outline-dark { + &:hover { + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-bg); + border-color: var(--bs-btn-hover-border-color); + } + } } \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 7d527c02..9d44bae7 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -27,15 +27,15 @@

Conheça candidaturas da sua cidade

-
+
-
-
+
+
{% for candidature in candidatures %} {% endwith %} \ No newline at end of file From dc5401dfe45d85a739b0133d890fdf0baba5a306 Mon Sep 17 00:00:00 2001 From: Igor Santos Date: Wed, 28 Aug 2024 18:13:10 -0300 Subject: [PATCH 14/19] feat(vote): form submit on select tags candidature search --- .../candidature/static/css/card.css | 43 ------------ .../static/scss/candidaturesearch.scss | 65 +++++++++++++++++++ .../candidature/candidature_search.html | 34 ++++------ 3 files changed, 76 insertions(+), 66 deletions(-) delete mode 100644 app/org_eleicoes/votepeloclima/candidature/static/css/card.css diff --git a/app/org_eleicoes/votepeloclima/candidature/static/css/card.css b/app/org_eleicoes/votepeloclima/candidature/static/css/card.css deleted file mode 100644 index 197a085f..00000000 --- a/app/org_eleicoes/votepeloclima/candidature/static/css/card.css +++ /dev/null @@ -1,43 +0,0 @@ -.card { - transition: border-color 0.3s ease; - text-decoration: none; -} - -/* .card:hover { - border-color: #1F8523; -} */ - -.card .card-title { - transition: color 0.3s ease; - -} - -/* .card:hover .card-title { - color: #1F8523; -} */ - -.card .btn-card { - transition: background-color 0.3s ease, border-color 0.3s ease; -} - -/* .card:hover .btn-card { - background-color: #1F8523; - border-color: #1F8523; -} */ - -.card-img-top { - height: 240px; - object-fit: cover; - object-position: top; -} - -.card-footer { - display: flex; - padding: 0; -} - -.card-footer .btn { - flex-grow: 1; - border-top-left-radius: 0; - border-top-right-radius: 0; -} \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss index b400f9e8..d4ad7d9d 100644 --- a/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss +++ b/app/org_eleicoes/votepeloclima/candidature/static/scss/candidaturesearch.scss @@ -49,4 +49,69 @@ legend.form-label { border-color: var(--bs-btn-hover-border-color); } } +} + +.empty-box { + background: rgba(255,255,255,.15); + border-color: var(--bs-gray-300); + border-radius: 20px; +} + +.form-control, .form-select, .select2-container--default .select2-selection--single .select2-selection__rendered { + font-size: 14px; +} + +// Card +.card { + --bs-card-border-radius: 12px; + --bs-border-radius: 12px; + + text-decoration: none; + background-color: rgba(255,255,255, .15); + cursor: pointer; + + h6 { + font-size: 14px; + margin-bottom: 5px; + } + + .form-text { + font-size: 12px; + height: 54px; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 5px; + } + + .card-img-top { + height: 235px; + object-fit: cover; + object-position: top; + } + + .card-footer { + display: flex; + padding: 0; + } + + .card-footer .btn { + flex-grow: 1; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &:hover { + border-color: var(--bs-secondary); + color: var(--bs-secondary); + background-color: rgba(255,255,255, .40); + + .btn { + background-color: var(--bs-secondary); + border-color: var(--bs-secondary); + } + + hr { + color: var(--bs-secondary); + } + } } \ No newline at end of file diff --git a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html index 9d44bae7..198a3d11 100644 --- a/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html +++ b/app/org_eleicoes/votepeloclima/candidature/templates/candidature/candidature_search.html @@ -3,7 +3,6 @@ {% block head_css %} {{ block.super }} - {% compress css %}