2025-08-04 17:45:44 +02:00
|
|
|
import { sql } from "kysely";
|
2025-08-05 19:23:55 +02:00
|
|
|
import type { StackedTimeserie } from "~/components/timeserieBarChartStacked";
|
2025-08-28 18:27:07 +02:00
|
|
|
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
|
|
|
|
|
import { adminProcedure, createTRPCRouter } from "~/server/api/trpc";
|
|
|
|
|
import { db } from "~/server/db";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
export const statsRouter = createTRPCRouter({
|
2025-08-28 18:27:07 +02:00
|
|
|
adminDashStats: adminProcedure.query(async () => {
|
|
|
|
|
const userTimeserie = (
|
|
|
|
|
await db
|
|
|
|
|
.selectFrom("users")
|
|
|
|
|
.select((eb) => [
|
|
|
|
|
sql<Date>`date_trunc('day', created_at)`.as("date"),
|
|
|
|
|
eb.fn.count<number>("id").as("value"),
|
|
|
|
|
])
|
|
|
|
|
.where("isAdmin", "=", false)
|
|
|
|
|
.groupBy("date")
|
|
|
|
|
.orderBy("date")
|
|
|
|
|
.execute()
|
|
|
|
|
).map((item) => ({
|
|
|
|
|
date: item.date,
|
|
|
|
|
value: parseInt(String(item.value)),
|
|
|
|
|
}));
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const serviziTimeserie = {
|
|
|
|
|
data: Object.values(
|
|
|
|
|
(
|
|
|
|
|
await db
|
|
|
|
|
.selectFrom("servizio")
|
|
|
|
|
.select((eb) => [
|
|
|
|
|
sql<Date>`date_trunc('day', created_at)`.as("date"),
|
|
|
|
|
eb.fn.count<number>("servizio_id").as("value"),
|
|
|
|
|
"tipologia",
|
|
|
|
|
])
|
|
|
|
|
.groupBy(["date", "tipologia"])
|
|
|
|
|
.orderBy("date")
|
|
|
|
|
.execute()
|
|
|
|
|
).reduce(
|
|
|
|
|
(acc, item) => {
|
|
|
|
|
const dateStr = item.date.toISOString().slice(0, 10);
|
|
|
|
|
if (!acc[dateStr]) {
|
|
|
|
|
acc[dateStr] = {
|
|
|
|
|
date: item.date,
|
|
|
|
|
valueA: 0,
|
|
|
|
|
valueB: 0,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if (item.tipologia === TipologiaPosizioneEnum.Transitorio) {
|
|
|
|
|
acc[dateStr].valueA = parseInt(String(item.value));
|
|
|
|
|
} else if (item.tipologia === TipologiaPosizioneEnum.Stabile) {
|
|
|
|
|
acc[dateStr].valueB = parseInt(String(item.value));
|
|
|
|
|
}
|
|
|
|
|
return acc;
|
|
|
|
|
},
|
|
|
|
|
{} as Record<string, StackedTimeserie>,
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-08-29 16:18:32 +02:00
|
|
|
valueALabel: "Transitorio",
|
|
|
|
|
valueBLabel: "Stabile",
|
2025-08-28 18:27:07 +02:00
|
|
|
};
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const contattiTimeserie = (
|
|
|
|
|
await db
|
|
|
|
|
.selectFrom("servizio_annunci")
|
|
|
|
|
.select((eb) => [
|
|
|
|
|
sql<Date>`date_trunc('day', created_at)`.as("date"),
|
|
|
|
|
eb.fn
|
|
|
|
|
.count<number>(eb.fn("concat", ["servizio_id", "annunci_id"]))
|
|
|
|
|
.as("value"),
|
|
|
|
|
])
|
|
|
|
|
.where("open_contatti_at", "is not", null)
|
|
|
|
|
.groupBy("date")
|
|
|
|
|
.orderBy("date")
|
|
|
|
|
.execute()
|
|
|
|
|
).map((item) => ({
|
|
|
|
|
date: item.date,
|
|
|
|
|
value: parseInt(String(item.value)),
|
|
|
|
|
}));
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
const userCount =
|
|
|
|
|
(
|
|
|
|
|
await db
|
|
|
|
|
.selectFrom("users")
|
|
|
|
|
.select((eb) => [eb.fn.count<number>("id").as("count")])
|
|
|
|
|
.executeTakeFirst()
|
|
|
|
|
)?.count || 0;
|
2025-08-05 19:23:55 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
return {
|
|
|
|
|
contattiTimeserie,
|
|
|
|
|
serviziTimeserie,
|
2025-08-29 16:18:32 +02:00
|
|
|
userCount,
|
|
|
|
|
userTimeserie,
|
2025-08-28 18:27:07 +02:00
|
|
|
};
|
|
|
|
|
}),
|
2025-08-04 17:45:44 +02:00
|
|
|
});
|