Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8ec36ad
feat: cria MesaDiretoraCrud e ComposicaoMesaCrud sem forms e regras d…
LeandroJatai Apr 13, 2026
ef96f77
feat: cria filterset para seleção de legislatura
LeandroJatai Apr 13, 2026
3cb9bdf
feat: add field titulo
LeandroJatai Apr 13, 2026
745a59e
feat: impl tabs na listagem de mesa diretora perfil público
LeandroJatai Apr 13, 2026
86913cd
ajustes no layout publico de mesa
LeandroJatai Apr 13, 2026
9318943
ajustes de scss para telas mobiles
LeandroJatai Apr 14, 2026
8ae8641
altera link para button nas tabs de mesa diretora
LeandroJatai Apr 14, 2026
0c64cad
add forms com regras de negócio
LeandroJatai Apr 14, 2026
586e3df
add testes para mesa e composição
LeandroJatai Apr 14, 2026
f6582f8
ajustes nos testes e no form mesa diretora
LeandroJatai Apr 14, 2026
0ec9585
remove código substituído
LeandroJatai Apr 14, 2026
b89a64f
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3083470632
LeandroJatai Apr 17, 2026
8128831
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3083471759
LeandroJatai Apr 17, 2026
5d3b742
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3083757964
LeandroJatai Apr 17, 2026
ef36793
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093425814
LeandroJatai Apr 17, 2026
c228b04
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3083452572
LeandroJatai Apr 17, 2026
4161598
fix: https://github.com/interlegis/sapl/pull/3829/changes/BASE..958e8…
LeandroJatai Apr 17, 2026
962fb4f
fix: reviews
LeandroJatai Apr 17, 2026
fd734b2
fix: ajuste em subnav_mesa.yaml
LeandroJatai Apr 17, 2026
04facfb
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093455410
LeandroJatai Apr 17, 2026
286b2f6
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093434428
LeandroJatai Apr 17, 2026
024a756
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093327707
LeandroJatai Apr 17, 2026
7b5ac2d
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093438428
LeandroJatai Apr 18, 2026
d1c572e
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093737607
LeandroJatai Apr 18, 2026
4f0dc4e
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3093543652
LeandroJatai Apr 18, 2026
5af98ee
fix: ajuste de related_name
LeandroJatai Apr 18, 2026
38fa86a
fix: https://github.com/interlegis/sapl/pull/3829#discussion_r3182291955
LeandroJatai May 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/__global/scss/layouts/_globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ small {
hyphens: auto;*/
}


@media print {
a[href]:after {
content: none !important;
Expand Down
89 changes: 89 additions & 0 deletions frontend/src/__global/scss/libs/bootstrap/_nav_tabs.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@import "~bootstrap/scss/variables";

// Estilização do tab-content conectado ao nav-tabs (substitui inline style)
.nav-tabs + .tab-content {
border: 1px solid $nav-tabs-border-color;
border-top: 0;
border-radius: 0 0 $border-radius $border-radius;
}

@media (max-width: 992px) {
.nav-tabs {
position: relative;
flex-direction: column;
border: 1px solid $nav-tabs-border-color;
border-radius: $border-radius; // Totalmente arredondado — parece um botão/select
background-color: $white;
overflow: hidden; // Recorta filhos nas bordas arredondadas

// Seta indicando dropdown
&::after {
content: "▾";
position: absolute;
right: 0.75rem;
top: 0.6rem;
font-size: 1rem;
color: $secondary;
pointer-events: none;
transition: transform 0.2s ease;
}

// Oculta todos os itens por padrão
.nav-item {
display: none;
width: 100%;

.nav-link {
border: none;
border-bottom: 1px solid $nav-tabs-border-color;
border-radius: 0;
width: 100%;
text-align: left;
padding-right: 2rem;
margin-bottom: 0;

&.active {
background-color: $nav-tabs-link-active-bg;
color: $nav-tabs-link-active-color;
border-color: transparent;
}

&:hover:not(.active) {
background-color: $light;
}
}

&:last-child .nav-link {
border-bottom: none;
}
}

// CSS nativo: exibe somente o item ativo (browsers com suporte a :has)
.nav-item:has(.nav-link.active) {
display: block;
}

// Estado expandido: via :focus-within (nativo) ou .nav-tabs--open (fallback JS)
&:focus-within,
&.nav-tabs--open {
border-radius: $border-radius $border-radius 0 0; // Arredonda apenas topo quando aberto
overflow: visible;
z-index: $zindex-dropdown;

&::after {
transform: rotate(180deg);
}

.nav-item {
display: block;
}
}
}

// Em mobile, tab-content é visualmente independente do nav-tabs (que vira um select)
.nav-tabs + .tab-content {
border-top: 1px solid $nav-tabs-border-color;
border-radius: $border-radius; // Rounding completo — desconectado do nav-tabs
margin-top: 0.5rem;
}
}
1 change: 1 addition & 0 deletions frontend/src/__global/scss/libs/libs.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import "./bootstrap/nav_navbar";
@import "./bootstrap/nav_tabs";
@import "./bootstrap/table";
54 changes: 50 additions & 4 deletions sapl/parlamentares/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import timedelta
import logging

from crispy_forms.layout import Fieldset, Layout
from crispy_forms.layout import Fieldset, Layout, Field

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Import Field adicionado mas não usado em nenhum lugar do diff. fix_qa.sh (isort/autopep8) deveria limpar.

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, User
Expand All @@ -19,10 +19,10 @@
from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.rules import SAPL_GROUP_VOTANTE
from sapl.utils import FileFieldCheckMixin
from sapl.utils import FileFieldCheckMixin, SelectSubmitChangeWidget

from .models import (Coligacao, ComposicaoColigacao, Filiacao, Frente, Legislatura,
Mandato, Parlamentar, Partido, Votante, Bloco, FrenteParlamentar, BlocoMembro)
from .models import (Coligacao, ComposicaoColigacao, ComposicaoMesa, Filiacao, Frente, Legislatura,
Mandato, MesaDiretora, Parlamentar, Partido, Votante, Bloco, FrenteParlamentar, BlocoMembro)


class CustomImageCropWidget(ImageCropWidget):
Expand Down Expand Up @@ -752,3 +752,49 @@ def clean(self):
_("Parlamentar já é membro do bloco parlamentar."))

return cd

class MesaDiretoraFilterSet(django_filters.FilterSet):

legislatura = django_filters.ModelChoiceFilter(
label='',
queryset=Legislatura.objects.all(),
widget=SelectSubmitChangeWidget)

class Meta:
model = MesaDiretora
fields = ['legislatura']

def __init__(self, *args, **kwargs):
super(MesaDiretoraFilterSet, self).__init__(*args, **kwargs)

row0 = to_row([('legislatura', 5)])

self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Escolha da Legislatura'),
row0,)
)


class MesaDiretoraForm(ModelForm):

class Meta:
model = MesaDiretora
fields = '__all__'


class ComposicaoMesaForm(ModelForm):

class Meta:
model = ComposicaoMesa
fields = (
'parlamentar',
'cargo'
)

def __init__(self, *args, **kwargs):
super(ComposicaoMesaForm, self).__init__(*args, **kwargs)
self.instance.mesa_diretora = self.initial.get('mesa_diretora')
self.fields['parlamentar'].queryset = self.fields['parlamentar'].queryset.filter(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ComposicaoMesaForm.__init__ quebra silenciosamente sem initial

Se o form for instanciado sem initial['mesa_diretora'], self.initial.get(...) retorna None e a próxima linha lança AttributeError: 'NoneType' object has no attribute 'legislatura'.

Hoje as views CRUD sempre passam initial, então não dispara em produção, mas qualquer caller futuro (admin, comando, outra view) quebra silenciosamente. Sugestão de guarda:

mesa = self.initial.get('mesa_diretora')
if mesa is not None:
    self.instance.mesa_diretora = mesa
    self.fields['parlamentar'].queryset = (
        self.fields['parlamentar'].queryset
        .filter(mandato__legislatura=mesa.legislatura)
    )

mandato__legislatura=self.initial.get('mesa_diretora').legislatura)
34 changes: 34 additions & 0 deletions sapl/parlamentares/migrations/0046_mesadiretora_legislatura.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 2.2.28 on 2026-04-13 01:48

from django.db import migrations, models
import django.db.models.deletion
from datetime import date

def add_legislatura_to_mesa_diretora(apps, schema_editor):
schema_editor.execute("""
UPDATE parlamentares_mesadiretora md
SET
legislatura_id = sl.legislatura_id,
data_inicio = sl.data_inicio,
data_fim = sl.data_fim
FROM
parlamentares_sessaolegislativa sl
WHERE
sl.id = md.sessao_legislativa_id
""")


class Migration(migrations.Migration):

dependencies = [
('parlamentares', '0045_auto_20251201_1531'),
]

operations = [
migrations.AddField(
model_name='mesadiretora',
name='legislatura',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Legislatura', verbose_name='Legislatura'),
),
migrations.RunPython(add_legislatura_to_mesa_diretora),
]
23 changes: 23 additions & 0 deletions sapl/parlamentares/migrations/0047_auto_20260412_2256.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 2.2.28 on 2026-04-13 01:56

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('parlamentares', '0046_mesadiretora_legislatura'),
]

operations = [
migrations.AlterField(
model_name='mesadiretora',
name='legislatura',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Legislatura', verbose_name='Legislatura'),
),
migrations.RemoveField(
model_name='mesadiretora',
name='sessao_legislativa',
),
]
42 changes: 42 additions & 0 deletions sapl/parlamentares/migrations/0048_auto_20260413_1049.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 2.2.28 on 2026-04-13 13:49

from django.db import migrations, models
import django.db.models.deletion

def preencher_titulo_mesa_diretora(apps, schema_editor):
schema_editor.execute("""
UPDATE parlamentares_mesadiretora
SET titulo = 'Mesa Diretora' ||
CASE WHEN EXTRACT(YEAR FROM data_fim)::integer - EXTRACT(YEAR FROM data_inicio)::integer = 1
THEN ' Biênio'
ELSE ''
END || ' ' ||
EXTRACT(YEAR FROM data_inicio)::integer::text || '/' ||
EXTRACT(YEAR FROM data_fim)::integer::text
WHERE data_inicio IS NOT NULL AND data_fim IS NOT NULL
""")


class Migration(migrations.Migration):

dependencies = [
('parlamentares', '0047_auto_20260412_2256'),
]

operations = [
migrations.AlterModelOptions(
name='mesadiretora',
options={'ordering': ('-legislatura', '-data_inicio'), 'verbose_name': 'Mesa Diretora', 'verbose_name_plural': 'Mesas Diretoras'},
),
migrations.AddField(
model_name='mesadiretora',
name='titulo',
field=models.CharField(default='', max_length=100, verbose_name='Título da Mesa Diretora'),
),
migrations.AlterField(
model_name='composicaomesa',
name='mesa_diretora',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='composicaomesa_set', to='parlamentares.MesaDiretora'),
),
migrations.RunPython(preencher_titulo_mesa_diretora),
]
39 changes: 39 additions & 0 deletions sapl/parlamentares/migrations/0049_auto_20260417_1917.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Generated by Django 2.2.28 on 2026-04-17 22:17

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('parlamentares', '0048_auto_20260413_1049'),
]

operations = [
migrations.AlterField(
model_name='composicaomesa',
name='cargo',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='parlamentares.CargoMesa', verbose_name='Cargo'),
),
migrations.AlterField(
model_name='composicaomesa',
name='mesa_diretora',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='composicaomesa_set', to='parlamentares.MesaDiretora', verbose_name='Mesa Diretora'),
),
migrations.AlterField(
model_name='composicaomesa',
name='parlamentar',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Parlamentar', verbose_name='Parlamentar'),
),
migrations.AlterField(
model_name='mesadiretora',
name='data_fim',
field=models.DateField(verbose_name='Data Fim'),
),
migrations.AlterField(
model_name='mesadiretora',
name='data_inicio',
field=models.DateField(verbose_name='Data Início'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Migration pode falhar em bases legadas

O modelo antigo permitia data_inicio/data_fim nulos em MesaDiretora (null=True). Fluxo atual:

  • 0046 popula essas datas a partir de sessao_legislativa, mas só replica os valores que a sessão tinha.
  • 0048 popula titulo apenas onde data_inicio IS NOT NULL AND data_fim IS NOT NULL.
  • 0049 (esta migration) aplica NOT NULL em ambas.

Se houver MesaDiretora cuja sessão não tinha datas (ou o join falhar), 0049 quebra com IntegrityError no upgrade. Como as bases dos clientes são bem heterogêneas, sugiro:

  1. Em 0046 (ou nova migration intermediária), preencher fallback antes do AlterField para NOT NULL — por exemplo, copiar das datas da legislatura quando a sessão não tiver.
  2. Rodar a migration contra alguns dumps reais antes do release.

),
]
19 changes: 19 additions & 0 deletions sapl/parlamentares/migrations/0050_auto_20260417_2235.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2026-04-18 01:35

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('parlamentares', '0049_auto_20260417_1917'),
]

operations = [
migrations.AlterField(
model_name='mesadiretora',
name='legislatura',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='mesadiretora_set', to='parlamentares.Legislatura', verbose_name='Legislatura'),
),
]
Loading