diff --git a/apps/infoalloggi/src/components/servizio/servizio.tsx b/apps/infoalloggi/src/components/servizio/servizio.tsx index 1abfac2..ac9a3ef 100644 --- a/apps/infoalloggi/src/components/servizio/servizio.tsx +++ b/apps/infoalloggi/src/components/servizio/servizio.tsx @@ -287,6 +287,7 @@ const Main = () => { + {isAdmin && } ); } @@ -378,6 +379,7 @@ const Main = () => { + {isAdmin && } ); } diff --git a/apps/infoalloggi/src/components/tables/richieste-table.tsx b/apps/infoalloggi/src/components/tables/richieste-table.tsx new file mode 100644 index 0000000..6ad3afc --- /dev/null +++ b/apps/infoalloggi/src/components/tables/richieste-table.tsx @@ -0,0 +1,200 @@ +import type { ColumnDef } from "@tanstack/react-table"; +import { ExternalLink } from "lucide-react"; +import Link from "next/link"; +import { DataTable } from "~/components/custom_ui/data-table"; +import { DataTableColumnHeader } from "~/components/custom_ui/dataTable-header"; +import type { + PinnedFiltro, + SearchFiltro, +} from "~/components/custom_ui/dataTable-toolbar"; +import { Button } from "~/components/ui/button"; +import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum"; +import type { RichiesteData } from "~/server/controllers/servizio.controller"; + +type RichiesteTableProps = { + data: RichiesteData[]; +}; + +export const RichiesteTable = ({ data }: RichiesteTableProps) => { + const tabledata = data; + + const columns_titles = { + id: "ID Ordine", + username: "Utente", + created_at: "Creato il", + status: "Status", + tipo: "Tipo", + annunci: "Annunci", + actions: "Azioni", + }; + const statusOptions = [ + { label: "Attesa di attivazione", value: "attesa_attivazione" }, + { label: "Acconto pagato", value: "acconto_pagato" }, + { label: "Saldo pagato", value: "saldo_pagato" }, + { label: "Conferma in corso", value: "conferma" }, + ]; + + const type_options = [ + { label: "Transitorio", value: TipologiaPosizioneEnum.Transitorio }, + { label: "Stabile", value: TipologiaPosizioneEnum.Stabile }, + ]; + + // const [selectedOrdine, setSelectedOrdine] = useState( + // null, + // ); + + type Column = (typeof tabledata)[number]; + + const columns: ColumnDef[] = [ + { + accessorKey: "id", + cell: () => null, + enableHiding: false, + header: () => null, + }, + + { + accessorKey: "username", + cell: ({ row }) => { + return ( + + + + ); + }, + enableHiding: false, + header: ({ column }) => ( + + ), + }, + { + accessorKey: "created_at", + cell: ({ row }) => { + const date = row.getValue("created_at"); + return new Date(date as Date).toLocaleDateString(); + }, + enableHiding: false, + header: ({ column }) => ( + + ), + sortingFn: "datetime", + }, + { + accessorKey: "tipologia", + cell: ({ row }) => { + return row.original.tipologia.toString(); + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, + header: ({ column }) => ( + + ), + }, + + { + accessorKey: "status", + cell: ({ row }) => { + const annunci = row.original.annunci_servizio; + + const awaitingConferma = annunci.some( + (a) => a.user_confirmed_at !== null && !a.hasConfermaAdmin, + ); + if (awaitingConferma) { + return "Conferma in corso"; + } + + if (row.original.isOkSaldo) { + return "Saldo pagato"; + } + if (row.original.isOkAcconto) { + return "Acconto pagato"; + } + if (!row.original.onboardOk) { + return "Attesa di attivazione"; + } + }, + + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, + header: ({ column }) => ( + + ), + }, + { + accessorKey: "annunci_servizio", + cell: ({ row }) => { + const annunci = row.original.annunci_servizio; + return {annunci.length}; + }, + header: ({ column }) => ( + + ), + enableSorting: false, + }, + { + cell: () => { + return ( + + ); + }, + enableHiding: false, + id: "actions", + }, + ]; + + const searchFiltro: SearchFiltro = { + columnName: "id", + placeholder: "Cerca ordine...", + }; + + const pinnedFiltri: PinnedFiltro[] = [ + { + columnName: "status", + + options: statusOptions, + title: "Stato Ordine", + }, + { + columnName: "tipo", + options: type_options, + title: "Tipo Ordine", + }, + ]; + + return ( +
+ + + {/* + */} +
+ ); +}; diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/richieste.tsx b/apps/infoalloggi/src/pages/area-riservata/admin/richieste.tsx new file mode 100644 index 0000000..e980394 --- /dev/null +++ b/apps/infoalloggi/src/pages/area-riservata/admin/richieste.tsx @@ -0,0 +1,58 @@ +import type { GetServerSideProps } from "next"; +import Head from "next/head"; +import { AreaRiservataLayout } from "~/components/Layout"; +import { LoadingPage } from "~/components/loading"; +import { Status500 } from "~/components/status-page"; +import { RichiesteTable } from "~/components/tables/richieste-table"; +import type { NextPageWithLayout } from "~/pages/_app"; +import { TrpcAuthedFetchingIstance } from "~/server/utils/ssgHelper"; +import { api } from "~/utils/api"; + +/** + * Pagina di gestione richieste per admin: /area-riservata/admin/richieste + */ +const Richieste: NextPageWithLayout = () => { + const { data, isLoading } = api.servizio.getRichieste.useQuery(); + + if (isLoading) return ; + if (!data) return ; + + return ( + <> + + Richieste - Infoalloggi.it + + +
+
+
+

Richieste

+
+
+ +
+ + ); +}; +export default Richieste; + +export const getServerSideProps = (async (context) => { + const access_token = context.req.cookies.access_token; + + const helpers = await TrpcAuthedFetchingIstance({ access_token }); + if (helpers) { + await helpers.trpc.servizio.getRichieste.prefetch(); + return { + props: { + trpcState: helpers.trpc.dehydrate(), + }, + }; + } + return { + props: {}, + }; +}) satisfies GetServerSideProps; + +Richieste.getLayout = function getLayout(page) { + return {page}; +}; diff --git a/apps/infoalloggi/src/server/api/routers/servizio.ts b/apps/infoalloggi/src/server/api/routers/servizio.ts index d510b51..28b91dc 100644 --- a/apps/infoalloggi/src/server/api/routers/servizio.ts +++ b/apps/infoalloggi/src/server/api/routers/servizio.ts @@ -20,6 +20,7 @@ import { getCompatibileAnnunci, getDataPerAcquisto, getOrdineRicevutaData, + getRichieste, getServiziByUserId, getServizioDataById, InteractionLogicTipologia, @@ -119,6 +120,9 @@ export const servizioRouter = createTRPCRouter({ .query(async ({ input }) => { return await getAllServizioAnnunci(input.userId); }), + getRichieste: adminProcedure.query(async () => { + return await getRichieste(); + }), getServizioData: protectedProcedure .input( z.object({ diff --git a/apps/infoalloggi/src/server/controllers/servizio.controller.ts b/apps/infoalloggi/src/server/controllers/servizio.controller.ts index 73c2e56..77e573e 100644 --- a/apps/infoalloggi/src/server/controllers/servizio.controller.ts +++ b/apps/infoalloggi/src/server/controllers/servizio.controller.ts @@ -1,3 +1,4 @@ +import type { Override } from "TypeHelpers.type"; import { TRPCError } from "@trpc/server"; import { add } from "date-fns"; import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/postgres"; @@ -1399,3 +1400,67 @@ export const getOrdineRicevutaData = async ({ }); } }; +export type RichiesteData = Servizio & + Pick & { + annunci_servizio: (Override< + ServizioAnnunci, + { + created_at: string; + open_contatti_at: string | null; + user_confirmed_at: string | null; + accettato_conferma_at: string | null; + contratto_decorrenza: string | null; + contratto_scadenza: string | null; + } + > & + Pick)[]; + ordini_servizio: (Override< + Ordini, + { + created_at: string; + paid_at: string | null; + } + > & + Pick)[]; + }; + +export const getRichieste = async (): Promise => { + try { + return await db + .selectFrom("servizio") + .innerJoin("users", "users.id", "servizio.user_id") + .selectAll("servizio") + .select(["users.username"]) + .select((eb) => [ + jsonArrayFrom( + eb + .selectFrom("servizio_annunci") + .selectAll("servizio_annunci") + .innerJoin("annunci", "annunci.id", "servizio_annunci.annunci_id") + .select(["annunci.codice", "annunci.titolo_it"]) + .whereRef( + "servizio.servizio_id", + "=", + "servizio_annunci.servizio_id", + ) + .orderBy("servizio_annunci.created_at", "desc"), + ).as("annunci_servizio"), + jsonArrayFrom( + eb + .selectFrom("ordini") + .selectAll("ordini") + .innerJoin("prezziario", "prezziario.idprezziario", "ordini.packid") + .select("prezziario.testo_condizioni") + .whereRef("ordini.servizio_id", "=", "servizio.servizio_id") + .orderBy("ordini.created_at", "desc"), + ).as("ordini_servizio"), + ]) + .orderBy("servizio.created_at", "desc") + .execute(); + } catch (e) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: `Error fetching richieste: ${(e as Error).message}`, + }); + } +};