style(dashboard) ajuste na responsividade

This commit is contained in:
Jonas Francisco 2025-11-24 21:51:05 -03:00
parent 77b7fdd599
commit 07c0533224
3 changed files with 87 additions and 95 deletions

View File

@ -602,7 +602,7 @@ export default function PacientePage() {
{/* Cards com Informações */}
<div className="grid grid-cols-1 gap-3 sm:gap-4 md:gap-4 md:grid-cols-2">
<Card className="group rounded-2xl border border-border/60 bg-card/70 p-4 sm:p-5 md:p-5 backdrop-blur-sm shadow-sm transition hover:shadow-md">
<Card className="group rounded-2xl border border-border/60 bg-card p-4 sm:p-5 md:p-5 shadow-sm transition hover:shadow-md">
<div className="flex h-32 sm:h-36 md:h-40 w-full flex-col items-center justify-center gap-2 sm:gap-3">
<div className="flex h-10 w-10 sm:h-11 sm:w-11 md:h-12 md:w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
<Calendar className="h-5 w-5 sm:h-5 sm:w-5 md:h-6 md:w-6" aria-hidden />
@ -616,7 +616,7 @@ export default function PacientePage() {
</div>
</Card>
<Card className="group rounded-2xl border border-border/60 bg-card/70 p-4 sm:p-5 md:p-5 backdrop-blur-sm shadow-sm transition hover:shadow-md">
<Card className="group rounded-2xl border border-border/60 bg-card p-4 sm:p-5 md:p-5 shadow-sm transition hover:shadow-md">
<div className="flex h-32 sm:h-36 md:h-40 w-full flex-col items-center justify-center gap-2 sm:gap-3">
<div className="flex h-10 w-10 sm:h-11 sm:w-11 md:h-12 md:w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
<FileText className="h-5 w-5 sm:h-5 sm:w-5 md:h-6 md:w-6" aria-hidden />
@ -1960,8 +1960,8 @@ export default function PacientePage() {
{/* Layout com sidebar e conteúdo */}
<div className="grid grid-cols-1 md:grid-cols-[200px_1fr] lg:grid-cols-[220px_1fr] gap-4 sm:gap-5 md:gap-6">
{/* Sidebar vertical - sticky */}
<aside className="sticky top-24 h-fit md:top-24">
<nav aria-label="Navegação do dashboard" className="bg-card shadow-md rounded-lg border border-border p-1.5 sm:p-2 md:p-3 z-30">
<aside className="sticky top-24 h-fit md:top-24 z-40">
<nav aria-label="Navegação do dashboard" className="relative isolate bg-card shadow-lg rounded-lg border border-border p-1.5 sm:p-2 md:p-3 z-50">
<div className="grid grid-cols-2 md:grid-cols-1 gap-1 sm:gap-1.5">
<Button
variant={tab==='dashboard'?'default':'ghost'}

View File

@ -33,105 +33,89 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
}, [dropdownOpen]);
return (
<header className="h-16 border-b border-border bg-background px-6 flex items-center justify-between">
<div className="flex flex-row items-center gap-4">
<header className="sticky top-0 z-40 border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 px-3 sm:px-6 py-2 flex flex-wrap items-center gap-3">
<div className="flex items-center gap-3 min-w-0">
<SidebarTrigger />
<div className="flex items-start flex-col justify-center py-2">
<h1 className="text-lg font-semibold text-foreground">{title}</h1>
<p className="text-muted-foreground">{subtitle}</p>
<div className="flex flex-col justify-center leading-tight min-w-0">
<h1 className="text-sm sm:text-lg font-semibold text-foreground truncate max-w-[55vw] sm:max-w-none">{title}</h1>
{subtitle && (
<p className="text-[11px] sm:text-xs text-muted-foreground truncate max-w-[55vw] sm:max-w-none">{subtitle}</p>
)}
</div>
</div>
<div className="flex items-center space-x-4">
<Button variant="ghost" size="icon" className="hover-primary-blue">
<div className="flex items-center gap-2 ml-auto">
<Button variant="ghost" size="icon" className="hover-primary-blue hidden xs:flex">
<Bell className="h-4 w-4" />
</Button>
<SimpleThemeToggle />
<Button
variant="outline"
className="text-blue-500 border-blue-500 bg-transparent shadow-sm shadow-blue-500/10 border hover-primary-blue"
asChild
></Button>
{/* Avatar Dropdown Simples */}
<div className="relative" ref={dropdownRef}>
<Button
variant="ghost"
className="relative h-8 w-8 rounded-full border-2 border-border hover:border-primary"
<Button
variant="ghost"
className="relative h-8 w-8 rounded-full border border-border hover:border-primary"
onClick={() => setDropdownOpen(!dropdownOpen)}
aria-label="Abrir menu do perfil"
>
{/* Mostrar foto do usuário quando disponível; senão, mostrar fallback com iniciais */}
<Avatar className="h-8 w-8">
{
(() => {
const userPhoto = (user as any)?.profile?.foto_url || (user as any)?.profile?.fotoUrl || (user as any)?.profile?.avatar_url
const alt = user?.name || user?.email || 'Usuário'
const getInitials = (name?: string, email?: string) => {
if (name) {
const parts = name.trim().split(/\s+/)
const first = parts[0]?.charAt(0) ?? ''
const second = parts[1]?.charAt(0) ?? ''
return (first + second).toUpperCase() || (email?.charAt(0) ?? 'U').toUpperCase()
}
if (email) return email.charAt(0).toUpperCase()
return 'U'
{(() => {
const userPhoto = (user as any)?.profile?.foto_url || (user as any)?.profile?.fotoUrl || (user as any)?.profile?.avatar_url
const alt = user?.name || user?.email || 'Usuário'
const getInitials = (name?: string, email?: string) => {
if (name) {
const parts = name.trim().split(/\s+/)
const first = parts[0]?.charAt(0) ?? ''
const second = parts[1]?.charAt(0) ?? ''
return (first + second).toUpperCase() || (email?.charAt(0) ?? 'U').toUpperCase()
}
return (
<>
<AvatarImage src={userPhoto || undefined} alt={alt} />
<AvatarFallback className="bg-primary text-primary-foreground font-semibold">{getInitials(user?.name, user?.email)}</AvatarFallback>
</>
)
})()
}
if (email) return email.charAt(0).toUpperCase()
return 'U'
}
return (
<>
<AvatarImage src={userPhoto || undefined} alt={alt} />
<AvatarFallback className="bg-primary text-primary-foreground font-semibold">{getInitials(user?.name, user?.email)}</AvatarFallback>
</>
)
})()}
</Avatar>
</Button>
{/* Dropdown Content */}
{dropdownOpen && (
<div className="absolute right-0 mt-2 w-80 bg-popover border border-border rounded-md shadow-lg z-50 text-popover-foreground">
<div className="p-4 border-b border-border">
<div className="absolute right-0 mt-2 w-64 sm:w-80 bg-popover border border-border rounded-md shadow-lg z-50 text-popover-foreground animate-in fade-in slide-in-from-top-2">
<div className="p-3 sm:p-4 border-b border-border">
<div className="flex flex-col space-y-1">
<p className="text-sm font-semibold leading-none">
<p className="text-xs sm:text-sm font-semibold leading-none">
{user?.userType === 'administrador' ? 'Administrador da Clínica' : 'Usuário do Sistema'}
</p>
{user?.email ? (
<p className="text-xs leading-none text-muted-foreground">{user.email}</p>
<p className="text-[10px] sm:text-xs leading-none text-muted-foreground truncate">{user.email}</p>
) : (
<p className="text-xs leading-none text-muted-foreground">Email não disponível</p>
<p className="text-[10px] sm:text-xs leading-none text-muted-foreground">Email não disponível</p>
)}
<p className="text-xs leading-none text-primary font-medium">
<p className="text-[10px] sm:text-xs leading-none text-primary font-medium">
Tipo: {user?.userType === 'administrador' ? 'Administrador' : user?.userType || 'Não definido'}
</p>
</div>
</div>
<div className="py-1">
<button
<button
onClick={(e) => {
e.preventDefault();
setDropdownOpen(false);
router.push('/perfil');
}}
className="w-full text-left px-4 py-2 text-sm hover:bg-accent cursor-pointer"
className="w-full text-left px-3 sm:px-4 py-2 text-xs sm:text-sm hover:bg-accent cursor-pointer"
>
Perfil
Perfil
</button>
<div className="border-t border-border my-1"></div>
<button
<div className="border-t border-border my-1" />
<button
onClick={(e) => {
e.preventDefault();
setDropdownOpen(false);
// Usar sempre o logout do hook useAuth (ele já redireciona corretamente)
logout();
}}
className="w-full text-left px-4 py-2 text-sm text-destructive hover:bg-destructive/10 cursor-pointer"
className="w-full text-left px-3 sm:px-4 py-2 text-xs sm:text-sm text-destructive hover:bg-destructive/10 cursor-pointer"
>
Sair
Sair
</button>
</div>
</div>

View File

@ -49,6 +49,23 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
const chatEndRef = useRef<HTMLDivElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
// Placeholder responsivo (não quebra, adapta o texto)
const [responsivePlaceholder, setResponsivePlaceholder] = useState("Pergunte qualquer coisa para a Zoe");
const computePlaceholder = (w: number) => {
if (w < 340) return "Pergunte à Zoe"; // ultra pequeno
if (w < 400) return "Pergunte algo à Zoe"; // pequeno
if (w < 520) return "Pergunte algo para a Zoe"; // médio estreito
return "Pergunte qualquer coisa para a Zoe"; // normal
};
useEffect(() => {
const update = () => setResponsivePlaceholder(computePlaceholder(window.innerWidth));
update();
window.addEventListener("resize", update);
return () => window.removeEventListener("resize", update);
}, []);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
@ -511,12 +528,11 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
{/* Input unificado com ícones embutidos */}
<div className="flex w-full">
<div className={`relative flex items-center w-full rounded-full border ${themeClasses.border} ${themeClasses.inputBg} overflow-hidden h-11`}>
{/* Botão anexar (esquerda) */}
<div className={`flex items-center w-full rounded-full border ${themeClasses.border} ${themeClasses.inputBg} h-11 px-2 gap-2`}>
<button
onClick={() => fileInputRef.current?.click()}
type="button"
className={`absolute left-2 flex items-center justify-center h-7 w-7 rounded-full transition-colors hover:bg-primary/20 ${themeClasses.text}`}
className={`flex items-center justify-center h-7 w-7 rounded-full transition-colors hover:bg-primary/20 flex-shrink-0 ${themeClasses.text}`}
aria-label="Anexar arquivos"
>
<Plus className="w-4 h-4" />
@ -528,41 +544,33 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
className="hidden"
onChange={(e) => handleFileSelect(e.target.files)}
/>
{/* Textarea */}
<textarea
ref={textareaRef}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={handleKeyPress}
placeholder="Pergunte qualquer coisa para a Zoe"
placeholder={responsivePlaceholder}
rows={1}
className={`pl-11 pr-24 w-full h-full bg-transparent resize-none focus:outline-none text-sm leading-snug py-3 ${themeClasses.text} placeholder-gray-400`}
className={`flex-1 bg-transparent resize-none focus:outline-none leading-snug py-3 pr-2 ${themeClasses.text} placeholder-gray-400 text-[13px] sm:text-sm placeholder:text-[12px] sm:placeholder:text-sm whitespace-nowrap overflow-hidden text-ellipsis placeholder:overflow-hidden placeholder:text-ellipsis`}
style={{ minHeight: 'auto', overflow: 'hidden' }}
/>
{/* Ícones à direita */}
<div className="absolute right-2 flex items-center gap-2">
<button
onClick={() => onOpenVoice?.()}
type="button"
className={`flex items-center justify-center h-8 w-8 rounded-full border ${themeClasses.border} transition-colors hover:bg-primary/20 ${themeClasses.text}`}
aria-label="Entrada de voz"
>
<AudioLines className="w-4 h-4" />
</button>
<button
onClick={sendMessage}
disabled={!inputValue.trim() && uploadedFiles.length === 0}
type="button"
className="flex items-center justify-center h-8 w-8 rounded-full bg-linear-to-r from-blue-500 to-purple-600 text-white hover:from-blue-600 hover:to-purple-700 disabled:from-gray-400 disabled:to-gray-500 disabled:cursor-not-allowed transition-colors shadow-md"
aria-label="Enviar mensagem"
>
<Send className="w-4 h-4" />
</button>
</div>
{/* Contador de caracteres */}
{inputValue.length > 0 && (
<span className={`absolute bottom-1 right-24 text-[10px] ${themeClasses.textSecondary}`}>{inputValue.length}</span>
)}
<button
onClick={() => onOpenVoice?.()}
type="button"
className={`flex items-center justify-center h-8 w-8 rounded-full border ${themeClasses.border} transition-colors hover:bg-primary/20 flex-shrink-0 ${themeClasses.text}`}
aria-label="Entrada de voz"
>
<AudioLines className="w-4 h-4" />
</button>
<button
onClick={sendMessage}
disabled={!inputValue.trim() && uploadedFiles.length === 0}
type="button"
className="flex items-center justify-center h-8 w-8 rounded-full bg-linear-to-r from-blue-500 to-purple-600 text-white hover:from-blue-600 hover:to-purple-700 disabled:from-gray-400 disabled:to-gray-500 disabled:cursor-not-allowed transition-colors shadow-md flex-shrink-0"
aria-label="Enviar mensagem"
>
<Send className="w-4 h-4" />
</button>
</div>
</div>
</div>