wagtail-enap-designsystem 1.2.1.167__py3-none-any.whl → 1.2.1.169__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- enap_designsystem/migrations/0440_capsulaindexpage_capsulapage_alter_areaaluno_body_and_more.py +56561 -0
- enap_designsystem/models.py +513 -0
- enap_designsystem/static/enap_designsystem/blocks/capsulas.css +708 -0
- enap_designsystem/static/enap_designsystem/blocks/pages/page_search.css +6 -11
- enap_designsystem/templates/enap_designsystem/base.html +2 -0
- enap_designsystem/templates/enap_designsystem/blocks/feature_estrutura.html +22 -23
- enap_designsystem/templates/enap_designsystem/pages/capsula_index_page.html +1012 -0
- enap_designsystem/templates/enap_designsystem/pages/capsula_page.html +1164 -0
- enap_designsystem/templates/enap_designsystem/pages/page_search.html +103 -93
- enap_designsystem/templatetags/capsula_tags.py +11 -0
- {wagtail_enap_designsystem-1.2.1.167.dist-info → wagtail_enap_designsystem-1.2.1.169.dist-info}/METADATA +1 -1
- {wagtail_enap_designsystem-1.2.1.167.dist-info → wagtail_enap_designsystem-1.2.1.169.dist-info}/RECORD +15 -10
- {wagtail_enap_designsystem-1.2.1.167.dist-info → wagtail_enap_designsystem-1.2.1.169.dist-info}/WHEEL +0 -0
- {wagtail_enap_designsystem-1.2.1.167.dist-info → wagtail_enap_designsystem-1.2.1.169.dist-info}/licenses/LICENSE +0 -0
- {wagtail_enap_designsystem-1.2.1.167.dist-info → wagtail_enap_designsystem-1.2.1.169.dist-info}/top_level.txt +0 -0
enap_designsystem/models.py
CHANGED
|
@@ -91,6 +91,7 @@ from enap_designsystem.blocks import EnapBannerBlock
|
|
|
91
91
|
from enap_designsystem.blocks import FeatureImageTextBlock
|
|
92
92
|
from enap_designsystem.blocks import EnapAccordionBlock
|
|
93
93
|
from enap_designsystem.blocks.base_blocks import ButtonGroupBlock
|
|
94
|
+
from enap_designsystem.blocks.content_blocks import CardBlock
|
|
94
95
|
from enap_designsystem.blocks.base_blocks import CarouselBlock
|
|
95
96
|
from enap_designsystem.blocks import CourseIntroTopicsBlock
|
|
96
97
|
from .blocks import WhyChooseEnaptBlock
|
|
@@ -6047,3 +6048,515 @@ class FooterGenericoSnippet(ClusterableModel):
|
|
|
6047
6048
|
|
|
6048
6049
|
|
|
6049
6050
|
|
|
6051
|
+
|
|
6052
|
+
|
|
6053
|
+
# enap_designsystem/models.py
|
|
6054
|
+
from django.db import models
|
|
6055
|
+
from django import forms
|
|
6056
|
+
from wagtail.models import Page
|
|
6057
|
+
from wagtail.fields import RichTextField
|
|
6058
|
+
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
|
|
6059
|
+
|
|
6060
|
+
|
|
6061
|
+
# ============================================
|
|
6062
|
+
# PÁGINA MÃE - ÍNDICE DAS CÁPSULAS
|
|
6063
|
+
# ============================================
|
|
6064
|
+
|
|
6065
|
+
class CapsulaIndexPage(Page):
|
|
6066
|
+
"""
|
|
6067
|
+
Página mãe que lista todas as cápsulas com sistema de filtros
|
|
6068
|
+
"""
|
|
6069
|
+
template = "enap_designsystem/pages/capsula_index_page.html"
|
|
6070
|
+
|
|
6071
|
+
intro = RichTextField(
|
|
6072
|
+
blank=True,
|
|
6073
|
+
verbose_name="Texto introdutório",
|
|
6074
|
+
help_text="Texto que aparece no topo da página de listagem"
|
|
6075
|
+
)
|
|
6076
|
+
|
|
6077
|
+
content_panels = Page.content_panels + [
|
|
6078
|
+
FieldPanel('intro'),
|
|
6079
|
+
]
|
|
6080
|
+
|
|
6081
|
+
# Não restringe onde pode ser criada
|
|
6082
|
+
subpage_types = ['enap_designsystem.CapsulaPage'] # Só aceita cápsulas como filhas
|
|
6083
|
+
max_count = 1 # Só pode ter uma página de índice no site
|
|
6084
|
+
|
|
6085
|
+
class Meta:
|
|
6086
|
+
verbose_name = "Página Índice de Cápsulas"
|
|
6087
|
+
verbose_name_plural = "Páginas Índice de Cápsulas"
|
|
6088
|
+
|
|
6089
|
+
def get_context(self, request):
|
|
6090
|
+
context = super().get_context(request)
|
|
6091
|
+
|
|
6092
|
+
# Todas as cápsulas publicadas (filhas desta página)
|
|
6093
|
+
capsulas = CapsulaPage.objects.child_of(self).live().public()
|
|
6094
|
+
|
|
6095
|
+
# SOLUÇÃO: Converter para lista e filtrar em Python para SQLite
|
|
6096
|
+
capsulas_list = list(capsulas)
|
|
6097
|
+
|
|
6098
|
+
# Aplicar filtros da URL
|
|
6099
|
+
tipos_def = request.GET.getlist('tipo_deficiencia')
|
|
6100
|
+
if tipos_def:
|
|
6101
|
+
capsulas_list = [
|
|
6102
|
+
c for c in capsulas_list
|
|
6103
|
+
if c.tipos_deficiencia and any(t in c.tipos_deficiencia for t in tipos_def)
|
|
6104
|
+
]
|
|
6105
|
+
|
|
6106
|
+
formatos = request.GET.getlist('formato_acao')
|
|
6107
|
+
if formatos:
|
|
6108
|
+
capsulas_list = [
|
|
6109
|
+
c for c in capsulas_list
|
|
6110
|
+
if c.formatos_acao and any(f in c.formatos_acao for f in formatos)
|
|
6111
|
+
]
|
|
6112
|
+
|
|
6113
|
+
recursos = request.GET.getlist('tipo_recurso')
|
|
6114
|
+
if recursos:
|
|
6115
|
+
capsulas_list = [
|
|
6116
|
+
c for c in capsulas_list
|
|
6117
|
+
if c.tipos_recurso and any(r in c.tipos_recurso for r in recursos)
|
|
6118
|
+
]
|
|
6119
|
+
|
|
6120
|
+
perfis = request.GET.getlist('perfil_profissional')
|
|
6121
|
+
if perfis:
|
|
6122
|
+
capsulas_list = [
|
|
6123
|
+
c for c in capsulas_list
|
|
6124
|
+
if c.perfis_profissionais and any(p in c.perfis_profissionais for p in perfis)
|
|
6125
|
+
]
|
|
6126
|
+
|
|
6127
|
+
prioridade = request.GET.get('prioridade')
|
|
6128
|
+
if prioridade:
|
|
6129
|
+
capsulas_list = [c for c in capsulas_list if c.prioridade == prioridade]
|
|
6130
|
+
|
|
6131
|
+
context['capsulas'] = capsulas_list
|
|
6132
|
+
context['total_resultados'] = len(capsulas_list)
|
|
6133
|
+
|
|
6134
|
+
# Passar as choices para o template
|
|
6135
|
+
context['opcoes_filtros'] = {
|
|
6136
|
+
'tipos_deficiencia': CapsulaPage.TIPOS_DEFICIENCIA_CHOICES,
|
|
6137
|
+
'formatos_acao': CapsulaPage.FORMATOS_ACAO_CHOICES,
|
|
6138
|
+
'tipos_recurso': CapsulaPage.TIPOS_RECURSO_CHOICES,
|
|
6139
|
+
'perfis_profissionais': CapsulaPage.PERFIS_PROFISSIONAIS_CHOICES,
|
|
6140
|
+
}
|
|
6141
|
+
|
|
6142
|
+
# Filtros ativos
|
|
6143
|
+
context['filtros_ativos'] = {
|
|
6144
|
+
'tipo_deficiencia': tipos_def,
|
|
6145
|
+
'formato_acao': formatos,
|
|
6146
|
+
'tipo_recurso': recursos,
|
|
6147
|
+
'perfil_profissional': perfis,
|
|
6148
|
+
'prioridade': prioridade,
|
|
6149
|
+
}
|
|
6150
|
+
|
|
6151
|
+
# Calcular contadores
|
|
6152
|
+
context['contadores'] = self._calcular_contadores(capsulas)
|
|
6153
|
+
|
|
6154
|
+
return context
|
|
6155
|
+
|
|
6156
|
+
def _calcular_contadores(self, queryset):
|
|
6157
|
+
"""Calcula quantas cápsulas têm cada tag - versão compatível com SQLite"""
|
|
6158
|
+
contadores = {
|
|
6159
|
+
'tipos_deficiencia': {},
|
|
6160
|
+
'formatos_acao': {},
|
|
6161
|
+
'tipos_recurso': {},
|
|
6162
|
+
'perfis_profissionais': {},
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
# Converter queryset para lista
|
|
6166
|
+
capsulas_list = list(queryset)
|
|
6167
|
+
|
|
6168
|
+
# Tipos de Deficiência
|
|
6169
|
+
for value, label in CapsulaPage.TIPOS_DEFICIENCIA_CHOICES:
|
|
6170
|
+
count = sum(1 for c in capsulas_list if c.tipos_deficiencia and value in c.tipos_deficiencia)
|
|
6171
|
+
contadores['tipos_deficiencia'][value] = count
|
|
6172
|
+
|
|
6173
|
+
# Formatos de Ação
|
|
6174
|
+
for value, label in CapsulaPage.FORMATOS_ACAO_CHOICES:
|
|
6175
|
+
count = sum(1 for c in capsulas_list if c.formatos_acao and value in c.formatos_acao)
|
|
6176
|
+
contadores['formatos_acao'][value] = count
|
|
6177
|
+
|
|
6178
|
+
# Tipos de Recurso
|
|
6179
|
+
for value, label in CapsulaPage.TIPOS_RECURSO_CHOICES:
|
|
6180
|
+
count = sum(1 for c in capsulas_list if c.tipos_recurso and value in c.tipos_recurso)
|
|
6181
|
+
contadores['tipos_recurso'][value] = count
|
|
6182
|
+
|
|
6183
|
+
# Perfis Profissionais
|
|
6184
|
+
for value, label in CapsulaPage.PERFIS_PROFISSIONAIS_CHOICES:
|
|
6185
|
+
count = sum(1 for c in capsulas_list if c.perfis_profissionais and value in c.perfis_profissionais)
|
|
6186
|
+
contadores['perfis_profissionais'][value] = count
|
|
6187
|
+
|
|
6188
|
+
return contadores
|
|
6189
|
+
|
|
6190
|
+
|
|
6191
|
+
# ============================================
|
|
6192
|
+
# PÁGINA FILHA - CÁPSULA INDIVIDUAL
|
|
6193
|
+
# ==================from django.db import models
|
|
6194
|
+
from django import forms
|
|
6195
|
+
|
|
6196
|
+
|
|
6197
|
+
class CapsulaPage(Page):
|
|
6198
|
+
"""
|
|
6199
|
+
Página individual de cada cápsula de acessibilidade
|
|
6200
|
+
Compatível com PostgreSQL (produção) e SQLite (desenvolvimento)
|
|
6201
|
+
"""
|
|
6202
|
+
template = "enap_designsystem/pages/capsula_page.html"
|
|
6203
|
+
|
|
6204
|
+
# ==========================================
|
|
6205
|
+
# CHOICES FIXAS PARA OS FILTROS
|
|
6206
|
+
# ==========================================
|
|
6207
|
+
|
|
6208
|
+
TIPOS_DEFICIENCIA_CHOICES = [
|
|
6209
|
+
('visual', 'Visual'),
|
|
6210
|
+
('auditiva', 'Auditiva'),
|
|
6211
|
+
('motora', 'Motora'),
|
|
6212
|
+
('cognitiva', 'Cognitiva'),
|
|
6213
|
+
]
|
|
6214
|
+
|
|
6215
|
+
FORMATOS_ACAO_CHOICES = [
|
|
6216
|
+
('distancia_sincrono', 'A distância síncrono'),
|
|
6217
|
+
('distancia_assincrono', 'A distância assíncrono'),
|
|
6218
|
+
('presencial', 'Presencial'),
|
|
6219
|
+
('semipresencial', 'Semipresencial'),
|
|
6220
|
+
]
|
|
6221
|
+
|
|
6222
|
+
TIPOS_RECURSO_CHOICES = [
|
|
6223
|
+
('imagem', 'Imagem'),
|
|
6224
|
+
('video', 'Vídeo'),
|
|
6225
|
+
('tabela', 'Tabela'),
|
|
6226
|
+
('grafico', 'Gráfico'),
|
|
6227
|
+
('botao', 'Botão'),
|
|
6228
|
+
('texto', 'Texto'),
|
|
6229
|
+
('audio', 'Áudio'),
|
|
6230
|
+
('hiperlink', 'Hiperlink'),
|
|
6231
|
+
('videoconferencia', 'Videoconferência'),
|
|
6232
|
+
]
|
|
6233
|
+
|
|
6234
|
+
PERFIS_PROFISSIONAIS_CHOICES = [
|
|
6235
|
+
('designer_instrucional', 'Designer instrucional'),
|
|
6236
|
+
('designer_grafico', 'Designer gráfico'),
|
|
6237
|
+
('implementador_web', 'Implementador web'),
|
|
6238
|
+
('editor_video', 'Editor de vídeo'),
|
|
6239
|
+
('docente', 'Docente'),
|
|
6240
|
+
('curador_conteudo', 'Curador de conteúdo'),
|
|
6241
|
+
('conteudista', 'Conteudista'),
|
|
6242
|
+
]
|
|
6243
|
+
|
|
6244
|
+
PRIORIDADE_CHOICES = [
|
|
6245
|
+
('obrigatorio', 'Obrigatório'),
|
|
6246
|
+
('recomendado', 'Recomendado'),
|
|
6247
|
+
]
|
|
6248
|
+
|
|
6249
|
+
# ==========================================
|
|
6250
|
+
# CAMPOS DE CLASSIFICAÇÃO (FILTROS)
|
|
6251
|
+
# ==========================================
|
|
6252
|
+
|
|
6253
|
+
tipos_deficiencia = models.JSONField(
|
|
6254
|
+
blank=True,
|
|
6255
|
+
default=list,
|
|
6256
|
+
verbose_name="Tipos de Deficiência",
|
|
6257
|
+
help_text="Selecione um ou mais tipos"
|
|
6258
|
+
)
|
|
6259
|
+
|
|
6260
|
+
formatos_acao = models.JSONField(
|
|
6261
|
+
blank=True,
|
|
6262
|
+
default=list,
|
|
6263
|
+
verbose_name="Formatos de Ação",
|
|
6264
|
+
help_text="Selecione um ou mais formatos"
|
|
6265
|
+
)
|
|
6266
|
+
|
|
6267
|
+
tipos_recurso = models.JSONField(
|
|
6268
|
+
blank=True,
|
|
6269
|
+
default=list,
|
|
6270
|
+
verbose_name="Tipos de Recurso",
|
|
6271
|
+
help_text="Selecione um ou mais tipos"
|
|
6272
|
+
)
|
|
6273
|
+
|
|
6274
|
+
perfis_profissionais = models.JSONField(
|
|
6275
|
+
blank=True,
|
|
6276
|
+
default=list,
|
|
6277
|
+
verbose_name="Perfis Profissionais",
|
|
6278
|
+
help_text="Selecione um ou mais perfis"
|
|
6279
|
+
)
|
|
6280
|
+
|
|
6281
|
+
prioridade = models.CharField(
|
|
6282
|
+
max_length=20,
|
|
6283
|
+
choices=PRIORIDADE_CHOICES,
|
|
6284
|
+
default='recomendado',
|
|
6285
|
+
verbose_name="Prioridade"
|
|
6286
|
+
)
|
|
6287
|
+
|
|
6288
|
+
# ==========================================
|
|
6289
|
+
# CONTEÚDO DA CÁPSULA
|
|
6290
|
+
# ==========================================
|
|
6291
|
+
|
|
6292
|
+
resumo_card = models.TextField(
|
|
6293
|
+
max_length=300,
|
|
6294
|
+
blank=True,
|
|
6295
|
+
verbose_name="Resumo para o card",
|
|
6296
|
+
help_text="Texto curto para exibir na listagem (máx. 300 caracteres)"
|
|
6297
|
+
)
|
|
6298
|
+
|
|
6299
|
+
# ==========================================
|
|
6300
|
+
# 1. O QUE É? - COM IMAGEM (StreamField)
|
|
6301
|
+
# ==========================================
|
|
6302
|
+
|
|
6303
|
+
o_que_e_imagem = StreamField(
|
|
6304
|
+
[
|
|
6305
|
+
('image', ImageChooserBlock(required=False, label="Imagem principal"))
|
|
6306
|
+
],
|
|
6307
|
+
blank=True,
|
|
6308
|
+
use_json_field=True,
|
|
6309
|
+
max_num=1,
|
|
6310
|
+
verbose_name="Imagem",
|
|
6311
|
+
help_text="Imagem ilustrativa da seção 'O que é?'"
|
|
6312
|
+
)
|
|
6313
|
+
|
|
6314
|
+
o_que_e_resumo = RichTextField(
|
|
6315
|
+
verbose_name="O que é? (Resumo)",
|
|
6316
|
+
help_text="Texto curto exibido inicialmente",
|
|
6317
|
+
features=['bold', 'italic', 'link']
|
|
6318
|
+
)
|
|
6319
|
+
|
|
6320
|
+
o_que_e_detalhado = RichTextField(
|
|
6321
|
+
blank=True,
|
|
6322
|
+
verbose_name="O que é? (Detalhado)",
|
|
6323
|
+
help_text="Texto completo - aparece ao clicar em 'Leia mais'",
|
|
6324
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul']
|
|
6325
|
+
)
|
|
6326
|
+
|
|
6327
|
+
# 2. Por que é importante?
|
|
6328
|
+
por_que_importante_resumo = RichTextField(
|
|
6329
|
+
verbose_name="Por que é importante? (Resumo)",
|
|
6330
|
+
help_text="Texto curto exibido inicialmente",
|
|
6331
|
+
features=['bold', 'italic', 'link']
|
|
6332
|
+
)
|
|
6333
|
+
|
|
6334
|
+
por_que_importante_detalhado = RichTextField(
|
|
6335
|
+
blank=True,
|
|
6336
|
+
verbose_name="Por que é importante? (Detalhado)",
|
|
6337
|
+
help_text="Texto completo - aparece ao clicar em 'Leia mais'",
|
|
6338
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul']
|
|
6339
|
+
)
|
|
6340
|
+
|
|
6341
|
+
# ==========================================
|
|
6342
|
+
# 3. QUANDO UTILIZAR? - STREAMFIELD DE CARDS
|
|
6343
|
+
# ==========================================
|
|
6344
|
+
|
|
6345
|
+
quando_utilizar_resumo = RichTextField(
|
|
6346
|
+
verbose_name="Quando utilizar? (Resumo)",
|
|
6347
|
+
help_text="Texto curto exibido inicialmente",
|
|
6348
|
+
features=['bold', 'italic', 'link']
|
|
6349
|
+
)
|
|
6350
|
+
|
|
6351
|
+
quando_utilizar_cards = StreamField(
|
|
6352
|
+
[
|
|
6353
|
+
("enap_cardgrid", EnapCardGridBlock([
|
|
6354
|
+
("card", CardBlock()),
|
|
6355
|
+
]))
|
|
6356
|
+
],
|
|
6357
|
+
blank=True,
|
|
6358
|
+
use_json_field=True,
|
|
6359
|
+
verbose_name="Cards de cenários",
|
|
6360
|
+
help_text="Adicione cards explicando diferentes cenários de uso"
|
|
6361
|
+
)
|
|
6362
|
+
|
|
6363
|
+
# ==========================================
|
|
6364
|
+
# 4. COMO APLICAR - COM IMAGEM (StreamField)
|
|
6365
|
+
# ==========================================
|
|
6366
|
+
|
|
6367
|
+
como_aplicar_imagem = StreamField(
|
|
6368
|
+
[
|
|
6369
|
+
('image', ImageChooserBlock(required=False, label="Imagem do passo a passo"))
|
|
6370
|
+
],
|
|
6371
|
+
blank=True,
|
|
6372
|
+
use_json_field=True,
|
|
6373
|
+
max_num=1,
|
|
6374
|
+
verbose_name="Imagem",
|
|
6375
|
+
help_text="Imagem ilustrativa de como aplicar"
|
|
6376
|
+
)
|
|
6377
|
+
|
|
6378
|
+
como_aplicar_resumo = RichTextField(
|
|
6379
|
+
verbose_name="Como aplicar (Resumo)",
|
|
6380
|
+
help_text="Texto curto exibido inicialmente",
|
|
6381
|
+
features=['bold', 'italic', 'link']
|
|
6382
|
+
)
|
|
6383
|
+
|
|
6384
|
+
como_aplicar_detalhado = RichTextField(
|
|
6385
|
+
blank=True,
|
|
6386
|
+
verbose_name="Como aplicar (Detalhado)",
|
|
6387
|
+
help_text="Texto completo - aparece ao clicar em 'Leia mais'",
|
|
6388
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'code']
|
|
6389
|
+
)
|
|
6390
|
+
|
|
6391
|
+
# ==========================================
|
|
6392
|
+
# 5. MÉTODO DE VERIFICAÇÃO - STREAMFIELD DE CARDS
|
|
6393
|
+
# ==========================================
|
|
6394
|
+
|
|
6395
|
+
metodo_verificacao_resumo = RichTextField(
|
|
6396
|
+
verbose_name="Método de verificação (Resumo)",
|
|
6397
|
+
help_text="Texto curto exibido inicialmente",
|
|
6398
|
+
features=['bold', 'italic', 'link']
|
|
6399
|
+
)
|
|
6400
|
+
|
|
6401
|
+
metodo_verificacao_cards = StreamField(
|
|
6402
|
+
[
|
|
6403
|
+
("enap_cardgrid", EnapCardGridBlock([
|
|
6404
|
+
("card", CardBlock()),
|
|
6405
|
+
]))
|
|
6406
|
+
],
|
|
6407
|
+
blank=True,
|
|
6408
|
+
use_json_field=True,
|
|
6409
|
+
verbose_name="Cards de métodos",
|
|
6410
|
+
help_text="Adicione cards com diferentes métodos de verificação"
|
|
6411
|
+
)
|
|
6412
|
+
|
|
6413
|
+
# 6. Exemplos
|
|
6414
|
+
exemplos_resumo = RichTextField(
|
|
6415
|
+
verbose_name="Exemplos (Resumo)",
|
|
6416
|
+
help_text="Texto curto exibido inicialmente",
|
|
6417
|
+
features=['bold', 'italic', 'link']
|
|
6418
|
+
)
|
|
6419
|
+
|
|
6420
|
+
exemplos_detalhado = RichTextField(
|
|
6421
|
+
blank=True,
|
|
6422
|
+
verbose_name="Exemplos (Detalhado)",
|
|
6423
|
+
help_text="Texto completo - aparece ao clicar em 'Leia mais'",
|
|
6424
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul', 'code', 'image']
|
|
6425
|
+
)
|
|
6426
|
+
|
|
6427
|
+
# 7. O que não fazer?
|
|
6428
|
+
o_que_nao_fazer_resumo = RichTextField(
|
|
6429
|
+
verbose_name="O que não fazer? (Resumo)",
|
|
6430
|
+
help_text="Texto curto exibido inicialmente",
|
|
6431
|
+
features=['bold', 'italic', 'link']
|
|
6432
|
+
)
|
|
6433
|
+
|
|
6434
|
+
o_que_nao_fazer_detalhado = RichTextField(
|
|
6435
|
+
blank=True,
|
|
6436
|
+
verbose_name="O que não fazer? (Detalhado)",
|
|
6437
|
+
help_text="Texto completo - aparece ao clicar em 'Leia mais'",
|
|
6438
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul']
|
|
6439
|
+
)
|
|
6440
|
+
|
|
6441
|
+
# 8. Normas de referência
|
|
6442
|
+
normas_referencia = RichTextField(
|
|
6443
|
+
blank=True,
|
|
6444
|
+
verbose_name="Normas de referência",
|
|
6445
|
+
help_text="Lista de normas técnicas principais (WCAG, NBR, etc.)",
|
|
6446
|
+
features=['h3', 'h4', 'bold', 'italic', 'link', 'ol', 'ul']
|
|
6447
|
+
)
|
|
6448
|
+
|
|
6449
|
+
# Metadados
|
|
6450
|
+
destaque_home = models.BooleanField(
|
|
6451
|
+
default=False,
|
|
6452
|
+
verbose_name="Destacar na Home",
|
|
6453
|
+
help_text="Marque para exibir esta cápsula na página inicial"
|
|
6454
|
+
)
|
|
6455
|
+
|
|
6456
|
+
data_atualizacao = models.DateField(
|
|
6457
|
+
null=True,
|
|
6458
|
+
blank=True,
|
|
6459
|
+
verbose_name="Data de atualização",
|
|
6460
|
+
help_text="Data da última revisão do conteúdo"
|
|
6461
|
+
)
|
|
6462
|
+
|
|
6463
|
+
# ==========================================
|
|
6464
|
+
# PAINÉIS DO WAGTAIL ADMIN
|
|
6465
|
+
# ==========================================
|
|
6466
|
+
|
|
6467
|
+
content_panels = Page.content_panels + [
|
|
6468
|
+
|
|
6469
|
+
MultiFieldPanel([
|
|
6470
|
+
FieldPanel('resumo_card'),
|
|
6471
|
+
FieldPanel('prioridade'),
|
|
6472
|
+
FieldPanel('destaque_home'),
|
|
6473
|
+
FieldPanel('data_atualizacao'),
|
|
6474
|
+
], heading="📋 Informações Gerais", classname="collapsible"),
|
|
6475
|
+
|
|
6476
|
+
MultiFieldPanel([
|
|
6477
|
+
FieldPanel('tipos_deficiencia', widget=forms.CheckboxSelectMultiple(choices=TIPOS_DEFICIENCIA_CHOICES)),
|
|
6478
|
+
FieldPanel('formatos_acao', widget=forms.CheckboxSelectMultiple(choices=FORMATOS_ACAO_CHOICES)),
|
|
6479
|
+
FieldPanel('tipos_recurso', widget=forms.CheckboxSelectMultiple(choices=TIPOS_RECURSO_CHOICES)),
|
|
6480
|
+
FieldPanel('perfis_profissionais', widget=forms.CheckboxSelectMultiple(choices=PERFIS_PROFISSIONAIS_CHOICES)),
|
|
6481
|
+
], heading="🔖 Classificação (Tags para Filtros)", classname="collapsible"),
|
|
6482
|
+
|
|
6483
|
+
MultiFieldPanel([
|
|
6484
|
+
FieldPanel('o_que_e_imagem'),
|
|
6485
|
+
FieldPanel('o_que_e_resumo'),
|
|
6486
|
+
FieldPanel('o_que_e_detalhado'),
|
|
6487
|
+
], heading="1️⃣ O que é?", classname="collapsible collapsed"),
|
|
6488
|
+
|
|
6489
|
+
MultiFieldPanel([
|
|
6490
|
+
FieldPanel('por_que_importante_resumo'),
|
|
6491
|
+
FieldPanel('por_que_importante_detalhado'),
|
|
6492
|
+
], heading="2️⃣ Por que é importante?", classname="collapsible collapsed"),
|
|
6493
|
+
|
|
6494
|
+
MultiFieldPanel([
|
|
6495
|
+
FieldPanel('quando_utilizar_resumo'),
|
|
6496
|
+
FieldPanel('quando_utilizar_cards'),
|
|
6497
|
+
], heading="3️⃣ Quando utilizar?", classname="collapsible collapsed"),
|
|
6498
|
+
|
|
6499
|
+
MultiFieldPanel([
|
|
6500
|
+
FieldPanel('como_aplicar_imagem'),
|
|
6501
|
+
FieldPanel('como_aplicar_resumo'),
|
|
6502
|
+
FieldPanel('como_aplicar_detalhado'),
|
|
6503
|
+
], heading="4️⃣ Como aplicar", classname="collapsible collapsed"),
|
|
6504
|
+
|
|
6505
|
+
MultiFieldPanel([
|
|
6506
|
+
FieldPanel('metodo_verificacao_resumo'),
|
|
6507
|
+
FieldPanel('metodo_verificacao_cards'),
|
|
6508
|
+
], heading="5️⃣ Método de verificação", classname="collapsible collapsed"),
|
|
6509
|
+
|
|
6510
|
+
MultiFieldPanel([
|
|
6511
|
+
FieldPanel('exemplos_resumo'),
|
|
6512
|
+
FieldPanel('exemplos_detalhado'),
|
|
6513
|
+
], heading="6️⃣ Exemplos", classname="collapsible collapsed"),
|
|
6514
|
+
|
|
6515
|
+
MultiFieldPanel([
|
|
6516
|
+
FieldPanel('o_que_nao_fazer_resumo'),
|
|
6517
|
+
FieldPanel('o_que_nao_fazer_detalhado'),
|
|
6518
|
+
], heading="7️⃣ O que não fazer?", classname="collapsible collapsed"),
|
|
6519
|
+
|
|
6520
|
+
MultiFieldPanel([
|
|
6521
|
+
FieldPanel('normas_referencia'),
|
|
6522
|
+
], heading="8️⃣ Normas de referência", classname="collapsible collapsed"),
|
|
6523
|
+
]
|
|
6524
|
+
|
|
6525
|
+
# Configuração de hierarquia
|
|
6526
|
+
parent_page_types = ['enap_designsystem.CapsulaIndexPage']
|
|
6527
|
+
subpage_types = []
|
|
6528
|
+
|
|
6529
|
+
class Meta:
|
|
6530
|
+
verbose_name = "Cápsula de Acessibilidade"
|
|
6531
|
+
verbose_name_plural = "Cápsulas de Acessibilidade"
|
|
6532
|
+
|
|
6533
|
+
# ==========================================
|
|
6534
|
+
# MÉTODOS HELPERS
|
|
6535
|
+
# ==========================================
|
|
6536
|
+
|
|
6537
|
+
def get_context(self, request):
|
|
6538
|
+
context = super().get_context(request)
|
|
6539
|
+
|
|
6540
|
+
# Buscar cápsulas relacionadas (mesmos tipos de deficiência)
|
|
6541
|
+
capsulas_relacionadas = CapsulaPage.objects.live().public().exclude(id=self.id)
|
|
6542
|
+
|
|
6543
|
+
if self.tipos_deficiencia:
|
|
6544
|
+
# SOLUÇÃO: Buscar manualmente em Python ao invés de query no banco
|
|
6545
|
+
# Para SQLite, fazemos a filtragem após recuperar os dados
|
|
6546
|
+
todas_capsulas = list(capsulas_relacionadas)
|
|
6547
|
+
capsulas_filtradas = []
|
|
6548
|
+
|
|
6549
|
+
for capsula in todas_capsulas:
|
|
6550
|
+
# Verifica se há interseção entre os tipos de deficiência
|
|
6551
|
+
if capsula.tipos_deficiencia:
|
|
6552
|
+
tipos_em_comum = set(self.tipos_deficiencia) & set(capsula.tipos_deficiencia)
|
|
6553
|
+
if tipos_em_comum:
|
|
6554
|
+
capsulas_filtradas.append(capsula)
|
|
6555
|
+
|
|
6556
|
+
capsulas_relacionadas = capsulas_filtradas[:3]
|
|
6557
|
+
else:
|
|
6558
|
+
capsulas_relacionadas = list(capsulas_relacionadas)[:3]
|
|
6559
|
+
|
|
6560
|
+
context['capsulas_relacionadas'] = capsulas_relacionadas
|
|
6561
|
+
|
|
6562
|
+
return context
|