develop #83
@ -602,7 +602,7 @@ export default function PacientePage() {
|
|||||||
|
|
||||||
{/* Cards com Informações */}
|
{/* Cards com Informações */}
|
||||||
<div className="grid grid-cols-1 gap-3 sm:gap-4 md:gap-4 md:grid-cols-2">
|
<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-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">
|
<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 />
|
<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>
|
</div>
|
||||||
</Card>
|
</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-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">
|
<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 />
|
<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 */}
|
{/* 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">
|
<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 */}
|
{/* Sidebar vertical - sticky */}
|
||||||
<aside className="sticky top-24 h-fit md:top-24">
|
<aside className="sticky top-24 h-fit md:top-24 z-40">
|
||||||
<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">
|
<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">
|
<div className="grid grid-cols-2 md:grid-cols-1 gap-1 sm:gap-1.5">
|
||||||
<Button
|
<Button
|
||||||
variant={tab==='dashboard'?'default':'ghost'}
|
variant={tab==='dashboard'?'default':'ghost'}
|
||||||
|
|||||||
@ -33,40 +33,32 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
|
|||||||
}, [dropdownOpen]);
|
}, [dropdownOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="h-16 border-b border-border bg-background px-6 flex items-center justify-between">
|
<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 flex-row items-center gap-4">
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
<SidebarTrigger />
|
<SidebarTrigger />
|
||||||
<div className="flex items-start flex-col justify-center py-2">
|
<div className="flex flex-col justify-center leading-tight min-w-0">
|
||||||
<h1 className="text-lg font-semibold text-foreground">{title}</h1>
|
<h1 className="text-sm sm:text-lg font-semibold text-foreground truncate max-w-[55vw] sm:max-w-none">{title}</h1>
|
||||||
<p className="text-muted-foreground">{subtitle}</p>
|
{subtitle && (
|
||||||
|
<p className="text-[11px] sm:text-xs text-muted-foreground truncate max-w-[55vw] sm:max-w-none">{subtitle}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex items-center gap-2 ml-auto">
|
||||||
<div className="flex items-center space-x-4">
|
<Button variant="ghost" size="icon" className="hover-primary-blue hidden xs:flex">
|
||||||
<Button variant="ghost" size="icon" className="hover-primary-blue">
|
|
||||||
<Bell className="h-4 w-4" />
|
<Bell className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<SimpleThemeToggle />
|
<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}>
|
<div className="relative" ref={dropdownRef}>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="relative h-8 w-8 rounded-full border-2 border-border hover:border-primary"
|
className="relative h-8 w-8 rounded-full border border-border hover:border-primary"
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
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">
|
<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 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 alt = user?.name || user?.email || 'Usuário'
|
||||||
|
|
||||||
const getInitials = (name?: string, email?: string) => {
|
const getInitials = (name?: string, email?: string) => {
|
||||||
if (name) {
|
if (name) {
|
||||||
const parts = name.trim().split(/\s+/)
|
const parts = name.trim().split(/\s+/)
|
||||||
@ -77,37 +69,32 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
|
|||||||
if (email) return email.charAt(0).toUpperCase()
|
if (email) return email.charAt(0).toUpperCase()
|
||||||
return 'U'
|
return 'U'
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<AvatarImage src={userPhoto || undefined} alt={alt} />
|
<AvatarImage src={userPhoto || undefined} alt={alt} />
|
||||||
<AvatarFallback className="bg-primary text-primary-foreground font-semibold">{getInitials(user?.name, user?.email)}</AvatarFallback>
|
<AvatarFallback className="bg-primary text-primary-foreground font-semibold">{getInitials(user?.name, user?.email)}</AvatarFallback>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})()
|
})()}
|
||||||
}
|
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Dropdown Content */}
|
|
||||||
{dropdownOpen && (
|
{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="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-4 border-b border-border">
|
<div className="p-3 sm:p-4 border-b border-border">
|
||||||
<div className="flex flex-col space-y-1">
|
<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'}
|
{user?.userType === 'administrador' ? 'Administrador da Clínica' : 'Usuário do Sistema'}
|
||||||
</p>
|
</p>
|
||||||
{user?.email ? (
|
{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'}
|
Tipo: {user?.userType === 'administrador' ? 'Administrador' : user?.userType || 'Não definido'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@ -115,21 +102,18 @@ export function PagesHeader({ title = "", subtitle = "" }: { title?: string, sub
|
|||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
router.push('/perfil');
|
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>
|
</button>
|
||||||
|
<div className="border-t border-border my-1" />
|
||||||
<div className="border-t border-border my-1"></div>
|
|
||||||
<button
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setDropdownOpen(false);
|
setDropdownOpen(false);
|
||||||
|
|
||||||
// Usar sempre o logout do hook useAuth (ele já redireciona corretamente)
|
|
||||||
logout();
|
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>
|
</button>
|
||||||
|
|||||||
@ -49,6 +49,23 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
|
|||||||
const chatEndRef = useRef<HTMLDivElement>(null);
|
const chatEndRef = useRef<HTMLDivElement>(null);
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(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(() => {
|
useEffect(() => {
|
||||||
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
@ -511,12 +528,11 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
|
|||||||
|
|
||||||
{/* Input unificado com ícones embutidos */}
|
{/* Input unificado com ícones embutidos */}
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
<div className={`relative flex items-center w-full rounded-full border ${themeClasses.border} ${themeClasses.inputBg} overflow-hidden h-11`}>
|
<div className={`flex items-center w-full rounded-full border ${themeClasses.border} ${themeClasses.inputBg} h-11 px-2 gap-2`}>
|
||||||
{/* Botão anexar (esquerda) */}
|
|
||||||
<button
|
<button
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
type="button"
|
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"
|
aria-label="Anexar arquivos"
|
||||||
>
|
>
|
||||||
<Plus className="w-4 h-4" />
|
<Plus className="w-4 h-4" />
|
||||||
@ -528,23 +544,20 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
|
|||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={(e) => handleFileSelect(e.target.files)}
|
onChange={(e) => handleFileSelect(e.target.files)}
|
||||||
/>
|
/>
|
||||||
{/* Textarea */}
|
|
||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={(e) => setInputValue(e.target.value)}
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
onKeyPress={handleKeyPress}
|
onKeyPress={handleKeyPress}
|
||||||
placeholder="Pergunte qualquer coisa para a Zoe"
|
placeholder={responsivePlaceholder}
|
||||||
rows={1}
|
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' }}
|
style={{ minHeight: 'auto', overflow: 'hidden' }}
|
||||||
/>
|
/>
|
||||||
{/* Ícones à direita */}
|
|
||||||
<div className="absolute right-2 flex items-center gap-2">
|
|
||||||
<button
|
<button
|
||||||
onClick={() => onOpenVoice?.()}
|
onClick={() => onOpenVoice?.()}
|
||||||
type="button"
|
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}`}
|
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"
|
aria-label="Entrada de voz"
|
||||||
>
|
>
|
||||||
<AudioLines className="w-4 h-4" />
|
<AudioLines className="w-4 h-4" />
|
||||||
@ -553,17 +566,12 @@ const FileUploadChat = ({ onOpenVoice }: { onOpenVoice?: () => void }) => {
|
|||||||
onClick={sendMessage}
|
onClick={sendMessage}
|
||||||
disabled={!inputValue.trim() && uploadedFiles.length === 0}
|
disabled={!inputValue.trim() && uploadedFiles.length === 0}
|
||||||
type="button"
|
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"
|
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"
|
aria-label="Enviar mensagem"
|
||||||
>
|
>
|
||||||
<Send className="w-4 h-4" />
|
<Send className="w-4 h-4" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* Contador de caracteres */}
|
|
||||||
{inputValue.length > 0 && (
|
|
||||||
<span className={`absolute bottom-1 right-24 text-[10px] ${themeClasses.textSecondary}`}>{inputValue.length}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user