chore: reorganize project structure and ensure default queues
This commit is contained in:
parent
854887f499
commit
1cccb852a5
201 changed files with 417 additions and 838 deletions
|
|
@ -1,125 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import { type ReactNode, useMemo, useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { usePathname, useRouter } from "next/navigation"
|
||||
import { LogOut, PlusCircle } from "lucide-react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuth, signOut } from "@/lib/auth-client"
|
||||
|
||||
interface PortalShellProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const navItems = [
|
||||
{ label: "Meus chamados", href: "/portal/tickets" },
|
||||
{ label: "Abrir chamado", href: "/portal/tickets/new", icon: PlusCircle },
|
||||
]
|
||||
|
||||
export function PortalShell({ children }: PortalShellProps) {
|
||||
const pathname = usePathname()
|
||||
const router = useRouter()
|
||||
const { session, isCustomer } = useAuth()
|
||||
const [isSigningOut, setIsSigningOut] = useState(false)
|
||||
|
||||
const initials = useMemo(() => {
|
||||
const name = session?.user.name || session?.user.email || "Cliente"
|
||||
return name
|
||||
.split(" ")
|
||||
.slice(0, 2)
|
||||
.map((part) => part.charAt(0).toUpperCase())
|
||||
.join("")
|
||||
}, [session?.user.name, session?.user.email])
|
||||
|
||||
async function handleSignOut() {
|
||||
if (isSigningOut) return
|
||||
setIsSigningOut(true)
|
||||
toast.loading("Encerrando sessão...", { id: "portal-signout" })
|
||||
try {
|
||||
await signOut()
|
||||
toast.success("Sessão encerrada", { id: "portal-signout" })
|
||||
router.replace("/login")
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
toast.error("Não foi possível encerrar a sessão", { id: "portal-signout" })
|
||||
} finally {
|
||||
setIsSigningOut(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-gradient-to-b from-slate-50 via-slate-50 to-white">
|
||||
<header className="border-b border-slate-200 bg-white/90 backdrop-blur">
|
||||
<div className="mx-auto flex w-full max-w-6xl items-center justify-between gap-4 px-6 py-4">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-xs font-semibold uppercase tracking-[0.28em] text-neutral-500">
|
||||
Portal do cliente
|
||||
</span>
|
||||
<span className="text-lg font-semibold text-neutral-900">Sistema de chamados</span>
|
||||
</div>
|
||||
<nav className="flex items-center gap-3 text-sm font-medium">
|
||||
{navItems.map((item) => {
|
||||
const isActive = pathname === item.href || pathname.startsWith(`${item.href}/`)
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 rounded-full px-4 py-2 transition",
|
||||
isActive
|
||||
? "bg-neutral-900 text-white shadow-sm"
|
||||
: "bg-transparent text-neutral-700 hover:bg-neutral-100"
|
||||
)}
|
||||
>
|
||||
{Icon ? <Icon className="size-4" /> : null}
|
||||
{item.label}
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Avatar className="size-9 border border-slate-200">
|
||||
<AvatarImage src={session?.user.avatarUrl ?? undefined} alt={session?.user.name ?? ""} />
|
||||
<AvatarFallback>{initials}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex flex-col leading-tight">
|
||||
<span className="font-semibold text-neutral-900">{session?.user.name ?? "Cliente"}</span>
|
||||
<span className="text-xs text-neutral-500">{session?.user.email ?? ""}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={handleSignOut}
|
||||
disabled={isSigningOut}
|
||||
className="inline-flex items-center gap-2"
|
||||
>
|
||||
<LogOut className="size-4" />
|
||||
Sair
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-6 px-6 py-8">
|
||||
{!isCustomer ? (
|
||||
<div className="rounded-2xl border border-dashed border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">
|
||||
Este portal é voltado a clientes. Algumas ações podem não estar disponíveis para o seu perfil.
|
||||
</div>
|
||||
) : null}
|
||||
{children}
|
||||
</main>
|
||||
<footer className="border-t border-slate-200 bg-white/70">
|
||||
<div className="mx-auto flex w-full max-w-6xl items-center justify-between px-6 py-4 text-xs text-neutral-500">
|
||||
<span>© {new Date().getFullYear()} Sistema de chamados</span>
|
||||
<span>Suporte: suporte@sistema.dev</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue