feat: add incroci page and API integration for managing incroci data

This commit is contained in:
Marco Pedone 2026-03-04 19:56:09 +01:00
parent 5579dfeed0
commit 81337c852f
6 changed files with 166 additions and 0 deletions

View file

@ -659,6 +659,7 @@ export const en: LangDict = {
{ 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" },
{ href: "/area-riservata/admin/incroci", title: "Incroci" },
{ href: "/area-riservata/admin/annunci", title: "Ads" }, { href: "/area-riservata/admin/annunci", title: "Ads" },
{ href: "/area-riservata/admin/prezziario", title: "Pricing" }, { href: "/area-riservata/admin/prezziario", title: "Pricing" },
], ],

View file

@ -664,6 +664,7 @@ export const it: LangDict = {
{ 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" },
{ href: "/area-riservata/admin/incroci", title: "Incroci" },
{ href: "/area-riservata/admin/annunci", title: "Annunci" }, { href: "/area-riservata/admin/annunci", title: "Annunci" },
{ href: "/area-riservata/admin/prezziario", title: "Prezziario" }, { href: "/area-riservata/admin/prezziario", title: "Prezziario" },
], ],

View file

@ -0,0 +1,118 @@
import { format } from "date-fns";
import type { GetServerSideProps } from "next";
import Head from "next/head";
import Link from "next/link";
import { AreaRiservataLayout } from "~/components/Layout";
import { LoadingPage } from "~/components/loading";
import { Status500 } from "~/components/status-page";
import { Button } from "~/components/ui/button";
import { Separator } from "~/components/ui/separator";
import type { NextPageWithLayout } from "~/pages/_app";
import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper";
import { api } from "~/utils/api";
const Incroci: NextPageWithLayout = () => {
const { data, isLoading } = api.incroci.getAll.useQuery();
if (isLoading) return <LoadingPage />;
if (!data) return <Status500 />;
return (
<>
<Head>
<title>Incorici - Infoalloggi.it</title>
</Head>
<div className="mx-3 flex flex-1 flex-col gap-4 overflow-auto pt-5">
<div className="items-start justify-between md:flex">
<div className="mx-1 flex items-center">
<h3 className="font-bold text-2xl">Incroci</h3>
</div>
</div>
<div className="space-y-4">
{data.map((incrocio) => (
<div className="rounded-md border p-4" key={incrocio.id}>
<div className="flex items-center gap-2 font-bold text-xl">
<span>Cod:</span>
<span>{incrocio.codice}</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Titolo:</span>
<span>{incrocio.titolo_it}</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Richieste:</span>
</div>
<ul className="ml-4 space-y-2">
{incrocio.richieste.map((r) => (
<li key={r.servizio_id}>
<div className="flex items-center gap-2">
<span className="font-bold">Username:</span>
<span>{r.username}</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Aggiunto il:</span>
<span>
{format(new Date(r.created_at), "dd/MM/yyyy")}
</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Aperto Contatti il:</span>
<span>
{r.open_contatti_at
? format(new Date(r.open_contatti_at), "dd/MM/yyyy")
: "Non aperto"}
</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Mandato conferma il:</span>
<span>
{r.user_confirmed_at
? format(new Date(r.user_confirmed_at), "dd/MM/yyyy")
: "Non confermato"}
</span>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">Conferma da Admin:</span>
<span>{r.hasConfermaAdmin ? "Si" : "No"}</span>
</div>
<Link
href={`/area-riservata/admin/user-view/servizio/${r.userId}/${r.servizio_id}`}
target="_blank"
>
<Button size="sm">Apri Servizio</Button>
</Link>
<Separator className="mt-2" />
</li>
))}
</ul>
</div>
))}
</div>
</div>
</>
);
};
export default Incroci;
export const getServerSideProps = (async (context) => {
const access_token = context.req.cookies.access_token;
const helpers = await TrpcAuthedFetchingIstance({ access_token });
if (helpers) {
await helpers.trpc.incroci.getAll.prefetch();
return {
props: {
trpcState: helpers.trpc.dehydrate(),
},
};
}
return {
props: {},
};
}) satisfies GetServerSideProps;
Incroci.getLayout = function getLayout(page) {
return <AreaRiservataLayout>{page}</AreaRiservataLayout>;
};

View file

@ -22,6 +22,7 @@ import { bannersRouter } from "./routers/banners";
import { catastoRouter } from "./routers/catasto"; import { catastoRouter } from "./routers/catasto";
import { etichetteRouter } from "./routers/etichette"; import { etichetteRouter } from "./routers/etichette";
import { flagsRouter } from "./routers/flags"; import { flagsRouter } from "./routers/flags";
import { incrociRouter } from "./routers/incroci";
import { inviteRouter } from "./routers/invite"; import { inviteRouter } from "./routers/invite";
import { potenzialiRouter } from "./routers/potenziali"; import { potenzialiRouter } from "./routers/potenziali";
import { revalidationRouter } from "./routers/revalidation"; import { revalidationRouter } from "./routers/revalidation";
@ -59,5 +60,6 @@ export const appRouter = createTRPCRouter({
revalidation: revalidationRouter, revalidation: revalidationRouter,
potenziali: potenzialiRouter, potenziali: potenzialiRouter,
invite: inviteRouter, invite: inviteRouter,
incroci: incrociRouter,
}); });
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;

View file

@ -0,0 +1,8 @@
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
import { getAll } from "~/server/controllers/incroci.controller";
export const incrociRouter = createTRPCRouter({
getAll: adminProcedure.query(async () => {
return await getAll();
}),
});

View file

@ -0,0 +1,36 @@
import { jsonArrayFrom } from "kysely/helpers/postgres";
import { db } from "../db";
export const getAll = async () => {
const data = await db
.selectFrom("annunci")
.innerJoin("servizio_annunci", "servizio_annunci.annunci_id", "annunci.id")
.select((eb) => [
"annunci.id",
"annunci.titolo_it",
"annunci.codice",
jsonArrayFrom(
eb
.selectFrom("servizio_annunci")
.select([
"servizio_annunci.open_contatti_at",
"servizio_annunci.servizio_id",
"servizio_annunci.created_at",
"servizio_annunci.user_confirmed_at",
"servizio_annunci.hasConfermaAdmin",
])
.leftJoin(
"servizio",
"servizio.servizio_id",
"servizio_annunci.servizio_id",
)
.leftJoin("users", "users.id", "servizio.user_id")
.select(["users.username", "users.id as userId"])
.whereRef("servizio_annunci.annunci_id", "=", "annunci.id"),
).as("richieste"),
])
.groupBy("annunci.id")
.execute();
return data;
};