This commit is contained in:
Marco Pedone 2025-11-20 15:05:05 +01:00
parent 9c18ca37ab
commit e7e397759a
7 changed files with 301 additions and 129 deletions

View file

@ -1,117 +0,0 @@
"use client";
import { useState } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar";
import {
KanbanBoard,
KanbanCard,
KanbanCards,
KanbanHeader,
KanbanProvider,
} from "~/components/ui/kanban";
const columns = [
{ id: "todo", name: "To Do", color: "red" },
{ id: "inprogress", name: "In Progress", color: "blue" },
{ id: "done", name: "Done", color: "green" },
];
const exampleFeatures = [
{
id: "1",
name: "Design new homepage",
column: "todo",
startAt: new Date("2024-06-01"),
endAt: new Date("2024-06-10"),
owner: {
name: "Alice",
image: "https://i.pravatar.cc/150?img=1",
},
},
{
id: "2",
name: "Implement authentication",
column: "inprogress",
startAt: new Date("2024-06-05"),
endAt: new Date("2024-06-15"),
owner: {
name: "Bob",
image: "https://i.pravatar.cc/150?img=2",
},
},
{
id: "3",
name: "Set up database",
column: "done",
startAt: new Date("2024-05-20"),
endAt: new Date("2024-05-30"),
owner: {
name: "Charlie",
image: "https://i.pravatar.cc/150?img=3",
},
},
];
const dateFormatter = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
const shortDateFormatter = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
});
const Example = () => {
const [features, setFeatures] = useState(exampleFeatures);
return (
<KanbanProvider
columns={columns}
data={features}
onDataChange={setFeatures}
>
{(column) => (
<KanbanBoard 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>
</div>
</KanbanHeader>
<KanbanCards id={column.id}>
{(feature: (typeof features)[number]) => (
<KanbanCard
column={column.id}
id={feature.id}
key={feature.id}
name={feature.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">
{feature.name}
</p>
</div>
{feature.owner && (
<Avatar className="h-4 w-4 shrink-0">
<AvatarImage src={feature.owner.image} />
<AvatarFallback>
{feature.owner.name?.slice(0, 2)}
</AvatarFallback>
</Avatar>
)}
</div>
<p className="m-0 text-muted-foreground text-xs">
{shortDateFormatter.format(feature.startAt)} -{" "}
{dateFormatter.format(feature.endAt)}
</p>
</KanbanCard>
)}
</KanbanCards>
</KanbanBoard>
)}
</KanbanProvider>
);
};
export default Example;

View file

@ -0,0 +1,179 @@
"use client";
import { format } from "date-fns";
import {
KanbanBoard,
KanbanCard,
KanbanCards,
KanbanHeader,
KanbanProvider,
} from "~/components/ui/kanban";
import {
type ElementoPotenziali,
PotenzialiProvider,
usePotenziali,
} from "~/providers/PotenzialiProvider";
import type { UsersId } from "~/schemas/public/Users";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "../ui/context-menu";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { UserAvatar } from "../user_avatar";
const columns = [
{ id: "todo", name: "To Do", color: "red" },
{ id: "inprogress", name: "In Progress", color: "blue" },
{ id: "done", name: "Done", color: "green" },
];
const SampleData: ElementoPotenziali[] = [
{
id: "1",
name: "Mario Rossi",
column: "todo",
descrizione:
"Single, cerca appartamento in centro da subito, budget 800 euro",
createdAt: new Date(),
owner: {
userId: "019b9a25-eff1-420a-9017-b0add205d7b6" as UsersId,
username: "Marco Pedone",
},
},
{
id: "2",
name: "Luigi Bianchi",
column: "inprogress",
descrizione: "Coppia, cerca bilocale in periferia, budget 600 euro",
createdAt: new Date(),
owner: null,
},
{
id: "3",
name: "Giovanni Verdi",
column: "done",
descrizione:
"Famiglia con 2 figli, cerca trilocale vicino a scuole, budget 1000 euro",
createdAt: new Date(),
owner: {
userId: "019b9a25-eff1-420a-9017-b0add205d7b6" as UsersId,
username: "Marco Pedone",
},
},
];
const Potenziali = () => {
return (
<PotenzialiProvider data={SampleData}>
<Kaban />
</PotenzialiProvider>
);
};
const Kaban = () => {
const { data, updateData, setEditingId, editingId, removeItem } =
usePotenziali();
return (
<>
<KanbanProvider columns={columns} data={data} onDataChange={updateData}>
{(column) => (
<KanbanBoard 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>
</div>
</KanbanHeader>
<KanbanCards id={column.id}>
{(data: ElementoPotenziali) => (
<ContextMenu key={data.id}>
<ContextMenuTrigger>
<KanbanCard
column={column.id}
id={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>
</div>
{data.owner && (
<UserAvatar
className="size-6 shrink-0"
userId={data.owner.userId}
username={data.owner.username}
/>
)}
</div>
<p className="m-0 text-muted-foreground text-xs">
{format(data.createdAt, "dd/MM/yyyy HH:mm")}
</p>
</KanbanCard>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() => {
setEditingId(data.id);
}}
>
Modifica
</ContextMenuItem>
<ContextMenuItem onClick={() => removeItem(data.id)}>
Elimina
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)}
</KanbanCards>
</KanbanBoard>
)}
</KanbanProvider>
<Dialog
onOpenChange={(v) => {
if (!v) {
setEditingId(null);
}
}}
open={!!editingId}
>
<EditingContent />
</Dialog>
</>
);
};
const EditingContent = () => {
const { getEditingItem } = usePotenziali();
const item = getEditingItem();
if (!item) return null;
return (
<DialogContent>
<DialogHeader>
<DialogTitle>Modifica {item.name}</DialogTitle>
<DialogDescription className="sr-only">desc</DialogDescription>
</DialogHeader>
<div className="mt-4 flex flex-col gap-4">
<p>Descrizione: {item.descrizione}</p>
<p>Creato il: {format(item.createdAt, "dd/MM/yyyy HH:mm")}</p>
<p>Utente Assegnato: {item.owner ? item.owner.username : "Nessuno"}</p>
</div>
</DialogContent>
);
};
export default Potenziali;

View file

@ -605,6 +605,7 @@ export const en: LangDict = {
{ {
icon: "cog", icon: "cog",
items: [ items: [
{ href: "/area-riservata/admin/potenziali", title: "Potenziali" },
{ href: "/area-riservata/admin/utenti", title: "Users" }, { href: "/area-riservata/admin/utenti", title: "Users" },
{ href: "/area-riservata/admin/chats", title: "Chat List" }, { href: "/area-riservata/admin/chats", title: "Chat List" },
{ href: "/area-riservata/admin/ordini", title: "Orders" }, { href: "/area-riservata/admin/ordini", title: "Orders" },

View file

@ -610,6 +610,7 @@ export const it: LangDict = {
{ {
icon: "cog", icon: "cog",
items: [ items: [
{ href: "/area-riservata/admin/potenziali", title: "Potenziali" },
{ href: "/area-riservata/admin/utenti", title: "Utenti" }, { href: "/area-riservata/admin/utenti", title: "Utenti" },
{ href: "/area-riservata/admin/chats", title: "Lista Chat" }, { href: "/area-riservata/admin/chats", title: "Lista Chat" },
{ href: "/area-riservata/admin/ordini", title: "Ordini" }, { href: "/area-riservata/admin/ordini", title: "Ordini" },

View file

@ -0,0 +1,53 @@
import dynamic from "next/dynamic";
import { AreaRiservataLayout } from "~/components/Layout";
import { LoadingPage } from "~/components/loading";
import type { NextPageWithLayout } from "~/pages/_app";
const Potenziali = dynamic(
() => import("~/components/area-riservata/potenziali"),
{
loading: () => <LoadingPage />,
ssr: false,
},
);
const AdminPotenziali: NextPageWithLayout = () => {
return (
<div className="mx-1 flex-1 overflow-auto">
<div className="mx-auto pt-4">
<div className="mx-3 items-start justify-between md:flex">
<div className="flex max-w-lg items-center gap-3">
<h3 className="font-bold text-accent-foreground text-xl sm:text-2xl">
Potenziali
</h3>
</div>
</div>
<div className="p-4">
<Potenziali />
</div>
</div>
</div>
);
};
export default AdminPotenziali;
// export const getServerSideProps = (async (context) => {
// const access_token = context.req.cookies.access_token;
// const helpers = await TrpcAuthedFetchingIstance({ access_token });
// if (helpers) {
// await helpers.trpc.etichette.getAllEtichette.prefetch();
// return {
// props: {
// trpcState: helpers.trpc.dehydrate(),
// },
// };
// }
// return {
// props: {},
// };
// }) satisfies GetServerSideProps;
AdminPotenziali.getLayout = function getLayout(page) {
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
};

View file

@ -1,18 +1,7 @@
import dynamic from "next/dynamic";
import { LoadingPage } from "~/components/loading";
import type { NextPageWithLayout } from "./_app"; import type { NextPageWithLayout } from "./_app";
const Kaban = dynamic(() => import("~/components/KabanExample"), {
loading: () => <LoadingPage />,
ssr: false,
});
const Test: NextPageWithLayout = () => { const Test: NextPageWithLayout = () => {
return ( return <main className="flex flex-col gap-4 p-8">asdsad</main>;
<main className="flex flex-col gap-4 p-8">
<Kaban />
</main>
);
}; };
export default Test; export default Test;

View file

@ -0,0 +1,66 @@
import { createContext, useContext, useState } from "react";
import type { UsersId } from "~/schemas/public/Users";
export type ElementoPotenziali = {
id: string;
name: string;
column: string;
descrizione: string;
createdAt: Date;
owner: { userId: UsersId; username: string } | null;
};
type PotenzialiContextType = {
data: ElementoPotenziali[];
editingId: string | null;
updateData: (data: ElementoPotenziali[]) => void;
setEditingId: (id: string | null) => void;
getEditingItem: () => ElementoPotenziali | null;
addItem: (item: ElementoPotenziali) => void;
removeItem: (id: string) => void;
};
const PotenzialiContext = createContext<PotenzialiContextType | null>(null);
type PotenzialiProviderProps = {
children: React.ReactNode;
data: ElementoPotenziali[];
};
export const PotenzialiProvider = ({
children,
data: initial,
}: PotenzialiProviderProps) => {
const [data, setData] = useState<ElementoPotenziali[]>(initial);
const [editingId, setEditingId] = useState<string | null>(null);
const getEditingItem = () => {
return data.find((item) => item.id === editingId) || null;
};
const addItem = (item: ElementoPotenziali) => {
setData((prev) => [...prev, item]);
};
const removeItem = (id: string) => {
setData((prev) => prev.filter((item) => item.id !== id));
};
return (
<PotenzialiContext.Provider
value={{
data,
editingId,
updateData: setData,
setEditingId,
getEditingItem,
addItem,
removeItem,
}}
>
{children}
</PotenzialiContext.Provider>
);
};
export const usePotenziali = () => {
const context = useContext(PotenzialiContext);
if (!context) {
throw new Error("usePotenziali must be used within a PotenzialiProvider");
}
return context;
};