infoalloggi-monorepo/apps/infoalloggi/src/components/Layout.tsx

346 lines
8.3 KiB
TypeScript
Raw Normal View History

import { getCookie } from "cookies-next";
2025-08-28 18:27:07 +02:00
import {
ExternalLink,
Mail,
MessagesSquare,
Paperclip,
Search,
ShoppingBag,
Tickets,
UserCog,
} from "lucide-react";
import Head from "next/head";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useRouter } from "next/router";
import { type ReactNode, useContext } from "react";
import toast from "react-hot-toast";
import {
SIDEBAR_COOKIE_NAME,
Sidebar,
} from "~/components/area-riservata/sidebar";
2025-08-04 17:45:44 +02:00
import { BannerFactory } from "~/components/banners";
import { Footer, MiniFooter } from "~/components/footer";
2025-08-28 18:27:07 +02:00
import { LoadingPage } from "~/components/loading";
2025-08-04 17:45:44 +02:00
import { SiteHeader } from "~/components/navbar/site-header";
import { WhatsAppIcon2 } from "~/components/svgs";
2025-08-04 17:45:44 +02:00
import { Button } from "~/components/ui/button";
import { Separator } from "~/components/ui/separator";
2025-08-28 18:27:07 +02:00
import { UserViewContext, UserViewProvider } from "~/lib/userViewContext";
import { cn } from "~/lib/utils";
2025-08-28 18:27:07 +02:00
import { useTranslation } from "~/providers/I18nProvider";
import {
2025-08-28 18:27:07 +02:00
EnforcedSessionContext,
useSession,
} from "~/providers/SessionProvider";
2025-08-28 18:27:07 +02:00
import type { UsersId } from "~/schemas/public/Users";
import type { ValidSession } from "~/server/api/trpc";
2025-08-28 18:27:07 +02:00
import { api } from "~/utils/api";
2025-08-04 17:45:44 +02:00
interface Props {
bodyClassName?: string;
containerClassName?: string;
2025-08-28 18:27:07 +02:00
children: ReactNode;
noFooter?: boolean;
footerClassName?: string;
2025-08-04 17:45:44 +02:00
}
export const Layout = ({
children,
noFooter,
bodyClassName,
containerClassName,
footerClassName,
}: Props) => {
2025-08-28 18:27:07 +02:00
const { data: bannerData } = api.settings.getBannerData.useQuery({
area_riservata: false,
});
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return (
<div className={cn("flex min-h-screen w-full flex-col", bodyClassName)}>
2025-08-28 18:27:07 +02:00
<SiteHeader />
{bannerData?.map((banner) => (
<div key={`banner-elem-${banner.idbanner}`}>
{BannerFactory(banner)}
</div>
))}
<main className={cn("h-full flex-1 grow", containerClassName)}>
{children}
</main>
2025-08-04 17:45:44 +02:00
{!noFooter && <Footer className={footerClassName} />}
2025-08-28 18:27:07 +02:00
{/* <CookieConsent /> */}
</div>
);
2025-08-04 17:45:44 +02:00
};
interface AreaRiservataLayoutProps extends Props {
ignoreSessionCheck?: boolean;
noSidebar?: boolean;
}
2025-08-04 17:45:44 +02:00
export const AreaRiservataLayout = ({
2025-08-28 18:27:07 +02:00
children,
noSidebar,
noFooter,
ignoreSessionCheck,
bodyClassName,
containerClassName,
footerClassName,
}: AreaRiservataLayoutProps) => {
2025-08-28 18:27:07 +02:00
const { data: bannerData } = api.settings.getBannerData.useQuery({
area_riservata: true,
});
const session = useSession();
if (!ignoreSessionCheck) {
if (session.status === "pending")
return (
<div
className={cn(
"flex h-full min-h-screen flex-col text-clip",
bodyClassName,
)}
>
2025-08-28 18:27:07 +02:00
<SiteHeader />
2025-08-04 17:45:44 +02:00
<main
className={cn(
"flex h-full flex-1 overflow-auto",
containerClassName,
)}
>
2025-08-28 18:27:07 +02:00
<div className="flex h-auto w-full flex-col md:flex-row">
<LoadingPage />
</div>
</main>
{!noFooter && <MiniFooter className={footerClassName} />}
2025-08-28 18:27:07 +02:00
</div>
);
if (session.status !== "success" || !session.user) {
window.location.reload();
return (
<div
className={cn(
"flex h-full min-h-screen flex-col text-clip",
bodyClassName,
)}
>
2025-08-28 18:27:07 +02:00
<SiteHeader />
<main
className={cn(
"flex h-full flex-1 overflow-auto",
containerClassName,
)}
>
2025-08-28 18:27:07 +02:00
<div className="flex h-auto w-full flex-col md:flex-row">
<LoadingPage />
</div>
</main>
{!noFooter && <MiniFooter className={footerClassName} />}
2025-08-28 18:27:07 +02:00
</div>
);
}
}
2025-08-28 18:27:07 +02:00
return (
<div
className={cn(
"flex h-full min-h-screen flex-col text-clip",
bodyClassName,
)}
>
2025-08-28 18:27:07 +02:00
<SiteHeader />
{bannerData?.map((banner) => (
<div key={`banner-elem-${banner.idbanner}`}>
{BannerFactory(banner)}
</div>
))}
<main
className={cn("flex h-full flex-1 overflow-auto", containerClassName)}
>
2025-08-28 18:27:07 +02:00
<div className="flex h-auto w-full flex-col md:flex-row">
{noSidebar ? null : (
<Sidebar
defaultOpen={getCookie(SIDEBAR_COOKIE_NAME) === "true"}
isAdmin={session.user?.isAdmin || false}
/>
2025-08-28 18:27:07 +02:00
)}
<EnforcedSessionContext.Provider value={session as ValidSession}>
{children}
</EnforcedSessionContext.Provider>
</div>
</main>
{!noFooter && <MiniFooter className={footerClassName} />}
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
{/* <CookieConsent /> */}
</div>
);
2025-08-04 17:45:44 +02:00
};
export const AreaRiservataLayoutUserView = ({
2025-08-28 18:27:07 +02:00
children,
2025-08-04 17:45:44 +02:00
}: {
2025-08-28 18:27:07 +02:00
children: ReactNode & { props: { userId: UsersId } };
2025-08-04 17:45:44 +02:00
}) => {
2025-08-28 18:27:07 +02:00
const { t } = useTranslation();
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
const props = children.props as { userId: UsersId };
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
return (
<div className="flex h-full min-h-screen flex-col bg-white">
<SiteHeader />
<main className="flex h-full flex-1 overflow-auto">
<UserViewProvider userId={props.userId}>
<Head>
<title>{t.heads.area_riservata_titolo}</title>
<meta
content={t.heads.area_riservata_description}
2025-08-29 16:18:32 +02:00
name="description"
2025-08-28 18:27:07 +02:00
/>
</Head>
<div className="flex h-auto w-full flex-col md:flex-row">
<Sidebar
defaultOpen={getCookie(SIDEBAR_COOKIE_NAME) === "true"}
isAdmin
/>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<div className="mx-4 my-3 flex-1 overflow-auto">
<UserViewHeader />
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
{children}
</div>
</div>
</UserViewProvider>
</main>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<MiniFooter />
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
{/* <CookieConsent /> */}
</div>
);
2025-08-04 17:45:44 +02:00
};
const UserViewHeader = () => {
2025-08-28 18:27:07 +02:00
const data = useContext(UserViewContext);
const pathname = usePathname();
const router = useRouter();
const { mutate: swap } = api.auth.swapUser.useMutation({
onSuccess: async () => {
toast.success("Utente cambiato con successo");
await router.push("/area-riservata/dashboard");
router.reload();
},
});
if (!data) return <div>Errore</div>;
return (
<div className="mb-5 flex flex-col space-y-5">
<div className="flex flex-row flex-wrap items-baseline gap-3">
<div className="text-3xl">{data.username}</div>
<div className="text-muted-foreground text-sm">
{data.email} | {data.telefono}
</div>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<Link href={`https://wa.me/${data.telefono}`} target="_blank">
<Button size="sm" variant="success">
<WhatsAppIcon2 className="size-4 fill-white" />
2025-08-28 18:27:07 +02:00
WhatsApp
</Button>
</Link>
</div>
<div className="flex flex-row flex-wrap gap-2">
<Link href={`/area-riservata/admin/user-view/edit-user/${data.id}`}>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={pathname.includes("edit-user") ? "secondary" : "outline"}
2025-08-28 18:27:07 +02:00
>
<UserCog /> Profilo
</Button>
</Link>
<Link href={`/area-riservata/admin/user-view/servizio/${data.id}`}>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={pathname.includes("servizio") ? "secondary" : "outline"}
2025-08-28 18:27:07 +02:00
>
<Tickets /> Servizi
</Button>
</Link>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<Link href={`/area-riservata/admin/user-view/ricerca/${data.id}`}>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={pathname.includes("ricerca") ? "secondary" : "outline"}
2025-08-28 18:27:07 +02:00
>
<Search /> Ricerca
</Button>
</Link>
<Link href={`/area-riservata/admin/user-view/comunicazioni/${data.id}`}>
<Button
className="border"
2025-08-29 16:18:32 +02:00
size="sm"
2025-08-28 18:27:07 +02:00
variant={
pathname.includes("comunicazioni") ? "secondary" : "outline"
}
>
<Mail /> Comunicazioni
</Button>
</Link>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<Link href={`/area-riservata/admin/user-view/ordini/${data.id}`}>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={pathname.includes("ordini") ? "secondary" : "outline"}
2025-08-28 18:27:07 +02:00
>
<ShoppingBag /> Ordini
</Button>
</Link>
<Link href={`/area-riservata/admin/user-view/allegati/${data.id}`}>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={pathname.includes("allegati") ? "secondary" : "outline"}
2025-08-28 18:27:07 +02:00
>
<Paperclip /> Allegati
</Button>
</Link>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<Link
href={
data.chatid
? {
pathname: "/area-riservata/admin/chats",
query: { chatid: data.chatid },
}
: "/area-riservata/admin/chats"
}
target="_blank"
>
<Button
className="border"
2025-08-28 18:27:07 +02:00
size="sm"
2025-08-29 16:18:32 +02:00
variant={!data.chatid ? "ghost" : "outline"}
2025-08-28 18:27:07 +02:00
>
<MessagesSquare /> Chat
</Button>
</Link>
2025-08-04 17:45:44 +02:00
2025-08-28 18:27:07 +02:00
<Button
className="flex gap-2"
onClick={() =>
swap({
id: data.id,
})
}
2025-08-29 16:18:32 +02:00
size="sm"
2025-08-28 18:27:07 +02:00
>
<ExternalLink /> Login
</Button>
</div>
<Separator />
</div>
);
2025-08-04 17:45:44 +02:00
};