From 9f02c41c317a80536b399a588f9147b08f9628d0 Mon Sep 17 00:00:00 2001 From: VPS 4 Deploy Agent Date: Sun, 31 May 2026 16:43:19 +0200 Subject: [PATCH] feat(ui): responsividade mobile profissional (320px+) sem alterar desktop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Todo o tratamento mobile é aditivo e isolado atrás de @media (max-width:1023px/ 480px/360px) — o visual desktop (≥1024px) permanece pixel-idêntico. Navegação: - Sidebar.jsx: vira drawer deslizante no mobile (top-bar fixo + hambúrguer + backdrop + botão fechar). Trava scroll do body, fecha ao navegar/tocar item. No desktop continua sidebar fixa idêntica. Componente novo: - DropdownMenu.jsx: menu flutuante chevron-down reutilizável (fecha em outside-click/ESC). Usado para colapsar ações secundárias no mobile. Headers (GtosPage, PatientsPage): - Busca full-width; botões "+ Novo/Atualizar" em linha no desktop (.hide-mobile) e colapsados em DropdownMenu no mobile (.show-mobile). Tabelas → cards empilhados (PatientsPage, GtosPage, AdminClinicsPage): - classe .responsive-table + data-label em cada td. No mobile thead some e cada linha vira card com rótulo:valor. Desktop mantém tabela normal. CSS (index.css): - Sistema de breakpoints completo, drawer, dropdown, cards de tabela, grid de thumbs reduzido, modais fullscreen, 100dvh, inputs 16px (anti-zoom iOS), alvos de toque ≥40px, paginação/stat-cards que quebram linha. Fix: remove chave 'border' duplicada no style do stat card da GtosPage. Co-Authored-By: Claude Opus 4.8 --- .../src/components/DropdownMenu.jsx | 75 ++++++ .../dental-client/src/components/Sidebar.jsx | 240 ++++++++++-------- dental-server/dental-client/src/index.css | 230 ++++++++++++++++- .../src/pages/AdminClinicsPage.jsx | 16 +- .../dental-client/src/pages/GtosPage.jsx | 49 ++-- .../dental-client/src/pages/PatientsPage.jsx | 39 ++- 6 files changed, 493 insertions(+), 156 deletions(-) create mode 100644 dental-server/dental-client/src/components/DropdownMenu.jsx diff --git a/dental-server/dental-client/src/components/DropdownMenu.jsx b/dental-server/dental-client/src/components/DropdownMenu.jsx new file mode 100644 index 0000000..7608916 --- /dev/null +++ b/dental-server/dental-client/src/components/DropdownMenu.jsx @@ -0,0 +1,75 @@ +import { useState, useRef, useEffect } from 'react'; + +/** + * Menu flutuante (chevron-down) reutilizável. + * Usado para colapsar ações secundárias em telas pequenas, economizando espaço. + * + * Props: + * - label: texto do botão (default "Ações") + * - icon: ícone opcional antes do label + * - items: array de { label, icon, onClick, danger, active, render } + * (use `render` para inserir um nó custom, ex: um toggleStatus(c.id, e.target.checked)} /> diff --git a/dental-server/dental-client/src/pages/GtosPage.jsx b/dental-server/dental-client/src/pages/GtosPage.jsx index 4606898..47719f9 100644 --- a/dental-server/dental-client/src/pages/GtosPage.jsx +++ b/dental-server/dental-client/src/pages/GtosPage.jsx @@ -5,6 +5,7 @@ import { useAuth } from '../contexts/AuthContext'; import Sidebar from '../components/Sidebar'; import ThumbImg from '../components/ThumbImg'; import Modal from '../components/Modal'; +import DropdownMenu from '../components/DropdownMenu'; import CreatePatientModal from '../components/CreatePatientModal'; import SettingsModal from '../components/SettingsModal'; import UserManagementModal from '../components/UserManagementModal'; @@ -341,10 +342,24 @@ export default function GtosPage() { onChange={e => setSearch(e.target.value)} style={{ height:42, minWidth:240 }} /> - - + {/* Desktop: botões em linha */} + + + + + {/* Mobile: ações em menu flutuante */} + + setShowCreate(true) }, + { label:'Atualizar lista', icon:'🔄', onClick:reload }, + ]} + /> + @@ -353,13 +368,13 @@ export default function GtosPage() {
{/* Stats */} -
+
{FILTERS.map(f => (
) : (
- +
{['', 'Número GTO', 'Paciente', 'Imagens', 'Descrição', 'Criado em', 'Status', 'Ações'].map(col => ( @@ -414,7 +429,7 @@ export default function GtosPage() { onMouseLeave={e => e.currentTarget.style.background = 'transparent'} > {/* Thumb */} - {/* Paciente */} - {/* Imagens */} - {/* Descrição */} - {/* Data */} - {/* Status */} - {/* Ações */} -
+ {/* Número */} - + #{gto.gto_number} + {gto.patient_name} + 0 ? 'rgba(79,70,229,0.1)' : 'rgba(148,163,184,0.1)', @@ -445,22 +460,22 @@ export default function GtosPage() { + {gto.description || '—'} + {fmtDate(gto.created_at)} + e.stopPropagation()}> + e.stopPropagation()}>
- + + + + + + setShowCreate(true) }, + { label: 'Atualizar lista', icon: '🔄', onClick: () => loadPatients(true) }, + ]} + /> +
@@ -143,7 +156,7 @@ export default function PatientsPage() { {!loading && patients.length > 0 && (
- e.currentTarget.style.background = 'var(--surface-hover, rgba(0,0,0,0.025))'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} > - - - - - -
+
+ {p.doctor || '—'} + + {formatDate(p.last_date)} + {p.remark || '—'} +