2025-08-28 18:27:07 +02:00
|
|
|
import { Plus } from "lucide-react";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useState } from "react";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Button } from "~/components/ui/button";
|
2025-08-04 17:45:44 +02:00
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogDescription,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/ui/dialog";
|
|
|
|
|
import {
|
2025-08-28 18:27:07 +02:00
|
|
|
Select,
|
|
|
|
|
SelectContent,
|
|
|
|
|
SelectItem,
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
SelectValue,
|
2025-08-04 17:45:44 +02:00
|
|
|
} from "~/components/ui/select";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Separator } from "~/components/ui/separator";
|
|
|
|
|
import { cn } from "~/lib/utils";
|
|
|
|
|
import type { ChatsChatid } from "~/schemas/public/Chats";
|
|
|
|
|
import type { EtichetteIdEtichetta } from "~/schemas/public/Etichette";
|
|
|
|
|
import { api } from "~/utils/api";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
export const EtichetteDisplayer = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
chatId,
|
|
|
|
|
className,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
chatId: ChatsChatid;
|
|
|
|
|
className?: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { data, isLoading } = api.chat.getChatEtichette.useQuery({
|
|
|
|
|
chatId,
|
|
|
|
|
});
|
|
|
|
|
const [openEdit, setOpenEdit] = useState(false);
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const handleOpen = (status: boolean) => {
|
|
|
|
|
setOpenEdit(status);
|
|
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
if ((!isLoading && !data) || !data) return null;
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<div className={cn("flex flex-wrap gap-2", className)}>
|
|
|
|
|
{data.map((etichetta) => (
|
|
|
|
|
<Etichetta
|
2025-08-29 16:18:32 +02:00
|
|
|
className="text-xs"
|
|
|
|
|
color_hex={etichetta.color_hex}
|
2025-08-28 18:27:07 +02:00
|
|
|
key={etichetta.id_etichetta}
|
|
|
|
|
title={etichetta.title}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
<button
|
|
|
|
|
className="flex items-center justify-center rounded-lg border p-1"
|
2025-08-29 16:18:32 +02:00
|
|
|
onClick={() => setOpenEdit(true)}
|
|
|
|
|
type="button"
|
2025-08-28 18:27:07 +02:00
|
|
|
>
|
|
|
|
|
<Plus className="size-4" />
|
|
|
|
|
</button>
|
|
|
|
|
<EtichetteModal
|
|
|
|
|
chatId={chatId}
|
|
|
|
|
onOpenChange={handleOpen}
|
2025-08-29 16:18:32 +02:00
|
|
|
open={openEdit}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const EtichetteModal = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
chatId,
|
|
|
|
|
open,
|
|
|
|
|
onOpenChange,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
chatId: ChatsChatid;
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange: (status: boolean) => void;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const [selected, setSelected] = useState<EtichetteIdEtichetta | null>(null);
|
|
|
|
|
const utils = api.useUtils();
|
|
|
|
|
const { data } = api.chat.getChatEtichette.useQuery({ chatId });
|
|
|
|
|
const { data: etichette } = api.settings.getAllEtichette.useQuery();
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const { mutate: remove } = api.chat.removeEtichettaFromChat.useMutation({
|
|
|
|
|
onSuccess: async () => {
|
|
|
|
|
await utils.chat.getChatEtichette.invalidate({ chatId });
|
|
|
|
|
await utils.chat.getActiveChats.invalidate();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
const { mutate: add } = api.chat.addEtichettaToChat.useMutation({
|
|
|
|
|
onSuccess: async () => {
|
|
|
|
|
await utils.chat.getChatEtichette.invalidate({ chatId });
|
|
|
|
|
await utils.chat.getActiveChats.invalidate();
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (!data) return null;
|
|
|
|
|
return (
|
2025-08-29 16:18:32 +02:00
|
|
|
<Dialog onOpenChange={onOpenChange} open={open}>
|
2025-08-28 18:27:07 +02:00
|
|
|
<DialogContent>
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Etichette Attive</DialogTitle>
|
|
|
|
|
<DialogDescription className="sr-only">Attive</DialogDescription>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
{data.map((etichetta, idx) => (
|
2025-08-29 16:18:32 +02:00
|
|
|
<div className="flex gap-2" key={idx}>
|
2025-08-28 18:27:07 +02:00
|
|
|
<Etichetta
|
|
|
|
|
color_hex={etichetta.color_hex}
|
2025-08-29 16:18:32 +02:00
|
|
|
title={etichetta.title}
|
2025-08-28 18:27:07 +02:00
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
remove({ chatId, etichettaId: etichetta.id_etichetta });
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Rimuovi
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
<Separator />
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<Select
|
|
|
|
|
onValueChange={(value) =>
|
|
|
|
|
setSelected(parseInt(value) as EtichetteIdEtichetta)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="w-[180px]">
|
|
|
|
|
<SelectValue placeholder="Seleziona" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{etichette?.map((etichetta, idx) => (
|
|
|
|
|
<SelectItem key={idx} value={String(etichetta.id_etichetta)}>
|
|
|
|
|
{etichetta.title}
|
|
|
|
|
</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (selected) {
|
|
|
|
|
add({ chatId, etichettaId: selected });
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Aggiungi
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const Etichetta = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
title,
|
|
|
|
|
color_hex,
|
|
|
|
|
className,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
title: string;
|
|
|
|
|
color_hex: string;
|
|
|
|
|
className?: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
return (
|
|
|
|
|
<span
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex w-fit items-center justify-center rounded-md px-1.5 text-sm whitespace-nowrap select-none",
|
|
|
|
|
className,
|
|
|
|
|
)}
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: color_hex,
|
|
|
|
|
color: colorIsDarkSimple(color_hex) ? "#FFFFFF" : "#000000",
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{title}
|
|
|
|
|
</span>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
2025-08-28 18:27:07 +02:00
|
|
|
const bigint = parseInt(hex.slice(1), 16);
|
|
|
|
|
return {
|
|
|
|
|
b: bigint & 255,
|
2025-08-29 16:18:32 +02:00
|
|
|
g: (bigint >> 8) & 255,
|
|
|
|
|
r: (bigint >> 16) & 255,
|
2025-08-28 18:27:07 +02:00
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function colorIsDarkSimple(color: string) {
|
2025-08-28 18:27:07 +02:00
|
|
|
if (color === "#FFFFFF") return false;
|
|
|
|
|
const { r, g, b } = hexToRgb(color);
|
|
|
|
|
return r * 0.299 + g * 0.587 + b * 0.114 <= 186;
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|