teqo/src/components/campaign/shell/CampaignSidebar.tsx
2026-08-01 07:24:10 +00:00

169 lines
5.9 KiB
TypeScript

'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useState, type FormEvent, type ReactNode } from 'react'
import { logoutCampaign } from '@/app/(campaign)/campanha/actions/auth'
import { CampaignScopeBadge } from '@/components/campaign/shared/CampaignScopeBadge'
import { CampaignUserAvatar } from '@/components/campaign/shared/CampaignUserAvatar'
import { CampaignLogo } from '@/components/campaign/shell/campaign-logo'
import { MunicipalityNavSavedFilters } from '@/components/campaign/shell/MunicipalityNavSavedFilters'
import {
getCampaignNav,
getCampaignSecondaryNav,
isCampaignNavActive,
MUNICIPALITY_NAV_HREF,
type CampaignNavItem,
} from '@/components/campaign/shell/nav'
import { Button } from '@/components/ui/button'
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from '@/components/ui/Sidebar'
import { Spinner } from '@/components/ui/Spinner'
import { clearLastActedMunicipality } from '@/lib/campaignLastActedMunicipality'
import { clearCampaignPwaCaches } from '@/utilities/campaignPwaClient'
import type { CampaignUserShellView } from '@/utilities/campaignUserProfile'
import { campaignRoleLabels } from '@/utilities/campaignUserProfile'
import { clearMunicipalitySavedFilters } from '@/utilities/municipality/municipalitySavedFilters'
import { clearRecentVisits } from '@/utilities/recentVisits'
export type CampaignSidebarUser = CampaignUserShellView
const CampaignSidebarLink = ({
item,
isActive,
onNavigate,
children,
}: {
item: CampaignNavItem
isActive: boolean
onNavigate: () => void
/** Row affordances that sit beside the link — B18's disclosure and sub-list. */
children?: ReactNode
}) => (
<SidebarMenuItem>
<SidebarMenuButton asChild isActive={isActive}>
<Link href={item.href} onClick={onNavigate} aria-current={isActive ? 'page' : undefined}>
<item.icon />
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
{children}
</SidebarMenuItem>
)
export const CampaignSidebar = ({ user }: { user: CampaignSidebarUser }) => {
const pathname = usePathname()
const { isMobile, setOpenMobile } = useSidebar()
const [isLoggingOut, setIsLoggingOut] = useState(false)
const secondaryNav = getCampaignSecondaryNav(user.role)
const closeMobileSidebar = () => {
if (isMobile) setOpenMobile(false)
}
const handleLogout = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (isLoggingOut) return
setIsLoggingOut(true)
clearRecentVisits()
clearLastActedMunicipality()
clearMunicipalitySavedFilters()
await clearCampaignPwaCaches()
// logoutCampaign redirects — no need to reset the pending flag on success.
await logoutCampaign()
}
return (
<Sidebar collapsible="offcanvas" className="print:hidden">
<SidebarHeader className="border-b border-sidebar-border">
<Link
href="/campanha"
className="block w-full rounded-md outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring"
>
<CampaignLogo />
</Link>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<CampaignScopeBadge className="mb-3 w-fit">
{campaignRoleLabels[user.role]}
</CampaignScopeBadge>
<SidebarGroupContent>
<SidebarMenu>
{getCampaignNav(user.role).map((item) => (
<CampaignSidebarLink
key={item.href}
item={item}
isActive={isCampaignNavActive(pathname, item.href)}
onNavigate={closeMobileSidebar}
>
{item.href === MUNICIPALITY_NAV_HREF ? (
<MunicipalityNavSavedFilters onNavigate={closeMobileSidebar} />
) : null}
</CampaignSidebarLink>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
{/* Reference, not a destination: pinned to the foot of the nav so it
never competes with the places the mesa works every day. */}
{secondaryNav.length > 0 ? (
<SidebarGroup className="mt-auto">
<SidebarGroupContent>
<SidebarMenu>
{secondaryNav.map((item) => (
<CampaignSidebarLink
key={item.href}
item={item}
isActive={isCampaignNavActive(pathname, item.href)}
onNavigate={closeMobileSidebar}
/>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
) : null}
</SidebarContent>
<SidebarFooter className="border-t border-sidebar-border">
<Link
href="/campanha/perfil"
className="flex min-w-0 items-center gap-3 rounded-md px-1 py-2 outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring"
>
<CampaignUserAvatar name={user.name} avatarUrl={user.avatarUrl} size="sm" />
<div className="flex min-w-0 flex-col gap-0.5 leading-none">
<span className="truncate text-sm font-medium text-sidebar-foreground">
{user.name}
</span>
<span className="truncate text-xs text-muted-foreground">Meu perfil</span>
</div>
</Link>
<form onSubmit={handleLogout}>
<Button
type="submit"
variant="outline"
size="sm"
disabled={isLoggingOut}
className="min-h-11 w-full border-sidebar-border bg-sidebar font-medium text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
>
{isLoggingOut ? <Spinner aria-hidden="true" /> : null}
{isLoggingOut ? 'Saindo…' : 'Sair'}
</Button>
</form>
</SidebarFooter>
</Sidebar>
)
}