infoalloggi-monorepo/apps/infoalloggi/src/components/chat/chat-topbar.tsx
Marco Pedone f85ea22215 chore: update biome schema and dependencies, refactor key usage in components
- Updated biome schema version from 2.2.2 to 2.3.1 in biome.json.
- Removed experimentalScannerIgnores from files section and replaced with ignore patterns.
- Added CSS parser configuration for Tailwind directives.
- Updated linter rules for better code quality.
- Refactored key usage in various components to use unique identifiers instead of array indices.
- Updated package-lock.json and package.json to reflect new biome version.
- General code cleanup and improvements across multiple components for better maintainability.
2025-10-28 11:55:01 +01:00

269 lines
6.8 KiB
TypeScript

"use client";
import {
Info,
Paperclip,
ShieldUser,
ShoppingBag,
User,
UserCog,
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { useCallback } from "react";
import toast from "react-hot-toast";
import { EtichetteDisplayer } from "~/components/etichette";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "~/components/ui/alert-dialog";
import { Button, buttonVariants } from "~/components/ui/button";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "~/components/ui/popover";
import { Separator } from "~/components/ui/separator";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "~/components/ui/tooltip";
import { UserAvatar } from "~/components/user_avatar";
import { cn } from "~/lib/utils";
import type { ChatsChatid } from "~/schemas/public/Chats";
import type { Session } from "~/server/api/trpc";
import type { ChatUserInfo } from "~/server/services/chat.service";
import type { OnlineStatus } from "~/server/sse";
import { api } from "~/utils/api";
type ChatTopbarProps = {
chatId: ChatsChatid;
userData: Session;
chatUserData: ChatUserInfo;
onlineStatus: OnlineStatus[];
};
export default function ChatTopbar({
chatId,
userData,
chatUserData,
onlineStatus,
}: ChatTopbarProps) {
const utils = api.useUtils();
const { mutate: deletechat } = api.chat.deleteChat.useMutation({
onSuccess: async () => {
await utils.chat.getActiveChats.invalidate();
await utils.users.getActiveUsersWithoutChat.invalidate();
await router.push("/area-riservata/admin/chats/");
},
});
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();
},
});
const handleDeleteChat = useCallback(() => {
deletechat({ chatId });
}, [chatId, deletechat]);
const handleSwapUser = useCallback(() => {
swap({ id: chatUserData.id });
}, [chatUserData, swap]);
return (
<div className="flex h-18 w-full items-center justify-between border-b px-4 py-2">
<div className="flex items-center gap-2">
{!userData.isAdmin ? (
<Image
alt="Infoalloggi logo"
className="size-10 rounded-full"
height={40}
src={"/favicon/favicon.png"}
width={40}
/>
) : (
<UserAvatar
className="size-10"
userId={chatUserData.id}
username={chatUserData.username}
/>
)}
<div className="flex flex-col gap-1">
<div className="flex gap-4">
<span className="font-medium">
{!userData.isAdmin ? "Chat Infoalloggi" : chatUserData.username}
</span>
{userData.isAdmin && <EtichetteDisplayer chatId={chatId} />}
</div>
<div className="flex flex-row items-center gap-2">
<UserStatus onlineStatus={onlineStatus} userData={userData} />
</div>
</div>
</div>
<div>
{userData.isAdmin && (
<AdminPopover
chatUserData={chatUserData}
handleDeleteChat={handleDeleteChat}
handleSwapUser={handleSwapUser}
/>
)}
</div>
</div>
);
}
const UserStatus = ({
onlineStatus,
userData,
}: {
onlineStatus: OnlineStatus[];
userData: Session;
}) => (
<div className="flex flex-row items-center gap-2">
{onlineStatus.map((user, idx) =>
user.isAdmin ? (
<TooltipProvider delayDuration={0} key={user.userId}>
<Tooltip>
<TooltipTrigger>
<span
className={cn(
"flex flex-row items-center font-bold text-green-500 text-xs",
)}
>
<ShieldUser className="mr-[0.2rem]" size={15} />
<span>
{user.nome}
{idx < onlineStatus.length - 1 && ","}
</span>
</span>
</TooltipTrigger>
<TooltipContent>
<p>Admin Infoalloggi</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<span
className={cn("flex flex-row font-bold text-green-500 text-xs")}
key={user.userId}
>
<span>
{user.nome}
{!userData.isAdmin && " (Tu)"}
{idx < onlineStatus.length - 1 && ", "}
</span>
</span>
),
)}
</div>
);
const AdminPopover = ({
chatUserData,
handleSwapUser,
handleDeleteChat,
}: {
chatUserData: ChatUserInfo;
handleSwapUser: () => void;
handleDeleteChat: () => void;
}) => (
<Popover>
<PopoverTrigger
className={cn(
buttonVariants({ size: "icon", variant: "outline" }),
"size-10",
"dark:bg-muted dark:text-muted-foreground dark:hover:bg-muted dark:hover:text-white",
)}
>
<Info className="text-muted-foreground" size={24} />
</PopoverTrigger>
<PopoverContent className="mr-4 w-fit px-4">
<div className="flex flex-col gap-2">
<Link
aria-label="Visualizza Profilo Utente"
className={cn(
buttonVariants({ variant: "outline" }),
"justify-start gap-2",
)}
href={`/area-riservata/admin/user-view/edit-user/${chatUserData.id}`}
>
<UserCog /> Profilo
</Link>
<Link
aria-label="Visualizza Ordini Utente"
className={cn(
buttonVariants({ variant: "outline" }),
"justify-start gap-2",
)}
href={`/area-riservata/admin/user-view/ordini/${chatUserData.id}`}
>
<ShoppingBag /> Ordini
</Link>
<Link
aria-label="Visualizza Allegati Utente"
className={cn(
buttonVariants({ variant: "outline" }),
"justify-start gap-2",
)}
href={`/area-riservata/admin/user-view/allegati/${chatUserData.id}`}
>
<Paperclip /> Allegati
</Link>
<button
aria-label="Cambia Utente"
className={cn(
buttonVariants({ variant: "outline" }),
"justify-start gap-2",
)}
onClick={handleSwapUser}
type="button"
>
<User /> Login
</button>
<Separator />
<AlertDialog>
<AlertDialogTrigger asChild>
<Button size="sm" variant="destructive">
Cancella Chat
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Sei assolutamente sicuro?</AlertDialogTitle>
<AlertDialogDescription>
Questa azione non può essere annullata. Questo eliminerà
definitivamente la chat e rimuoverà i dati dai nostri server.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Annulla</AlertDialogCancel>
<AlertDialogAction
aria-label="Continua Eliminazione Chat"
className={cn(buttonVariants({ variant: "destructive" }))}
onClick={handleDeleteChat}
>
Continua
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</PopoverContent>
</Popover>
);