pot
This commit is contained in:
parent
9c18ca37ab
commit
e7e397759a
7 changed files with 301 additions and 129 deletions
|
|
@ -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;
|
|
||||||
179
apps/infoalloggi/src/components/area-riservata/potenziali.tsx
Normal file
179
apps/infoalloggi/src/components/area-riservata/potenziali.tsx
Normal 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;
|
||||||
|
|
@ -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" },
|
||||||
|
|
|
||||||
|
|
@ -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" },
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
66
apps/infoalloggi/src/providers/PotenzialiProvider.tsx
Normal file
66
apps/infoalloggi/src/providers/PotenzialiProvider.tsx
Normal 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;
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue