mirror of https://github.com/interlegis/sapl.git
8 changed files with 1179 additions and 245 deletions
@ -0,0 +1,262 @@ |
|||||
|
## Resumo das Alterações |
||||
|
|
||||
|
- **Home do Vereador**: Novos cards de acesso rápido |
||||
|
- **OnlyOffice**: Correções no salvamento de documentos |
||||
|
- **Listagem de Proposições**: Nova UI minimalista com status visual |
||||
|
- **Detalhes da Proposição**: Layout moderno e intuitivo |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 1. Tela Home do Vereador |
||||
|
|
||||
|
### Arquivos Alterados |
||||
|
- `sapl/templates/index.html` |
||||
|
- `sapl/base/views.py` |
||||
|
|
||||
|
### Mudanças |
||||
|
|
||||
|
**Novo card "Matérias Legislativas"** |
||||
|
- Adicionado na seção do vereador (`is_parlamentar`) |
||||
|
- Permite pesquisar projetos de lei e proposições |
||||
|
- Link para `/materia/pesquisar-materia` |
||||
|
|
||||
|
**Novo card "Minhas Matérias"** |
||||
|
- Substitui o antigo card "Fluxo de Proposições" |
||||
|
- Filtra automaticamente as matérias pelo autor logado |
||||
|
- Permite ao vereador visualizar e assinar documentos de sua autoria |
||||
|
|
||||
|
**Alteração na View** |
||||
|
- `IndexView` agora passa `autor_id` no contexto |
||||
|
- Usado para filtrar matérias por autor na pesquisa |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 2. Correções no OnlyOffice |
||||
|
|
||||
|
### Arquivos Alterados |
||||
|
- `sapl/materia/onlyoffice_views.py` |
||||
|
- `sapl/templates/materia/onlyoffice_editor.html` |
||||
|
- `sapl/materia/views.py` |
||||
|
|
||||
|
### Problema Resolvido |
||||
|
Ao tentar enviar uma proposição, aparecia o erro: |
||||
|
> "Proposição não possui nenhum tipo de Texto associado" |
||||
|
|
||||
|
Isso ocorria porque o callback do OnlyOffice não conseguia salvar o documento corretamente. |
||||
|
|
||||
|
### Soluções Implementadas |
||||
|
|
||||
|
**1. Correção no Callback** |
||||
|
```python |
||||
|
# Antes: só substituía localhost:8001 |
||||
|
if 'localhost:8001' in download_url: |
||||
|
download_url = download_url.replace('localhost:8001', 'onlyoffice:80') |
||||
|
|
||||
|
# Depois: substitui qualquer URL externa |
||||
|
download_url = re.sub(r'https?://[^/]+', 'http://onlyoffice:80', download_url) |
||||
|
``` |
||||
|
|
||||
|
**2. Botão "Salvar e Voltar" no Editor** |
||||
|
- Força o fechamento do editor (triggering callback) |
||||
|
- Aguarda 3 segundos para o callback processar |
||||
|
- Redireciona para a página de detalhes |
||||
|
|
||||
|
**3. Mensagem de Erro Melhorada** |
||||
|
- Orienta o usuário a usar o OnlyOffice |
||||
|
- Explica como salvar o documento antes de enviar |
||||
|
|
||||
|
**4. Aviso no Template de Detalhes** |
||||
|
- Alerta quando a proposição não tem texto |
||||
|
- Instrui o usuário a criar o documento |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 3. Listagem de Proposições (`/proposicao/`) |
||||
|
|
||||
|
### Arquivos Alterados |
||||
|
- `sapl/templates/materia/proposicao_list.html` |
||||
|
- `sapl/materia/views.py` |
||||
|
|
||||
|
### Nova Interface |
||||
|
|
||||
|
**Design Minimalista** |
||||
|
- Layout limpo com max-width de 900px |
||||
|
- Cards com bordas sutis ao invés de sombras pesadas |
||||
|
- Tipografia clara e espaçamento consistente |
||||
|
|
||||
|
**Header com Estatísticas** |
||||
|
``` |
||||
|
Proposições 5 total 2 em elaboração 1 enviadas |
||||
|
``` |
||||
|
|
||||
|
**Cards de Proposição** |
||||
|
Cada card mostra: |
||||
|
- Tipo (ex: Requerimento) |
||||
|
- Status com badge colorido |
||||
|
- Data de envio |
||||
|
- Ementa (truncada em 40 palavras) |
||||
|
- Alerta de devolução (se aplicável) |
||||
|
- Links: Abrir / Editar |
||||
|
|
||||
|
**Status com Cores** |
||||
|
| Status | Cor | Descrição | |
||||
|
|--------|-----|-----------| |
||||
|
| Em Elaboração | Cinza | Proposição ainda não enviada | |
||||
|
| Aguardando | Azul | Enviada, aguardando recebimento | |
||||
|
| Incorporada | Verde | Recebida pela Mesa | |
||||
|
| Devolvida | Laranja | Devolvida com justificativa | |
||||
|
| Cancelada | Cinza escuro | Proposição cancelada | |
||||
|
|
||||
|
**Paginação** |
||||
|
- 10 itens por página |
||||
|
- Navegação: Anterior / Números / Próxima |
||||
|
- Indicador "Página X de Y" |
||||
|
|
||||
|
### Código da View |
||||
|
```python |
||||
|
class ListView(Crud.ListView): |
||||
|
paginate_by = 10 |
||||
|
|
||||
|
def get_context_data(self, **kwargs): |
||||
|
# Estatísticas |
||||
|
context['stats'] = { |
||||
|
'total': qs.count(), |
||||
|
'elaboracao': qs.filter(data_envio__isnull=True).count(), |
||||
|
# ... |
||||
|
} |
||||
|
|
||||
|
# Lista processada com status |
||||
|
for obj in page_obj.object_list: |
||||
|
if obj.cancelado: |
||||
|
status = 'cancelada' |
||||
|
elif obj.data_devolucao: |
||||
|
status = 'devolvida' |
||||
|
# ... |
||||
|
|
||||
|
# Paginação |
||||
|
context['is_paginated'] = paginator.num_pages > 1 |
||||
|
context['page_obj'] = page_obj |
||||
|
context['page_range'] = make_pagination(...) |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 4. Detalhes da Proposição (`/proposicao/{id}`) |
||||
|
|
||||
|
### Arquivos Alterados |
||||
|
- `sapl/templates/materia/proposicao_detail.html` |
||||
|
|
||||
|
### Nova Interface |
||||
|
|
||||
|
**Header** |
||||
|
- Link "Voltar para lista" |
||||
|
- Título: Tipo + Número/Ano ou "Rascunho" |
||||
|
- Badge de status colorido |
||||
|
|
||||
|
**Alertas Contextuais** |
||||
|
- **Vermelho**: Proposição devolvida (mostra justificativa) |
||||
|
- **Azul**: Aguardando recebimento |
||||
|
- **Amarelo**: Documento não criado |
||||
|
|
||||
|
**Barra de Ações** |
||||
|
Muda conforme o estado da proposição: |
||||
|
|
||||
|
*Em Elaboração:* |
||||
|
- Editar Documento (OnlyOffice) |
||||
|
- Enviar Proposição (se tem texto) |
||||
|
- Editar Dados |
||||
|
- Excluir |
||||
|
|
||||
|
*Enviada:* |
||||
|
- Ver Recibo |
||||
|
- Retornar Proposição (se não recebida) |
||||
|
- Baixar Documento |
||||
|
|
||||
|
**Seções de Conteúdo** |
||||
|
|
||||
|
1. **Ementa** - Texto descritivo em destaque |
||||
|
|
||||
|
2. **Informações** - Grid com: |
||||
|
- Tipo |
||||
|
- Número |
||||
|
- Data de Envio |
||||
|
- Data de Recebimento |
||||
|
- Data de Devolução |
||||
|
- Status do Documento (Criado/Não criado) |
||||
|
|
||||
|
3. **Observação** - Se houver |
||||
|
|
||||
|
4. **Vínculos** - Links para: |
||||
|
- Matéria gerada |
||||
|
- Matéria anexadora |
||||
|
|
||||
|
5. **Info do Sistema** (só admin) - Usuário, IP, Última edição, Hash |
||||
|
|
||||
|
**Página de Acesso Negado** |
||||
|
- Ícone de cadeado |
||||
|
- Mensagem clara |
||||
|
- Botão para voltar à lista |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Fluxo do Vereador |
||||
|
|
||||
|
``` |
||||
|
1. Acessar Home |
||||
|
└── Card "Proposições" → Criar / Listar |
||||
|
|
||||
|
2. Criar Proposição |
||||
|
└── Preencher tipo e ementa |
||||
|
└── Redirecionado para OnlyOffice |
||||
|
|
||||
|
3. Editar Documento (OnlyOffice) |
||||
|
└── Escrever texto da proposição |
||||
|
└── Clicar "Salvar e Voltar" |
||||
|
|
||||
|
4. Enviar Proposição |
||||
|
└── Verificar dados |
||||
|
└── Clicar "Enviar Proposição" |
||||
|
└── Aguardar recebimento |
||||
|
|
||||
|
5. Acompanhar Status |
||||
|
└── Listagem mostra status atual |
||||
|
└── Ver detalhes para mais informações |
||||
|
└── Se devolvida, ver justificativa e corrigir |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Considerações Técnicas |
||||
|
|
||||
|
### Compatibilidade Docker |
||||
|
O OnlyOffice roda em container separado. As URLs são convertidas: |
||||
|
- Browser → OnlyOffice: `localhost:8001` |
||||
|
- SAPL → OnlyOffice: `onlyoffice:80` |
||||
|
- OnlyOffice → SAPL: `sapl-dev:8000` |
||||
|
|
||||
|
### Paginação |
||||
|
Usa a função `make_pagination` do CRUD base para gerar o range de páginas com reticências. |
||||
|
|
||||
|
### CSS |
||||
|
Estilos inline no template usando o bloco `webpack_loader_chunks_css` para evitar conflitos com o CSS global. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## Arquivos Modificados (Resumo) |
||||
|
|
||||
|
``` |
||||
|
sapl/ |
||||
|
├── base/ |
||||
|
│ └── views.py # IndexView com autor_id |
||||
|
├── materia/ |
||||
|
│ ├── views.py # ListView e mensagens de erro |
||||
|
│ └── onlyoffice_views.py # Correção do callback |
||||
|
└── templates/ |
||||
|
├── index.html # Cards do vereador |
||||
|
└── materia/ |
||||
|
├── proposicao_list.html # Nova listagem |
||||
|
├── proposicao_detail.html # Novos detalhes |
||||
|
└── onlyoffice_editor.html # Botão salvar e voltar |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
@ -1,239 +1,471 @@ |
|||||
{% extends "crud/detail.html" %} |
{% extends "base.html" %} |
||||
{% load i18n common_tags %} |
{% load i18n common_tags %} |
||||
{% load tz %} |
{% load tz %} |
||||
{% block sub_actions %} |
|
||||
|
{% block webpack_loader_chunks_css %} |
||||
{{ block.super }} |
{{ block.super }} |
||||
|
<style> |
||||
|
.prop-detail { |
||||
|
max-width: 800px; |
||||
|
margin: 0 auto; |
||||
|
padding: 20px 0; |
||||
|
} |
||||
|
|
||||
|
.prop-detail-header { |
||||
|
margin-bottom: 24px; |
||||
|
} |
||||
|
|
||||
|
.prop-back { |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
gap: 6px; |
||||
|
color: #666; |
||||
|
text-decoration: none; |
||||
|
font-size: 0.9rem; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-back:hover { |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.prop-title-row { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: flex-start; |
||||
|
gap: 16px; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
|
||||
|
.prop-title { |
||||
|
font-size: 1.1rem; |
||||
|
font-weight: 500; |
||||
|
color: #333; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
.prop-title-tipo { |
||||
|
color: #666; |
||||
|
font-weight: 400; |
||||
|
} |
||||
|
|
||||
|
.prop-status-badge { |
||||
|
padding: 6px 14px; |
||||
|
border-radius: 20px; |
||||
|
font-size: 0.8rem; |
||||
|
font-weight: 500; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.prop-status-badge.elaboracao { background: #f0f0f0; color: #666; } |
||||
|
.prop-status-badge.aguardando { background: #e3f2fd; color: #1976d2; } |
||||
|
.prop-status-badge.incorporada { background: #e8f5e9; color: #388e3c; } |
||||
|
.prop-status-badge.devolvida { background: #fff3e0; color: #f57c00; } |
||||
|
.prop-status-badge.cancelada { background: #f5f5f5; color: #999; } |
||||
|
|
||||
|
.prop-alert { |
||||
|
padding: 14px 18px; |
||||
|
border-radius: 8px; |
||||
|
margin-bottom: 20px; |
||||
|
font-size: 0.9rem; |
||||
|
} |
||||
|
|
||||
|
.prop-alert.warning { |
||||
|
background: #fff8e1; |
||||
|
border: 1px solid #ffe082; |
||||
|
color: #8d6e00; |
||||
|
} |
||||
|
|
||||
|
.prop-alert.info { |
||||
|
background: #e3f2fd; |
||||
|
border: 1px solid #90caf9; |
||||
|
color: #1565c0; |
||||
|
} |
||||
|
|
||||
|
.prop-alert.danger { |
||||
|
background: #ffebee; |
||||
|
border: 1px solid #ef9a9a; |
||||
|
color: #c62828; |
||||
|
} |
||||
|
|
||||
|
.prop-alert strong { |
||||
|
display: block; |
||||
|
margin-bottom: 4px; |
||||
|
} |
||||
|
|
||||
|
.prop-section { |
||||
|
background: #fff; |
||||
|
border: 1px solid #e5e5e5; |
||||
|
border-radius: 8px; |
||||
|
padding: 20px; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-section-title { |
||||
|
font-size: 0.75rem; |
||||
|
font-weight: 500; |
||||
|
text-transform: uppercase; |
||||
|
letter-spacing: 0.5px; |
||||
|
color: #999; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.prop-ementa { |
||||
|
font-size: 1rem; |
||||
|
line-height: 1.7; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.prop-meta { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); |
||||
|
gap: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-meta-item label { |
||||
|
display: block; |
||||
|
font-size: 0.75rem; |
||||
|
font-weight: 500; |
||||
|
text-transform: uppercase; |
||||
|
letter-spacing: 0.5px; |
||||
|
color: #999; |
||||
|
margin-bottom: 4px; |
||||
|
} |
||||
|
|
||||
|
.prop-meta-item span { |
||||
|
font-size: 0.95rem; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.prop-meta-item a { |
||||
|
color: #1976d2; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
|
||||
|
.prop-meta-item a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.prop-actions-bar { |
||||
|
display: flex; |
||||
|
gap: 12px; |
||||
|
flex-wrap: wrap; |
||||
|
padding: 16px 20px; |
||||
|
background: #fafafa; |
||||
|
border: 1px solid #e5e5e5; |
||||
|
border-radius: 8px; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-actions-bar .btn { |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
gap: 6px; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section { |
||||
|
background: #f8f9fa; |
||||
|
border: 2px dashed #dee2e6; |
||||
|
border-radius: 8px; |
||||
|
padding: 30px; |
||||
|
text-align: center; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section.has-document { |
||||
|
background: #fff; |
||||
|
border: 1px solid #e5e5e5; |
||||
|
border-style: solid; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section i { |
||||
|
font-size: 2.5rem; |
||||
|
color: #adb5bd; |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section.has-document i { |
||||
|
color: #28a745; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section h4 { |
||||
|
font-size: 1rem; |
||||
|
font-weight: 500; |
||||
|
color: #333; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.prop-document-section p { |
||||
|
font-size: 0.9rem; |
||||
|
color: #666; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-document-actions { |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
justify-content: center; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
|
||||
|
.prop-vinculo { |
||||
|
background: #f8f9fa; |
||||
|
border-radius: 6px; |
||||
|
padding: 12px 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-vinculo a { |
||||
|
color: #1976d2; |
||||
|
text-decoration: none; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.prop-vinculo a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.prop-no-access { |
||||
|
text-align: center; |
||||
|
padding: 60px 20px; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.prop-no-access i { |
||||
|
font-size: 3rem; |
||||
|
margin-bottom: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-observacao { |
||||
|
font-size: 0.9rem; |
||||
|
color: #666; |
||||
|
font-style: italic; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 640px) { |
||||
|
.prop-title-row { |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.prop-actions-bar { |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.prop-actions-bar .btn { |
||||
|
width: 100%; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
{% endblock webpack_loader_chunks_css %} |
||||
|
|
||||
|
{% block base_content %} |
||||
|
<div class="prop-detail"> |
||||
{% if user in object.autor.operadores.all %} |
{% if user in object.autor.operadores.all %} |
||||
<div class="actions btn-group btn-group-sm {%block sub_actions_pull%}{% endblock%}" role="group"> |
<!-- Header --> |
||||
{% if object.texto_articulado.exists %} |
<div class="prop-detail-header"> |
||||
<a class="btn btn-success" href="{% url 'sapl.materia:proposicao_ta' object.pk%}">{% trans "Texto Eletrônico" %}</a> |
<a href="{% url 'sapl.materia:proposicao_list' %}" class="prop-back"> |
||||
|
<i class="fa fa-arrow-left"></i> {% trans "Voltar para lista" %} |
||||
|
</a> |
||||
|
|
||||
|
<div class="prop-title-row"> |
||||
|
<h1 class="prop-title"> |
||||
|
<span class="prop-title-tipo">{{ object.tipo }} —</span> |
||||
|
{% if object.numero_proposicao %}#{{ object.numero_proposicao }}/{{ object.ano }}{% else %}{% trans "Rascunho" %}{% endif %} |
||||
|
</h1> |
||||
|
|
||||
|
{% if object.cancelado %} |
||||
|
<span class="prop-status-badge cancelada">Cancelada</span> |
||||
|
{% elif object.data_devolucao %} |
||||
|
<span class="prop-status-badge devolvida">Devolvida</span> |
||||
|
{% elif object.data_recebimento %} |
||||
|
<span class="prop-status-badge incorporada">Incorporada</span> |
||||
|
{% elif object.data_envio %} |
||||
|
<span class="prop-status-badge aguardando">Aguardando Recebimento</span> |
||||
|
{% else %} |
||||
|
<span class="prop-status-badge elaboracao">Em Elaboração</span> |
||||
{% endif %} |
{% endif %} |
||||
{% if object.texto_original %} |
</div> |
||||
<a class="btn btn-success" href="{% url 'sapl.materia:proposicao_texto' object.pk %}">{% trans "Texto Original" %}</a> |
</div> |
||||
|
|
||||
|
<!-- Alertas --> |
||||
|
{% if object.data_devolucao %} |
||||
|
<div class="prop-alert danger"> |
||||
|
<strong><i class="fa fa-exclamation-circle"></i> {% trans "Proposição Devolvida" %}</strong> |
||||
|
{{ object.justificativa_devolucao }} |
||||
|
</div> |
||||
|
{% elif object.data_envio and not object.data_recebimento %} |
||||
|
<div class="prop-alert info"> |
||||
|
<i class="fa fa-clock-o"></i> {% trans "Proposição enviada e aguardando recebimento pela Mesa Diretora." %} |
||||
|
</div> |
||||
|
{% elif not object.data_envio and not object.texto_original and not object.texto_articulado.exists %} |
||||
|
<div class="prop-alert warning"> |
||||
|
<strong><i class="fa fa-file-text-o"></i> {% trans "Documento não criado" %}</strong> |
||||
|
{% trans "Use o botão 'Editar Documento' abaixo para criar o texto da proposição." %} |
||||
|
</div> |
||||
{% endif %} |
{% endif %} |
||||
|
|
||||
|
<!-- Ações principais --> |
||||
|
<div class="prop-actions-bar"> |
||||
{% if not object.data_envio %} |
{% if not object.data_envio %} |
||||
<a class="btn btn-primary" href="{% url 'sapl.materia:onlyoffice_editor' object.pk %}"> |
<a href="{% url 'sapl.materia:onlyoffice_editor' object.pk %}" class="btn btn-primary"> |
||||
<i class="fa fa-file-word-o"></i> {% trans "Editar com OnlyOffice" %} |
<i class="fa fa-edit"></i> {% trans "Editar Documento" %} |
||||
|
</a> |
||||
|
{% if object.texto_original or object.texto_articulado.exists %} |
||||
|
<a href="{{ view.detail_url }}?action=send" class="btn btn-success"> |
||||
|
<i class="fa fa-paper-plane"></i> {% trans "Enviar Proposição" %} |
||||
</a> |
</a> |
||||
{% endif %} |
{% endif %} |
||||
</div> |
<a href="{{ view.update_url }}" class="btn btn-outline-secondary"> |
||||
{% endif %} |
<i class="fa fa-pencil"></i> {% trans "Editar Dados" %} |
||||
{% endblock sub_actions%} |
</a> |
||||
{% block editions %} |
<a href="{{ view.delete_url }}" class="btn btn-outline-danger"> |
||||
{% if user in object.autor.operadores.all %} |
<i class="fa fa-trash"></i> {% trans "Excluir" %} |
||||
{% if object.data_envio %} |
</a> |
||||
{% block editions_actions_return %} |
{% else %} |
||||
<div class="actions btn-group" role="group"> |
<a href="#" onclick="window.open('{% url 'sapl.materia:recibo-proposicao' object.pk %}','Recibo','width=1100, height=600, scrollbars=yes'); return false;" class="btn btn-outline-primary"> |
||||
<a class="btn btn-outline-primary" onclick="window.open('{% url 'sapl.materia:recibo-proposicao' object.pk %}','Recibo','width=1100, height=600, scrollbars=yes')">{% trans "Recibo de Envio" %}</a> |
<i class="fa fa-file-text-o"></i> {% trans "Ver Recibo" %} |
||||
|
</a> |
||||
{% if not object.data_recebimento %} |
{% if not object.data_recebimento %} |
||||
<a href="{% url 'sapl.materia:retornar-proposicao' object.pk %}" class="btn btn-outline-primary btn-outline-danger">{% trans 'Retornar Proposição Enviada' %}</a> |
<a href="{% url 'sapl.materia:retornar-proposicao' object.pk %}" class="btn btn-outline-warning"> |
||||
|
<i class="fa fa-undo"></i> {% trans "Retornar Proposição" %} |
||||
|
</a> |
||||
{% endif %} |
{% endif %} |
||||
</div> |
|
||||
{% endblock %} |
|
||||
{% else %} |
|
||||
{% block editions_actions_send %} |
|
||||
<div class="actions btn-group" role="group"> |
|
||||
<a href="{{ view.detail_url }}?action=send" class="btn btn-primary">{% trans 'Enviar' %}</a> |
|
||||
</div> |
|
||||
<div class="actions btn-group" role="group"> |
|
||||
<a href="{{ view.update_url }}" class="btn btn-outline-primary">{% trans 'Editar' %}</a> |
|
||||
<a href="{{ view.delete_url }}" class="btn btn-outline-primary btn-outline-danger">{% trans 'Excluir' %}</a> |
|
||||
</div> |
|
||||
{% endblock %} |
|
||||
{% endif %} |
{% endif %} |
||||
|
|
||||
|
{% if object.texto_original %} |
||||
|
<a href="{% url 'sapl.materia:proposicao_texto' object.pk %}" class="btn btn-outline-secondary" target="_blank"> |
||||
|
<i class="fa fa-download"></i> {% trans "Baixar Documento" %} |
||||
|
</a> |
||||
{% endif %} |
{% endif %} |
||||
{% endblock editions %} |
|
||||
{% block detail_content %} |
|
||||
{% if user in object.autor.operadores.all %} |
|
||||
<h2 class="legend">{% model_verbose_name 'sapl.materia.models.Proposicao' %}</h2> |
|
||||
<div class="row"> |
|
||||
<div class="col-sm-3"> |
|
||||
<div id="div_id_tipo" class="form-group"> |
|
||||
<p class="control-label">{%field_verbose_name object 'tipo'%}</p> |
|
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.tipo}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{% if object.data_devolucao %} |
|
||||
<div class="col-sm-9"> |
|
||||
<div class="alert alert-danger alert-dismissible " role="alert"> |
|
||||
<strong>{% trans "Proposição devolvida em:" %} {{ object.data_devolucao}}</strong> |
|
||||
<div >{% trans "Justificativa:" %} {{object.justificativa_devolucao}}</div> |
|
||||
</div> |
</div> |
||||
|
|
||||
|
<!-- Ementa --> |
||||
|
<div class="prop-section"> |
||||
|
<div class="prop-section-title">{% trans "Ementa" %}</div> |
||||
|
<div class="prop-ementa">{{ object.descricao }}</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Informações --> |
||||
|
<div class="prop-section"> |
||||
|
<div class="prop-section-title">{% trans "Informações" %}</div> |
||||
|
<div class="prop-meta"> |
||||
|
<div class="prop-meta-item"> |
||||
|
<label>{% trans "Tipo" %}</label> |
||||
|
<span>{{ object.tipo }}</span> |
||||
|
</div> |
||||
|
{% if object.numero_proposicao %} |
||||
|
<div class="prop-meta-item"> |
||||
|
<label>{% trans "Número" %}</label> |
||||
|
<span>{{ object.numero_proposicao }}/{{ object.ano }}</span> |
||||
</div> |
</div> |
||||
{% else %} |
{% endif %} |
||||
{% if object.data_envio %} |
{% if object.data_envio %} |
||||
<div class="col-sm-3"> |
<div class="prop-meta-item"> |
||||
<div id="div_id_data_envio" class="form-group"> |
<label>{% trans "Data de Envio" %}</label> |
||||
<p class="control-label">{%field_verbose_name object 'data_envio' %}</p> |
<span>{{ object.data_envio|date:"d/m/Y H:i" }}</span> |
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.data_envio}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% if object.data_recebimento %} |
{% if object.data_recebimento %} |
||||
<div class="col-sm-3"> |
<div class="prop-meta-item"> |
||||
<div id="div_id_data_envio" class="form-group"> |
<label>{% trans "Data de Recebimento" %}</label> |
||||
<p class="control-label">{%field_verbose_name object 'data_recebimento'%}</p> |
<span>{{ object.data_recebimento|date:"d/m/Y H:i" }}</span> |
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.data_recebimento}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{% elif object.data_envio %} |
|
||||
<div class="col-sm-6"> |
|
||||
<div class="alert alert-info alert-dismissible " role="alert"> |
|
||||
<div >{% trans "Proposição aguardando recebimento" %}</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% endif %} |
{% if object.data_devolucao %} |
||||
</div> |
<div class="prop-meta-item"> |
||||
<div class="row"> |
<label>{% trans "Data de Devolução" %}</label> |
||||
<div class="col-sm-12"> |
<span>{{ object.data_devolucao|date:"d/m/Y H:i" }}</span> |
||||
<div id="div_id_descricao" class="form-group"> |
|
||||
<p class="control-label">{%field_verbose_name object 'descricao'%}</p> |
|
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.descricao}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
{% if object.observacao %} |
|
||||
<div class="row"> |
|
||||
<div class="col-sm-12"> |
|
||||
<div id="div_id_obseracao" class="form-group"> |
|
||||
<p class="control-label">{%field_verbose_name object 'observacao'%}</p> |
|
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.observacao}}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% if object.data_envio %} |
<div class="prop-meta-item"> |
||||
<div class="row"> |
<label>{% trans "Documento" %}</label> |
||||
<div class="col-sm-12"> |
<span> |
||||
<div id="div_id_obseracao" class="form-group"> |
{% if object.texto_original %} |
||||
<p class="control-label">Enviada por</p> |
<i class="fa fa-check-circle" style="color: #28a745;"></i> {% trans "Criado" %} |
||||
<div class="controls"> |
{% elif object.texto_articulado.exists %} |
||||
<div class="form-control-static"> |
<i class="fa fa-check-circle" style="color: #28a745;"></i> {% trans "Texto Eletrônico" %} |
||||
{# TODO remover condicional apos migrar as acoes para os devidos campos#} |
|
||||
{% if proposicao.usuario_envio %} |
|
||||
{{ proposicao.usuario_envio|format_user }} |
|
||||
{% else %} |
{% else %} |
||||
{{ proposicao.user|format_user }} |
<i class="fa fa-times-circle" style="color: #dc3545;"></i> {% trans "Não criado" %} |
||||
{% endif %} |
{% endif %} |
||||
|
</span> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
</div> |
||||
</div> |
|
||||
|
<!-- Observação --> |
||||
|
{% if object.observacao %} |
||||
|
<div class="prop-section"> |
||||
|
<div class="prop-section-title">{% trans "Observação" %}</div> |
||||
|
<div class="prop-observacao">{{ object.observacao }}</div> |
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
<div class="row"> |
|
||||
|
<!-- Conteúdo Gerado / Vínculo --> |
||||
|
{% if object.conteudo_gerado_related or object.materia_de_vinculo %} |
||||
|
<div class="prop-section"> |
||||
|
<div class="prop-section-title">{% trans "Vínculos" %}</div> |
||||
{% if object.conteudo_gerado_related %} |
{% if object.conteudo_gerado_related %} |
||||
<div class="col-sm-6"> |
<div class="prop-vinculo" style="margin-bottom: 10px;"> |
||||
<h2 class="legend">{% trans "Conteúdo Gerado" %}</h2> |
<label style="font-size: 0.8rem; color: #666;">{% trans "Matéria Gerada:" %}</label> |
||||
<div id="div_id_conteudo_gerado_related" class="form-group"> |
|
||||
<div class="controls"> |
|
||||
<a href="{% url object|urldetail_content_type:object.conteudo_gerado_related object.conteudo_gerado_related.id %}"> |
<a href="{% url object|urldetail_content_type:object.conteudo_gerado_related object.conteudo_gerado_related.id %}"> |
||||
{{ object.conteudo_gerado_related }} |
{{ object.conteudo_gerado_related }} |
||||
</a> |
</a> |
||||
</div> |
</div> |
||||
</div> |
|
||||
</div> |
|
||||
{% endif %} |
{% endif %} |
||||
{% if object.materia_de_vinculo %} |
{% if object.materia_de_vinculo %} |
||||
<div class="col-sm-6"> |
<div class="prop-vinculo"> |
||||
<h2 class="legend">{% trans "Vínculo com a Matéria Legislativa" %}</h2> |
<label style="font-size: 0.8rem; color: #666;">{% trans "Matéria Anexadora:" %}</label> |
||||
<div id="div_id_materia_de_vinculo" class="form-group"> |
|
||||
<div class="controls"> |
|
||||
Matéria</br> |
|
||||
|
|
||||
<a href="{% url object.materia_de_vinculo|urldetail object.materia_de_vinculo.id %}"> |
<a href="{% url object.materia_de_vinculo|urldetail object.materia_de_vinculo.id %}"> |
||||
{{ object.materia_de_vinculo }} |
{{ object.materia_de_vinculo }} |
||||
</a> |
</a> |
||||
</br> |
|
||||
{% if object.materia_de_vinculo.autoria_set.all %} |
|
||||
Autores |
|
||||
{% for a in object.materia_de_vinculo.autoria_set.all %} |
|
||||
</br> {{a.autor}} |
|
||||
{% endfor %} |
|
||||
{% endif %} |
|
||||
</br> |
|
||||
Texto Original |
|
||||
</br> |
|
||||
{% if object.materia_de_vinculo.texto_original %} |
|
||||
<a href="{{object.materia_de_vinculo.texto_original.url}}"> |
|
||||
{{object.materia_de_vinculo.texto_original| to_str | split:"/" | get_last_item_from_list:-1}} |
|
||||
</a> |
|
||||
{% endif %} |
|
||||
</br> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% if not AppConfig.receber_recibo_proposicao %} |
|
||||
{% if object.hash_code %} |
|
||||
<div class="col-sm-12"> |
|
||||
<div id="div_id_hash_code" class="form-group"> |
|
||||
<p class="control-label">{%field_verbose_name object 'hash_code'%}</p> |
|
||||
<div class="controls"> |
|
||||
<div class="form-control-static">{{object.hash_code}}</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
</div> |
|
||||
{% endif %} |
|
||||
{% endif %} |
{% endif %} |
||||
</div> |
|
||||
|
<!-- Info para superusuário --> |
||||
{% if user.is_superuser %} |
{% if user.is_superuser %} |
||||
<div class="row"> |
<div class="prop-section" style="background: #f8f9fa;"> |
||||
|
<div class="prop-section-title">{% trans "Informações do Sistema" %}</div> |
||||
|
<div class="prop-meta"> |
||||
{% if proposicao.user %} |
{% if proposicao.user %} |
||||
<div class="col-sm-4"> |
<div class="prop-meta-item"> |
||||
<div id="div_id_user" class="form-group"> |
<label>{% trans "Usuário" %}</label> |
||||
<p class="control-label">Usuário</p> |
<span><a href="{% url 'sapl.base:user_update' proposicao.user.pk %}">{{ proposicao.user }}</a></span> |
||||
<div class="controls"> |
|
||||
<div class="form-control-static"> |
|
||||
<div class="dont-break-out"> |
|
||||
<a href="{% url 'sapl.base:user_update' proposicao.user.pk %}">{{ proposicao.user }}</a> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% if proposicao.ip %} |
{% if proposicao.ip %} |
||||
<div class="col-sm-4"> |
<div class="prop-meta-item"> |
||||
<div id="div_ip_user" class="form-group"> |
<label>{% trans "IP" %}</label> |
||||
<p class="control-label">IP</p> |
<span>{{ proposicao.ip }}</span> |
||||
<div class="controls"> |
|
||||
<div class="form-control-static"> |
|
||||
<div class="dont-break-out">{{ proposicao.ip }}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% if proposicao.ultima_edicao %} |
{% if proposicao.ultima_edicao %} |
||||
<div class="col-sm-4"> |
<div class="prop-meta-item"> |
||||
<div id="div_ud_user" class="form-group"> |
<label>{% trans "Última Edição" %}</label> |
||||
<p class="control-label">Data e Hora da Edição</p> |
<span>{{ proposicao.ultima_edicao }}</span> |
||||
<div class="controls"> |
|
||||
<div class="form-control-static"> |
|
||||
<div class="dont-break-out">{{ proposicao.ultima_edicao }}</div> |
|
||||
</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
|
{% endif %} |
||||
|
{% if object.hash_code %} |
||||
|
<div class="prop-meta-item"> |
||||
|
<label>{% trans "Hash" %}</label> |
||||
|
<span style="font-size: 0.75rem; word-break: break-all;">{{ object.hash_code }}</span> |
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
</div> |
</div> |
||||
|
</div> |
||||
{% endif %} |
{% endif %} |
||||
|
|
||||
{% else %} |
{% else %} |
||||
<br><br> |
<!-- Sem acesso --> |
||||
<div class="alert alert-danger alert-dismissible" role="alert"> |
<div class="prop-no-access"> |
||||
<h3>Você não tem acesso a essa Proposição porque não é de sua autoria.</h3> |
<i class="fa fa-lock"></i> |
||||
|
<h3>{% trans "Acesso Negado" %}</h3> |
||||
|
<p>{% trans "Você não tem permissão para visualizar esta proposição." %}</p> |
||||
|
<a href="{% url 'sapl.materia:proposicao_list' %}" class="btn btn-outline-primary"> |
||||
|
{% trans "Voltar para lista" %} |
||||
|
</a> |
||||
</div> |
</div> |
||||
{% endif %} |
{% endif %} |
||||
{% endblock detail_content %} |
</div> |
||||
|
{% endblock base_content %} |
||||
|
|||||
@ -1,12 +1,302 @@ |
|||||
{% extends "crud/list.html" %} |
{% extends "base.html" %} |
||||
{% load i18n %} |
{% load i18n common_tags crispy_forms_tags %} |
||||
|
|
||||
|
{% block webpack_loader_chunks_css %} |
||||
|
{{ block.super }} |
||||
|
<style> |
||||
|
.prop-page { |
||||
|
max-width: 900px; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.prop-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 24px; |
||||
|
padding-bottom: 16px; |
||||
|
border-bottom: 1px solid #e5e5e5; |
||||
|
} |
||||
|
|
||||
|
.prop-header h2 { |
||||
|
margin: 0; |
||||
|
font-size: 1.5rem; |
||||
|
font-weight: 500; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.prop-stats { |
||||
|
display: flex; |
||||
|
gap: 16px; |
||||
|
font-size: 0.85rem; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.prop-stats span { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 4px; |
||||
|
} |
||||
|
|
||||
|
.prop-actions { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.prop-card { |
||||
|
background: #fff; |
||||
|
border: 1px solid #e5e5e5; |
||||
|
border-radius: 8px; |
||||
|
padding: 16px 20px; |
||||
|
margin-bottom: 12px; |
||||
|
transition: border-color 0.15s; |
||||
|
} |
||||
|
|
||||
|
.prop-card:hover { |
||||
|
border-color: #ccc; |
||||
|
} |
||||
|
|
||||
|
.prop-card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.prop-card-meta { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 12px; |
||||
|
} |
||||
|
|
||||
|
.prop-tipo { |
||||
|
font-size: 0.75rem; |
||||
|
font-weight: 500; |
||||
|
color: #666; |
||||
|
text-transform: uppercase; |
||||
|
letter-spacing: 0.5px; |
||||
|
} |
||||
|
|
||||
|
.prop-status { |
||||
|
font-size: 0.75rem; |
||||
|
padding: 3px 10px; |
||||
|
border-radius: 12px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.prop-status.elaboracao { background: #f0f0f0; color: #666; } |
||||
|
.prop-status.aguardando { background: #e3f2fd; color: #1976d2; } |
||||
|
.prop-status.incorporada { background: #e8f5e9; color: #388e3c; } |
||||
|
.prop-status.devolvida { background: #fff3e0; color: #f57c00; } |
||||
|
.prop-status.cancelada { background: #f5f5f5; color: #999; } |
||||
|
|
||||
|
.prop-date { |
||||
|
font-size: 0.8rem; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.prop-ementa { |
||||
|
color: #333; |
||||
|
font-size: 0.95rem; |
||||
|
line-height: 1.5; |
||||
|
margin-bottom: 12px; |
||||
|
} |
||||
|
|
||||
|
.prop-card-footer { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.prop-info { |
||||
|
font-size: 0.8rem; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.prop-info a { |
||||
|
color: #1976d2; |
||||
|
text-decoration: none; |
||||
|
} |
||||
|
|
||||
|
.prop-info a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.prop-card-actions a { |
||||
|
font-size: 0.85rem; |
||||
|
color: #1976d2; |
||||
|
text-decoration: none; |
||||
|
margin-left: 16px; |
||||
|
} |
||||
|
|
||||
|
.prop-card-actions a:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
.prop-alert { |
||||
|
background: #fff8e1; |
||||
|
border-radius: 4px; |
||||
|
padding: 8px 12px; |
||||
|
margin-bottom: 12px; |
||||
|
font-size: 0.85rem; |
||||
|
color: #8d6e00; |
||||
|
} |
||||
|
|
||||
|
.prop-empty { |
||||
|
text-align: center; |
||||
|
padding: 60px 20px; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.prop-empty i { |
||||
|
font-size: 3rem; |
||||
|
margin-bottom: 16px; |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
|
||||
|
.prop-pagination { |
||||
|
margin-top: 24px; |
||||
|
padding-top: 16px; |
||||
|
border-top: 1px solid #e5e5e5; |
||||
|
} |
||||
|
|
||||
|
.prop-pagination-info { |
||||
|
text-align: center; |
||||
|
font-size: 0.8rem; |
||||
|
color: #999; |
||||
|
margin-top: 8px; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 640px) { |
||||
|
.prop-card-header { |
||||
|
flex-direction: column; |
||||
|
align-items: flex-start; |
||||
|
gap: 8px; |
||||
|
} |
||||
|
|
||||
|
.prop-date { |
||||
|
order: -1; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
{% endblock webpack_loader_chunks_css %} |
||||
|
|
||||
{% block base_content %} |
{% block base_content %} |
||||
{% block sub_actions %} |
<div class="prop-page"> |
||||
<div class="actions btn-group btn-group-sm" role="group"> |
<div class="prop-header"> |
||||
<a href="{% url 'sapl.materia:historico-proposicao' %}" class="btn btn-outline-primary"> |
<h2>{% trans "Proposições" %}</h2> |
||||
{% blocktrans with verbose_name=view.verbose_name %} Histórico de {{ verbose_name }} {% endblocktrans %} |
<div class="prop-stats"> |
||||
|
<span><strong>{{ stats.total|default:"0" }}</strong> total</span> |
||||
|
<span><strong>{{ stats.elaboracao|default:"0" }}</strong> em elaboração</span> |
||||
|
<span><strong>{{ stats.aguardando|default:"0" }}</strong> enviadas</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="prop-actions"> |
||||
|
{% if view.create_url %} |
||||
|
<a href="{{ view.create_url }}" class="btn btn-primary btn-sm"> |
||||
|
<i class="fa fa-plus"></i> {% trans "Nova Proposição" %} |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
<a href="{% url 'sapl.materia:historico-proposicao' %}" class="btn btn-outline-secondary btn-sm"> |
||||
|
<i class="fa fa-history"></i> {% trans "Histórico" %} |
||||
</a> |
</a> |
||||
</div> |
</div> |
||||
{% endblock sub_actions %} |
|
||||
{{ block.super }} |
{% if not proposicoes %} |
||||
|
<div class="prop-empty"> |
||||
|
<i class="fa fa-file-text-o"></i> |
||||
|
<p>{% trans "Nenhuma proposição encontrada" %}</p> |
||||
|
{% if view.create_url %} |
||||
|
<a href="{{ view.create_url }}" class="btn btn-primary"> |
||||
|
{% trans "Criar primeira proposição" %} |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
{% else %} |
||||
|
{% for prop in proposicoes %} |
||||
|
<div class="prop-card"> |
||||
|
<div class="prop-card-header"> |
||||
|
<div class="prop-card-meta"> |
||||
|
<span class="prop-tipo">{{ prop.obj.tipo }}</span> |
||||
|
<span class="prop-status {{ prop.status }}">{{ prop.status_label }}</span> |
||||
|
</div> |
||||
|
<span class="prop-date"> |
||||
|
{% if prop.data_envio_fmt %} |
||||
|
{{ prop.data_envio_fmt }} |
||||
|
{% else %} |
||||
|
— |
||||
|
{% endif %} |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
<div class="prop-ementa"> |
||||
|
{{ prop.obj.descricao|truncatewords:40 }} |
||||
|
</div> |
||||
|
|
||||
|
{% if prop.status == 'devolvida' and prop.obj.justificativa_devolucao %} |
||||
|
<div class="prop-alert"> |
||||
|
<strong>{% trans "Devolvida:" %}</strong> {{ prop.obj.justificativa_devolucao }} |
||||
|
</div> |
||||
|
{% endif %} |
||||
|
|
||||
|
<div class="prop-card-footer"> |
||||
|
<div class="prop-info"> |
||||
|
{% if prop.obj.numero_proposicao %} |
||||
|
#{{ prop.obj.numero_proposicao }}/{{ prop.obj.ano }} |
||||
|
{% endif %} |
||||
|
{% if prop.obj.conteudo_gerado_related %} |
||||
|
<a href="{% url prop.obj|urldetail_content_type:prop.obj.conteudo_gerado_related prop.obj.conteudo_gerado_related.id %}"> |
||||
|
{{ prop.obj.conteudo_gerado_related }} |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
<div class="prop-card-actions"> |
||||
|
<a href="{% url 'sapl.materia:proposicao_detail' prop.obj.pk %}"> |
||||
|
{% trans "Abrir" %} |
||||
|
</a> |
||||
|
{% if prop.status == 'elaboracao' %} |
||||
|
<a href="{% url 'sapl.materia:proposicao_update' prop.obj.pk %}"> |
||||
|
{% trans "Editar" %} |
||||
|
</a> |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
{% endfor %} |
||||
|
{% endif %} |
||||
|
</div> |
||||
|
|
||||
|
{% if is_paginated %} |
||||
|
<div class="prop-pagination"> |
||||
|
<nav> |
||||
|
<ul class="pagination pagination-sm justify-content-center"> |
||||
|
{% if page_obj.has_previous %} |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Anterior</a> |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
|
||||
|
{% for page in page_range %} |
||||
|
{% if page == None or page == 'None' %} |
||||
|
<li class="page-item disabled"><span class="page-link">...</span></li> |
||||
|
{% else %} |
||||
|
<li class="page-item {% if page == page_obj.number %}active{% endif %}"> |
||||
|
<a class="page-link" href="?page={{ page }}">{{ page }}</a> |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
{% endfor %} |
||||
|
|
||||
|
{% if page_obj.has_next %} |
||||
|
<li class="page-item"> |
||||
|
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Próxima</a> |
||||
|
</li> |
||||
|
{% endif %} |
||||
|
</ul> |
||||
|
</nav> |
||||
|
<p class="prop-pagination-info"> |
||||
|
Página {{ page_obj.number }} de {{ paginator.num_pages }} |
||||
|
</p> |
||||
|
</div> |
||||
|
{% endif %} |
||||
|
|
||||
{% endblock %} |
{% endblock %} |
||||
Loading…
Reference in new issue