From 007d8200deefc7ffac5d6142e51c302370e84aa7 Mon Sep 17 00:00:00 2001 From: lack0fcode Date: Fri, 28 Nov 2025 15:53:22 -0300 Subject: [PATCH 1/2] =?UTF-8?q?Transferir=20mudan=C3=A7as=20para=20lack0fd?= =?UTF-8?q?ev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 23 ++ READMEimplementation.md | 293 ++++++++++++++++++ .../alterar_senha_funcionario.html | 63 ++++ .../administrador/cadastrar_funcionario.html | 90 ++---- .../editar_dados_funcionario.html | 49 +++ .../administrador/listar_funcionarios.html | 18 +- administrador/urls.py | 10 + administrador/views.py | 45 ++- core/forms.py | 51 ++- core/views.py | 20 +- docker-compose.prod.yml | 50 +++ entrypoint.sh | 120 +++++++ guiche/templates/guiche/tv1.html | 11 +- guiche/views.py | 13 +- nginx.conf | 51 +++ .../templates/profissional_saude/tv2.html | 58 ++-- requirements-docker.txt | 10 + staticfiles/admin/css/widgets.css | 4 +- staticfiles/admin/js/SelectFilter2.js | 12 +- templates/base.html | 2 + 20 files changed, 881 insertions(+), 112 deletions(-) create mode 100644 Dockerfile create mode 100644 READMEimplementation.md create mode 100644 administrador/templates/administrador/alterar_senha_funcionario.html create mode 100644 administrador/templates/administrador/editar_dados_funcionario.html create mode 100644 docker-compose.prod.yml create mode 100755 entrypoint.sh create mode 100644 nginx.conf create mode 100644 requirements-docker.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..78ad520 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends build-essential libpq-dev gcc \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements-docker.txt /app/requirements-docker.txt + +RUN python -m pip install --upgrade pip setuptools wheel \ + && pip install -r /app/requirements-docker.txt + +COPY . /app + +RUN chmod +x /app/entrypoint.sh || true + +EXPOSE 8000 + +ENTRYPOINT ["/app/entrypoint.sh"] diff --git a/READMEimplementation.md b/READMEimplementation.md new file mode 100644 index 0000000..847d008 --- /dev/null +++ b/READMEimplementation.md @@ -0,0 +1,293 @@ +# Implementação em Produção - SGA LSL Univesp + +## Pré-requisitos +- Servidor Linux com Docker e Docker Compose instalados +- PostgreSQL (via Docker) +- Porta 80 disponível para Nginx + +## Passo a Passo + +### 1. Clonagem e Configuração Inicial + +- Adicione os arquivos do sistema no diretório do servidor (Por ex. /home/user/python-sga-lsl-univesp) +- Entre no diretório (cd /caminho/do/sistema) + +```bash +# No diretório do projeto (onde está entrypoint.sh) +chmod +x entrypoint.sh +``` + +### 2. Configuração do Ambiente (.env) +Edite o arquivo `.env` com valores seguros para produção: + +```env +POSTGRES_USER=sga_prod_user +POSTGRES_PASSWORD=SuaSenhaSuperSeguraAqui123! +POSTGRES_DB=sga_prod_db +SECRET_KEY=SuaSecretKeySuperSeguraDePeloMenos50CaracteresAqui +DEBUG=0 +DATABASE_URL=postgres://sga_prod_user:SuaSenhaSuperSeguraAqui123!@db:5432/sga_prod_db + +# Superuser para produção +DJANGO_SUPERUSER_USERNAME=admin_cpf # CPF válido sem máscara +DJANGO_SUPERUSER_EMAIL=admin@seudominio.com +DJANGO_SUPERUSER_PASSWORD=SenhaSuperSeguraParaAdmin +``` + +**Recomendações de Segurança:** +- Use senhas fortes e únicas (mínimo 16 caracteres, com letras, números e símbolos) +- Mantenha o `DEBUG=0` em produção +- Use um `SECRET_KEY` gerado aleatoriamente (pode usar `openssl rand -hex 32`) +- Restrinja acesso ao arquivo `.env` (chmod 600) +- Considere usar variáveis de ambiente do sistema em vez de arquivo `.env` para maior segurança + +### 3. Build e Inicialização +```bash +# Build das imagens +docker-compose -f docker-compose.prod.yml build + +# Inicialização (cria banco, superuser, guichês) +docker-compose -f docker-compose.prod.yml up -d +``` + +### 4. Verificação +Acesse `http://seu-servidor` e faça login com o superuser configurado. + +Sugestão de fluxo inicial após o primeiro login: + +- **Login do administrador:** faça login usando as credenciais do superuser definidas no arquivo `.env` (campo `DJANGO_SUPERUSER_USERNAME` e `DJANGO_SUPERUSER_PASSWORD`). +- **Ajuste do perfil:** após o login, acesse a interface de administrador e edite suas próprias informações de contato/perfil para que constem corretamente no sistema. +- **Cadastrar funcionários:** comece a adicionar os funcionários no sistema pela interface de administração. Recomendamos chamar cada funcionário até o computador do administrador para que o próprio funcionário preencha ou confirme seus dados na tela — a interface é responsiva e também pode ser usada a partir de um smartphone caso prefira levar o dispositivo até o funcionário. +- **Informar acesso aos funcionários:** depois de criar a conta, informe ao funcionário a URL que ele deve digitar no navegador para acessar o sistema (ex.: `http://seu-servidor/` ou `http://seu-servidor/login`). + +- **IMPORTANTE:** Esses passos garantem que o cadastro seja feito com supervisão e que cada funcionário saiba imediatamente como acessar a aplicação. + +- **Preparar as tvs:** Cada tv tem uma url especifica (`http://seu-servidor/guiche/tv1` e `http://seu-servidor/profissional_saude/tv2`). +Instale os cabos hdmi em quaisquer computadores proximos (maximo 10m de distancia) ou alguma outra solução para que as tvs tenham acesso remoto a algum computador para exibirem a interface web (É possivel testar o navegador da própria TV caso seja Smart). +Esses endpoints nao precisam de login. + +### 5. Configuração para Iniciar Automaticamente no Boot + +#### Opção 1: Usando systemd (Recomendado) +Crie um serviço systemd: + +```bash +sudo nano /etc/systemd/system/sga.service +``` + +Conteúdo do arquivo: +```ini +[Unit] +Description=SGA LSL Univesp +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/caminho/para/python-sga-LSL-Univesp +ExecStart=/usr/bin/docker-compose -f docker-compose.prod.yml up -d +ExecStop=/usr/bin/docker-compose -f docker-compose.prod.yml down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target +``` + +Habilite e inicie: +```bash +sudo systemctl daemon-reload +sudo systemctl enable sga.service +sudo systemctl start sga.service +``` + +#### Opção 2: Usando cron (@reboot) +Adicione ao crontab do root: +```bash +sudo crontab -e +``` + +Adicione a linha: +```cron +@reboot cd /caminho/para/python-sga-LSL-Univesp && /usr/bin/docker-compose -f docker-compose.prod.yml up -d +``` + +### 6. Manutenção +- **Logs:** `docker-compose -f docker-compose.prod.yml logs -f` +- **Backup do banco:** Use volumes Docker ou scripts externos +- **Atualizações:** Pare o serviço, atualize o código, rebuild e reinicie + +### 7. Segurança Adicional + +#### Firewall e Restrição de Acesso +Para garantir que apenas dispositivos autorizados (ex.: rede do hospital) acessem a aplicação, configure restrições por IP no Nginx e no firewall do servidor. + +##### Opção 1: Restrição no Nginx (Recomendado para Controle Fino) +Edite o arquivo `nginx.conf` e adicione as linhas `allow` e `deny` no bloco `server`: + +```nginx +server { + listen 80; + server_name localhost; + + # Restringir acesso por IP (ajuste a sub-rede do hospital) + allow 192.168.1.0/24; # Permite sub-rede específica (ex.: rede interna do hospital) + deny all; # Bloqueia todos os outros IPs + + location / { + proxy_pass http://django; + # ... resto da configuração + } + # ... outras locations +} +``` + +- Substitua `192.168.1.0/24` pela sub-rede real da rede do hospital (ex.: `10.0.0.0/8` para redes privadas). +- Após editar, reinicie o Nginx: `docker-compose -f docker-compose.prod.yml restart nginx`. + +##### Opção 2: Firewall no Servidor (ufw - Ubuntu/Debian) +Use `ufw` para restringir acesso na porta do Nginx (padrão 80, ou mude para outra como 8080 no `docker-compose.prod.yml`). + +Instale e configure o ufw (se não estiver instalado): +```bash +sudo apt update +sudo apt install ufw +sudo ufw enable +``` + +Permita apenas a sub-rede específica: +```bash +sudo ufw allow from 192.168.1.0/24 to any port 80 # Permite sub-rede na porta 80 +sudo ufw deny 80 # Bloqueia todos os outros na porta 80 +``` + +- Ajuste a sub-rede e porta conforme necessário. +- Verifique o status: `sudo ufw status`. +- Isso adiciona uma camada extra de segurança além do Nginx. + +#### Outras Medidas de Segurança +- **HTTPS**: Configure SSL no Nginx para criptografar o tráfego (use Let's Encrypt ou certificado próprio). +- **Monitoramento**: Monitore logs do Nginx e Docker regularmente: `docker-compose -f docker-compose.prod.yml logs -f nginx`. +- **Atualizações**: Mantenha Docker, Nginx, Django e dependências atualizadas para patches de segurança. +- **Backup**: Faça backups regulares do banco de dados (use volumes Docker ou ferramentas como `pg_dump`). + +### 8. HTTPS / Certificados (Passo-a-passo) + +Para produção recomendamos usar HTTPS para criptografar o tráfego mesmo em redes internas. Abaixo estão 3 opções com passos concretos. + +Opção A — Self-signed (rápido, gera aviso no navegador) + +1. Gere certificados no servidor (ex.: dentro do diretório `nginx/ssl` no repo): + +```bash +mkdir -p nginx/ssl +openssl req -x509 -nodes -days 365 \ + -newkey rsa:2048 \ + -keyout nginx/ssl/nginx.key \ + -out nginx/ssl/nginx.crt \ + -subj "/CN=sga.hospital.local" +``` + +2. Monte os arquivos no serviço `nginx` do `docker-compose.prod.yml` (exemplo): + +```yaml +services: + nginx: + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + - staticfiles:/app/staticfiles + ports: + - "443:443" + - "80:80" +``` + +3. Adicione um bloco `server` em `nginx.conf` para TLS (exemplo): + +```nginx +server { + listen 443 ssl; + server_name sga.hospital.local; + + ssl_certificate /etc/nginx/ssl/nginx.crt; + ssl_certificate_key /etc/nginx/ssl/nginx.key; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + + location / { + proxy_pass http://django; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /static/ { alias /app/staticfiles/; } + location /media/ { alias /app/media/; } +} + +server { + listen 80; + server_name sga.hospital.local; + return 301 https://$host$request_uri; +} +``` + +4. Reinicie o nginx container: + +```bash +docker-compose -f docker-compose.prod.yml up -d --no-deps --build nginx +# ou +docker-compose -f docker-compose.prod.yml restart nginx +``` + +Nota: navegadores mostrarão aviso de certificado não confiável. Use apenas para testes ou quando for aceitável confiar manualmente. + +Opção B — mkcert (boa para LAN gerenciada) + +- `mkcert` cria um CA local e pode gerar certificados confiáveis se você instalar a CA em todas as máquinas clientes (TI do hospital pode distribuir a CA via políticas): + +1. Instale `mkcert` (veja docs: https://github.com/FiloSottile/mkcert). +2. No host de administração gere os certificados: + +```bash +mkcert -install +mkcert sga.hospital.local 192.168.1.10 +# Isso gera algo como sga.hospital.local+IP.pem e key file +``` + +3. Monte as chaves em `nginx` como no passo A e reinicie. Instale a CA raiz `mkcert` nos navegadores/hosts clientes (TI). + +Opção C — Let's Encrypt (melhor se tiver domínio público) + +1. Se seu servidor tem um domínio público válido (`sga.hospital.example`) e aponta para o IP, use `certbot`: + +```bash +sudo apt update +sudo apt install certbot +sudo certbot certonly --standalone -d sga.hospital.example +``` + +2. Monte `/etc/letsencrypt/live/sga.hospital.example/fullchain.pem` e `privkey.pem` no container `nginx` (somente se terminar TLS no container). Use `certbot renew` em cron/systemd timer. + +Firewall e HSTS +- Após habilitar HTTPS, ajuste o `ufw` para permitir apenas a sub-rede hospitalar nas portas 443/80: + +```bash +sudo ufw allow from 192.168.1.0/24 to any port 443 +sudo ufw deny 443 +``` + +- Cuidado com HSTS: só ative `add_header Strict-Transport-Security` quando tiver certificados confiáveis e controle sobre os clientes (HSTS impede acesso HTTP por período configurado). + +### 9. Desinstalação completa + +- Caso tenha interesse em desinstalar completamente o sistema rode o comando + +```bash +docker-compose -f docker-compose.prod.yml down -v +``` +(Use com cuidado, remove também todos os dados do Banco de dados) + +--- diff --git a/administrador/templates/administrador/alterar_senha_funcionario.html b/administrador/templates/administrador/alterar_senha_funcionario.html new file mode 100644 index 0000000..8956851 --- /dev/null +++ b/administrador/templates/administrador/alterar_senha_funcionario.html @@ -0,0 +1,63 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+

Alterar Senha do Funcionário

+

Altere a senha do funcionário selecionado: {{ funcionario.first_name }} {{ funcionario.last_name }} (CPF: {{ funcionario.cpf }})

+
+ +
+
+ {% csrf_token %} + + {% if messages %} +
+ {% for message in messages %} +
+
+
+ {% if message.tags == 'success' %} + + {% elif message.tags == 'error' %} + + {% else %} + + {% endif %} +
+
+

{{ message }}

+
+
+
+ {% endfor %} +
+ {% endif %} + +
+
+ + {{ form.new_password1 }} + {% if form.errors.new_password1 %} +

{{ form.errors.new_password1.0 }}

+ {% endif %} +
+ +
+ + {{ form.new_password2 }} + {% if form.errors.new_password2 %} +

{{ form.errors.new_password2.0 }}

+ {% endif %} +
+
+ +
+ Cancelar + +
+
+
+
+{% endblock %} diff --git a/administrador/templates/administrador/cadastrar_funcionario.html b/administrador/templates/administrador/cadastrar_funcionario.html index 6cc0fc2..e86fae7 100644 --- a/administrador/templates/administrador/cadastrar_funcionario.html +++ b/administrador/templates/administrador/cadastrar_funcionario.html @@ -22,7 +22,7 @@

Cadastrar Funcionário

-
+ {% csrf_token %} @@ -104,6 +104,31 @@

Cadastrar Funcionário

{% endif %}
+ + {% if form.password1 %} +
+ + {{ form.password1 }} + {% if form.password1.errors %} +

{{ form.password1.errors.0 }}

+ {% endif %} +
+ +
+ + {{ form.password2 }} + {% if form.password2.errors %} +

{{ form.password2.errors.0 }}

+ {% endif %} +
+ {% endif %} +
@@ -183,63 +208,6 @@

Dicas para Cadastro

- +{% block atividade_script %}{% endblock %} + {% endblock %} \ No newline at end of file diff --git a/administrador/templates/administrador/editar_dados_funcionario.html b/administrador/templates/administrador/editar_dados_funcionario.html new file mode 100644 index 0000000..a46a502 --- /dev/null +++ b/administrador/templates/administrador/editar_dados_funcionario.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} +{% load static %} + +{% block content %} +
+
+

Editar Dados do Funcionário

+

Editando: {{ funcionario.first_name }} {{ funcionario.last_name }} — CPF: {{ funcionario.cpf }}

+
+ +
+ + {% csrf_token %} + + {% if messages %} +
+ {% for message in messages %} +
+
+
+ {% if message.tags == 'success' %} + + {% elif message.tags == 'error' %} + + {% else %} + + {% endif %} +
+
+

{{ message }}

+
+
+
+ {% endfor %} +
+ {% endif %} + +
+ {{ form.as_p }} +
+ +
+ Cancelar + +
+ +
+
+{% endblock %} diff --git a/administrador/templates/administrador/listar_funcionarios.html b/administrador/templates/administrador/listar_funcionarios.html index 0daf827..55733b6 100644 --- a/administrador/templates/administrador/listar_funcionarios.html +++ b/administrador/templates/administrador/listar_funcionarios.html @@ -193,10 +193,15 @@

Lista de Funcionário
- - Editar + Editar Dados + + + + Alterar Senha
- - Editar + Editar Dados + + + + Alterar Senha

function falarSoletracao(nomeCompleto, numeroGuiche) { console.log("Função falarSoletracao chamada!"); + if (!('speechSynthesis' in window)) { + console.log("Speech Synthesis não suportado neste navegador."); + return; + } + if (!synth) { console.error("speechSynthesis não está disponível!"); return; @@ -282,7 +287,11 @@

Histórico de Chamadas

} utterThis.onerror = function(event) { - console.error('Erro ao falar:', event.error); + if (event.error === 'not-allowed') { + console.log('Fala bloqueada pelo navegador. Possivelmente requer interação do usuário ou HTTPS.'); + } else { + console.error('Erro ao falar:', event.error); + } } synth.speak(utterThis); diff --git a/guiche/views.py b/guiche/views.py index e6d06dc..629e5c7 100644 --- a/guiche/views.py +++ b/guiche/views.py @@ -345,7 +345,7 @@ def tv1_historico_api_view(request) -> JsonResponse: class SelecionarGuicheForm(forms.Form): guiche = forms.ModelChoiceField( - queryset=Guiche.objects.all().order_by("numero"), + queryset=Guiche.objects.filter(funcionario__isnull=True).order_by("numero"), empty_label="Selecione um guichê", label="Guichê do dia", ) @@ -358,6 +358,17 @@ def selecionar_guiche(request): form = SelecionarGuicheForm(request.POST) if form.is_valid(): g = form.cleaned_data["guiche"] + # Liberar guichê anterior se existir + old_guiche_id = request.session.get("guiche_id") + if old_guiche_id: + try: + old_guiche = Guiche.objects.get(id=old_guiche_id) + old_guiche.funcionario = None + old_guiche.save() + except Guiche.DoesNotExist: + pass + g.funcionario = request.user + g.save() request.session["guiche_id"] = g.id request.session.modified = True return redirect("guiche:painel_guiche") diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..bffc751 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,51 @@ +events { + worker_connections 1024; +} + +# Segurança: Restringir acesso por IP +# Para permitir apenas dispositivos específicos (ex.: rede do hospital), adicione as linhas abaixo no bloco 'server'. +# Exemplo: +# allow 192.168.1.0/24; # Permite sub-rede do hospital (ajuste o IP/sub-rede real) +# deny all; # Bloqueia todos os outros acessos +# +# Nota: Se o servidor usar ufw (Ubuntu/Debian), configure o firewall no host: +# sudo ufw allow from 192.168.1.0/24 to any port 8080 # Permite sub-rede na porta do Nginx (ajuste porta se mudar) +# sudo ufw deny 8080 # Bloqueia outros na mesma porta +# Isso adiciona uma camada extra de segurança além do Nginx. + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + upstream django { + server web:8000; + } + + server { + listen 80; + server_name localhost; + + # Exemplo de restrição por IP (descomente e ajuste): + # allow 192.168.1.0/24; # Permite sub-rede do hospital + # deny all; # Bloqueia todos os outros + + location / { + proxy_pass http://django; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /static/ { + alias /app/staticfiles/; + } + + location /media/ { + alias /app/media/; + } + } +} \ No newline at end of file diff --git a/profissional_saude/templates/profissional_saude/tv2.html b/profissional_saude/templates/profissional_saude/tv2.html index c47d74b..5db863c 100644 --- a/profissional_saude/templates/profissional_saude/tv2.html +++ b/profissional_saude/templates/profissional_saude/tv2.html @@ -7,9 +7,12 @@ Painel de Chamadas @@ -217,7 +211,7 @@

Histórico de Confirmações

$('#senha-chamada-texto').data('nome', data.nome_completo); // Atualiza o data-nome $('#senha-chamada-texto').data('sala', data.sala_profissional); // Atualiza o data-sala - falarSenhaTresVezes(data.nome_completo, data.sala_profissional); + falarSenhaTresVezes(data.nome_completo, data.sala_profissional, data.profissional_nome); ultimaChamadaId = data.id; // Atualiza o ID da última chamada } else { @@ -232,7 +226,7 @@

Histórico de Confirmações

// Função para chamar o paciente - function falarSoletracao(nomeCompleto, profissionalNome) { + function falarSoletracao(nomeCompleto, sala, profissionalNome) { console.log("Função falarSoletracao chamada!"); if (!synth) { @@ -240,13 +234,13 @@

Histórico de Confirmações

return; } - if (!nomeCompleto || !profissionalNome) { - console.log("Nome ou Profissional não definidos. Não há nada para falar."); + if (!nomeCompleto || !sala || !profissionalNome) { + console.log("Nome, sala ou profissional não definidos. Não há nada para falar."); return; } var utterThis = new SpeechSynthesisUtterance(); - utterThis.text = "Paciente " + nomeCompleto + ", dirija-se à sala " + salaConsulta + " para consulta com o Doutor " + profissionalNome + "."; // Formata a frase + utterThis.text = "Paciente " + nomeCompleto + ", dirija-se à sala " + sala + "."; // Formata a frase console.log("String de fala:", utterThis.text); utterThis.lang = 'pt-BR'; @@ -270,7 +264,11 @@

Histórico de Confirmações

} utterThis.onerror = function(event) { - console.error('Erro ao falar:', event.error); + if (event.error === 'not-allowed') { + console.log('Fala bloqueada pelo navegador. Clique na página para permitir.'); + } else { + console.error('Erro ao falar:', event.error); + } } synth.speak(utterThis); @@ -297,9 +295,9 @@

Histórico de Confirmações

return null; } - function falarSenhaTresVezes(nomeCompleto, profissionalNome) { //Removi a senha + function falarSenhaTresVezes(nomeCompleto, sala, profissionalNome) { for (var i = 0; i < 3; i++) { - falarSoletracao(nomeCompleto, profissionalNome); + falarSoletracao(nomeCompleto, sala, profissionalNome); } } diff --git a/requirements-docker.txt b/requirements-docker.txt new file mode 100644 index 0000000..91deb75 --- /dev/null +++ b/requirements-docker.txt @@ -0,0 +1,10 @@ +Django==5.2.7 +djangorestframework==3.16.1 +gunicorn==23.0.0 +gTTS==2.5.4 +whitenoise==6.11.0 +dj-database-url==3.0.1 +psycopg2-binary==2.9.11 +python-decouple==3.8 +twilio==9.8.5 +python-dotenv==1.2.1 diff --git a/staticfiles/admin/css/widgets.css b/staticfiles/admin/css/widgets.css index 538af2e..a5f615a 100644 --- a/staticfiles/admin/css/widgets.css +++ b/staticfiles/admin/css/widgets.css @@ -49,7 +49,7 @@ padding: 8px; } -.aligned .selector-chosen-title label { +.selector-chosen-title label { color: var(--header-link-color); width: 100%; } @@ -60,7 +60,7 @@ padding: 8px; } -.aligned .selector-available-title label { +.selector-available-title label { width: 100%; } diff --git a/staticfiles/admin/js/SelectFilter2.js b/staticfiles/admin/js/SelectFilter2.js index 08d47fc..970b511 100644 --- a/staticfiles/admin/js/SelectFilter2.js +++ b/staticfiles/admin/js/SelectFilter2.js @@ -72,7 +72,8 @@ Requires core.js and SelectBox.js. selector_available, interpolate(gettext('Choose all %s'), [field_name]), 'id', field_id + '_add_all', - 'class', 'selector-chooseall' + 'class', 'selector-chooseall', + 'type', 'button' ); //