2025-12-02 16:14:37 +01:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { format } from "date-fns";
|
2025-12-03 13:56:53 +01:00
|
|
|
import { Plus } from "lucide-react";
|
|
|
|
|
import { useState } from "react";
|
2025-12-02 16:14:37 +01:00
|
|
|
import {
|
|
|
|
|
KanbanBoard,
|
|
|
|
|
KanbanCard,
|
|
|
|
|
KanbanCards,
|
|
|
|
|
KanbanHeader,
|
|
|
|
|
KanbanProvider,
|
|
|
|
|
} from "~/components/ui/kanban";
|
2025-12-03 13:56:53 +01:00
|
|
|
import { usePotenziali } from "~/providers/PotenzialiProvider";
|
|
|
|
|
import type { PotenzialiGroupsId } from "~/schemas/public/PotenzialiGroups";
|
|
|
|
|
import type { PotenzialeData } from "~/server/services/potenziali.service";
|
2025-12-02 16:14:37 +01:00
|
|
|
import {
|
|
|
|
|
Credenza,
|
|
|
|
|
CredenzaBody,
|
2025-12-03 14:51:35 +01:00
|
|
|
CredenzaClose,
|
2025-12-02 16:14:37 +01:00
|
|
|
CredenzaContent,
|
|
|
|
|
CredenzaDescription,
|
2025-12-03 14:51:35 +01:00
|
|
|
CredenzaFooter,
|
2025-12-02 16:14:37 +01:00
|
|
|
CredenzaHeader,
|
|
|
|
|
CredenzaTitle,
|
2025-12-03 13:56:53 +01:00
|
|
|
CredenzaTrigger,
|
2025-12-02 16:14:37 +01:00
|
|
|
} from "../custom_ui/credenza";
|
2025-12-03 13:56:53 +01:00
|
|
|
import Input from "../custom_ui/input";
|
|
|
|
|
import { Textarea } from "../custom_ui/textarea";
|
|
|
|
|
import { Button } from "../ui/button";
|
|
|
|
|
import { Label } from "../ui/label";
|
2025-12-02 16:14:37 +01:00
|
|
|
import { UserAvatar } from "../user_avatar";
|
|
|
|
|
|
2025-12-03 13:56:53 +01:00
|
|
|
export const Potenziali = () => {
|
2025-12-03 14:51:35 +01:00
|
|
|
const { data, columns, setData, updateCall, setEditing, editingItem } =
|
2025-12-03 13:56:53 +01:00
|
|
|
usePotenziali();
|
2025-12-02 16:14:37 +01:00
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<KanbanProvider
|
|
|
|
|
className="w-max"
|
|
|
|
|
columns={columns}
|
|
|
|
|
data={data}
|
2025-12-03 13:56:53 +01:00
|
|
|
onDataChange={setData}
|
|
|
|
|
onDragEnd={() => {
|
|
|
|
|
updateCall();
|
|
|
|
|
}}
|
2025-12-02 16:14:37 +01:00
|
|
|
>
|
|
|
|
|
{(column) => (
|
|
|
|
|
<KanbanBoard
|
|
|
|
|
className="w-fit overflow-auto"
|
|
|
|
|
id={column.id}
|
|
|
|
|
key={column.id}
|
|
|
|
|
>
|
|
|
|
|
<KanbanHeader>
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<div
|
|
|
|
|
className="h-2 w-2 rounded-full"
|
|
|
|
|
style={{ backgroundColor: column.color }}
|
|
|
|
|
/>
|
|
|
|
|
<span>{column.name}</span>
|
2025-12-03 13:56:53 +01:00
|
|
|
|
|
|
|
|
<NewCard columnId={column.id} />
|
2025-12-02 16:14:37 +01:00
|
|
|
</div>
|
|
|
|
|
</KanbanHeader>
|
|
|
|
|
<KanbanCards className="w-68" id={column.id}>
|
2025-12-03 13:56:53 +01:00
|
|
|
{(data: PotenzialeData) => (
|
2025-12-02 16:14:37 +01:00
|
|
|
<KanbanCard
|
|
|
|
|
cardOnClick={() => {
|
2025-12-03 14:51:35 +01:00
|
|
|
setEditing(data.id);
|
2025-12-02 16:14:37 +01:00
|
|
|
}}
|
|
|
|
|
column={column.id}
|
|
|
|
|
id={data.id}
|
|
|
|
|
key={data.id}
|
|
|
|
|
name={data.name}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex items-start justify-between gap-2">
|
|
|
|
|
<div className="flex flex-col gap-1">
|
|
|
|
|
<p className="m-0 flex-1 font-medium text-sm">
|
|
|
|
|
{data.name}
|
|
|
|
|
</p>
|
|
|
|
|
<p className="m-0 text-muted-foreground text-xs">
|
|
|
|
|
{data.descrizione}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2025-12-03 13:56:53 +01:00
|
|
|
{data.userid && (
|
2025-12-02 16:14:37 +01:00
|
|
|
<UserAvatar
|
|
|
|
|
className="size-6 shrink-0 text-sm"
|
2025-12-03 13:56:53 +01:00
|
|
|
userId={data.userid}
|
|
|
|
|
username={data.username || "Utente"}
|
2025-12-02 16:14:37 +01:00
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<p className="mt-2 text-muted-foreground text-xs">
|
2025-12-03 13:56:53 +01:00
|
|
|
{format(data.created_at, "dd/MM/yyyy HH:mm")}
|
2025-12-02 16:14:37 +01:00
|
|
|
</p>
|
|
|
|
|
</KanbanCard>
|
|
|
|
|
)}
|
|
|
|
|
</KanbanCards>
|
|
|
|
|
</KanbanBoard>
|
|
|
|
|
)}
|
|
|
|
|
</KanbanProvider>
|
|
|
|
|
<Credenza
|
|
|
|
|
onOpenChange={(v) => {
|
|
|
|
|
if (!v) {
|
2025-12-03 14:51:35 +01:00
|
|
|
setEditing(null);
|
2025-12-02 16:14:37 +01:00
|
|
|
}
|
|
|
|
|
}}
|
2025-12-03 14:51:35 +01:00
|
|
|
open={!!editingItem}
|
2025-12-02 16:14:37 +01:00
|
|
|
>
|
|
|
|
|
<EditingContent />
|
|
|
|
|
</Credenza>
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-03 13:56:53 +01:00
|
|
|
export const NewColumn = () => {
|
|
|
|
|
const [newColumnName, setNewColumnName] = useState("");
|
|
|
|
|
const { addColumn } = usePotenziali();
|
|
|
|
|
return (
|
|
|
|
|
<Credenza>
|
|
|
|
|
<CredenzaTrigger asChild>
|
|
|
|
|
<Button>Nuova colonna</Button>
|
|
|
|
|
</CredenzaTrigger>
|
|
|
|
|
<CredenzaContent className="max-h-[90vh]">
|
|
|
|
|
<CredenzaHeader>
|
|
|
|
|
<CredenzaTitle className="text-center font-semibold text-2xl">
|
|
|
|
|
Nuova colonna
|
|
|
|
|
</CredenzaTitle>
|
|
|
|
|
<CredenzaDescription className="sr-only">desc</CredenzaDescription>
|
|
|
|
|
</CredenzaHeader>
|
|
|
|
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto p-2">
|
|
|
|
|
<Label htmlFor="titolo">Titolo</Label>
|
|
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
defaultValue={newColumnName}
|
|
|
|
|
id="titolo"
|
|
|
|
|
onChange={(e) => setNewColumnName(e.target.value)}
|
|
|
|
|
type="string"
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
disabled={!newColumnName.length}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
addColumn(newColumnName);
|
|
|
|
|
setNewColumnName("");
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Aggiungi
|
|
|
|
|
</Button>
|
|
|
|
|
</CredenzaBody>
|
|
|
|
|
</CredenzaContent>
|
|
|
|
|
</Credenza>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const NewCard = ({ columnId }: { columnId: PotenzialiGroupsId }) => {
|
|
|
|
|
const [newItemName, setNewItemName] = useState("");
|
|
|
|
|
const [newItemDescrizione, setNewItemDescrizione] = useState("");
|
|
|
|
|
const { addItem } = usePotenziali();
|
2025-12-03 14:51:35 +01:00
|
|
|
const [open, setOpen] = useState(false);
|
|
|
|
|
const handleAddItem = () => {
|
|
|
|
|
addItem({
|
|
|
|
|
name: newItemName,
|
|
|
|
|
descrizione: newItemDescrizione,
|
|
|
|
|
potenziali_group_id: columnId,
|
|
|
|
|
});
|
|
|
|
|
setNewItemName("");
|
|
|
|
|
setNewItemDescrizione("");
|
|
|
|
|
setOpen(false);
|
|
|
|
|
};
|
2025-12-03 13:56:53 +01:00
|
|
|
return (
|
2025-12-03 14:51:35 +01:00
|
|
|
<Credenza onOpenChange={setOpen} open={open}>
|
2025-12-03 13:56:53 +01:00
|
|
|
<CredenzaTrigger asChild>
|
|
|
|
|
<Button className="ml-auto" size="sm">
|
|
|
|
|
<Plus className="size-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</CredenzaTrigger>
|
|
|
|
|
<CredenzaContent className="max-h-[90vh]">
|
|
|
|
|
<CredenzaHeader>
|
|
|
|
|
<CredenzaTitle className="text-center font-semibold text-2xl">
|
|
|
|
|
Nuova colonna
|
|
|
|
|
</CredenzaTitle>
|
|
|
|
|
<CredenzaDescription className="sr-only">desc</CredenzaDescription>
|
|
|
|
|
</CredenzaHeader>
|
|
|
|
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto p-2">
|
|
|
|
|
<Label htmlFor="titolo">Titolo</Label>
|
|
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
id="titolo"
|
|
|
|
|
onChange={(e) => setNewItemName(e.target.value)}
|
|
|
|
|
type="string"
|
2025-12-03 14:51:35 +01:00
|
|
|
value={newItemName}
|
2025-12-03 13:56:53 +01:00
|
|
|
/>
|
|
|
|
|
<Label htmlFor="descrizione">Descrizione</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
className="min-h-64"
|
|
|
|
|
id="descrizione"
|
|
|
|
|
onChange={(e) => setNewItemDescrizione(e.target.value)}
|
|
|
|
|
placeholder=""
|
2025-12-03 14:51:35 +01:00
|
|
|
value={newItemDescrizione}
|
2025-12-03 13:56:53 +01:00
|
|
|
/>
|
2025-12-03 14:51:35 +01:00
|
|
|
</CredenzaBody>
|
|
|
|
|
<CredenzaFooter>
|
|
|
|
|
<Button disabled={!newItemName.length} onClick={handleAddItem}>
|
2025-12-03 13:56:53 +01:00
|
|
|
Aggiungi
|
|
|
|
|
</Button>
|
2025-12-03 14:51:35 +01:00
|
|
|
<CredenzaClose>Chiudi</CredenzaClose>
|
|
|
|
|
</CredenzaFooter>
|
2025-12-03 13:56:53 +01:00
|
|
|
</CredenzaContent>
|
|
|
|
|
</Credenza>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2025-12-02 16:14:37 +01:00
|
|
|
const EditingContent = () => {
|
2025-12-03 14:51:35 +01:00
|
|
|
const { editingItem, updateItem } = usePotenziali();
|
|
|
|
|
|
|
|
|
|
const [state, setState] = useState<PotenzialeData | null>(editingItem);
|
|
|
|
|
const [hasChanges, setHasChanges] = useState(false);
|
|
|
|
|
if (!state) return null;
|
|
|
|
|
|
2025-12-02 16:14:37 +01:00
|
|
|
return (
|
|
|
|
|
<CredenzaContent className="max-h-[90vh]">
|
|
|
|
|
<CredenzaHeader>
|
|
|
|
|
<CredenzaTitle className="text-center font-semibold text-2xl">
|
2025-12-03 14:51:35 +01:00
|
|
|
Modifica {state.name}
|
2025-12-02 16:14:37 +01:00
|
|
|
</CredenzaTitle>
|
|
|
|
|
<CredenzaDescription className="sr-only">desc</CredenzaDescription>
|
|
|
|
|
</CredenzaHeader>
|
2025-12-03 14:51:35 +01:00
|
|
|
<CredenzaBody className="flex max-h-[80vh] flex-col gap-4 overflow-auto p-2">
|
|
|
|
|
<Label htmlFor="titolo">Titolo</Label>
|
|
|
|
|
|
|
|
|
|
<Input
|
|
|
|
|
id="titolo"
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
setState((prev) => prev && { ...prev, name: e.target.value });
|
|
|
|
|
setHasChanges(true);
|
|
|
|
|
}}
|
|
|
|
|
type="string"
|
|
|
|
|
value={state.name}
|
|
|
|
|
/>
|
|
|
|
|
<Label htmlFor="descrizione">Descrizione</Label>
|
|
|
|
|
<Textarea
|
|
|
|
|
className="min-h-64"
|
|
|
|
|
id="descrizione"
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
setState(
|
|
|
|
|
(prev) => prev && { ...prev, descrizione: e.target.value },
|
|
|
|
|
);
|
|
|
|
|
setHasChanges(true);
|
|
|
|
|
}}
|
|
|
|
|
placeholder=""
|
|
|
|
|
value={state.descrizione || undefined}
|
|
|
|
|
/>
|
2025-12-02 16:14:37 +01:00
|
|
|
</CredenzaBody>
|
2025-12-03 14:51:35 +01:00
|
|
|
<CredenzaFooter>
|
|
|
|
|
<Button
|
|
|
|
|
disabled={!hasChanges || !state}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
updateItem(state);
|
|
|
|
|
}}
|
|
|
|
|
type="button"
|
|
|
|
|
>
|
|
|
|
|
Salva
|
|
|
|
|
</Button>
|
|
|
|
|
<CredenzaClose>Chiudi</CredenzaClose>
|
|
|
|
|
</CredenzaFooter>
|
2025-12-02 16:14:37 +01:00
|
|
|
</CredenzaContent>
|
|
|
|
|
);
|
|
|
|
|
};
|