141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import type { Route } from './+types/route'
|
|
|
|
import * as cookie from 'cookie'
|
|
import { FlaskConicalIcon } from 'lucide-react'
|
|
import { useEffect } from 'react'
|
|
import { Outlet, type ShouldRevalidateFunctionArgs } from 'react-router'
|
|
|
|
import { userContext } from '@repo/auth/context'
|
|
import { authMiddleware } from '@repo/auth/middleware/auth'
|
|
import { ModeToggle, ThemedImage } from '@repo/ui/components/dark-mode'
|
|
import { NavUser } from '@repo/ui/components/nav-user'
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogTitle
|
|
} from '@repo/ui/components/ui/alert-dialog'
|
|
import {
|
|
SidebarInset,
|
|
SidebarProvider,
|
|
SidebarTrigger
|
|
} from '@repo/ui/components/ui/sidebar'
|
|
import { Toaster } from '@repo/ui/components/ui/sonner'
|
|
import { cn } from '@repo/ui/lib/utils'
|
|
|
|
import { AppSidebar } from '@/components/app-sidebar'
|
|
import {
|
|
useWorksapce,
|
|
WorkspaceProvider
|
|
} from '@/components/workspace-switcher'
|
|
import { workspaceContext, workspaceMiddleware } from '@/middleware/workspace'
|
|
|
|
// import { Notification } from '@/components/notification'
|
|
|
|
export const middleware: Route.MiddlewareFunction[] = [
|
|
authMiddleware,
|
|
workspaceMiddleware
|
|
]
|
|
|
|
export async function loader({ context, request }: Route.ActionArgs) {
|
|
const user = context.get(userContext)!
|
|
const workspace = context.get(workspaceContext)
|
|
const rawCookie = request.headers.get('cookie') || ''
|
|
const parsedCookies = cookie.parse(rawCookie)
|
|
const { sidebar_state = 'true' } = parsedCookies
|
|
|
|
return {
|
|
user,
|
|
sidebar_state,
|
|
...workspace
|
|
}
|
|
}
|
|
|
|
export function shouldRevalidate({
|
|
currentParams,
|
|
nextParams
|
|
}: ShouldRevalidateFunctionArgs) {
|
|
return currentParams.orgid !== nextParams.orgid
|
|
}
|
|
|
|
export default function Route({ loaderData }: Route.ComponentProps) {
|
|
const { user, sidebar_state, blocked, ...props } = loaderData
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== 'undefined' && window.rybbit) {
|
|
window.rybbit.identify(user.sub, {
|
|
username: user.email,
|
|
name: user.name,
|
|
email: user.email
|
|
})
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<>
|
|
{blocked ? (
|
|
<AlertDialog open={true}>
|
|
<AlertDialogContent>
|
|
<AlertDialogTitle>Serviço com acesso suspenso</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Seu acesso está temporariamente bloqueado devido a um pagamento em
|
|
atraso. Regularize para continuar usando a plataforma.
|
|
</AlertDialogDescription>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
) : null}
|
|
|
|
<WorkspaceProvider {...props}>
|
|
<SidebarProvider
|
|
defaultOpen={sidebar_state === 'true'}
|
|
className={cn('flex', blocked && 'pointer-events-none')}
|
|
>
|
|
<AppSidebar />
|
|
|
|
<SidebarInset className="relative flex flex-col flex-1 min-w-0">
|
|
<header
|
|
className="bg-background/15 backdrop-blur-sm
|
|
px-4 py-2 lg:py-4 sticky top-0 z-10"
|
|
>
|
|
<div className="container mx-auto flex items-center max-w-7xl">
|
|
<SidebarTrigger className="md:hidden" />
|
|
<ThemedImage className="max-md:hidden" />
|
|
|
|
<div className="ml-auto flex gap-2.5 items-center">
|
|
<TestMode />
|
|
{/*<Notification />*/}
|
|
<ModeToggle />
|
|
<NavUser user={user} excludeApps={['admin']} />
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="p-4">
|
|
<div className="container mx-auto relative max-w-7xl">
|
|
<Outlet />
|
|
|
|
<Toaster
|
|
position="top-center"
|
|
richColors={true}
|
|
duration={Infinity}
|
|
closeButton={true}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</SidebarInset>
|
|
</SidebarProvider>
|
|
</WorkspaceProvider>
|
|
</>
|
|
)
|
|
}
|
|
|
|
function TestMode() {
|
|
const { test_mode } = useWorksapce()
|
|
|
|
if (!test_mode) {
|
|
return null
|
|
}
|
|
|
|
return <FlaskConicalIcon className="size-3.5 text-muted-foreground" />
|
|
}
|