estadisticas por estado, municipio y parroquia agregadas

This commit is contained in:
2026-04-16 17:08:14 -04:00
parent 548bb0cdb2
commit f9eef8ebd6
3 changed files with 125 additions and 24 deletions

View File

@@ -4,7 +4,7 @@ import { and, eq, gte, ilike, lte, or, SQL, sql } from 'drizzle-orm';
import { NodePgDatabase } from 'drizzle-orm/node-postgres'; import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider'; import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
import * as schema from 'src/database/index'; import * as schema from 'src/database/index';
import { states, trainingSurveys } from 'src/database/index'; import { municipalities, parishes, states, trainingSurveys } from 'src/database/index';
import { PaginationDto } from '../../common/dto/pagination.dto'; import { PaginationDto } from '../../common/dto/pagination.dto';
import { CreateTrainingDto } from './dto/create-training.dto'; import { CreateTrainingDto } from './dto/create-training.dto';
@@ -120,6 +120,8 @@ export class TrainingService {
isOpenSpaceDistribution, isOpenSpaceDistribution,
hasTransportDistribution, hasTransportDistribution,
genderResult, genderResult,
municipalityDistribution,
parishDistribution,
] = await Promise.all([ ] = await Promise.all([
// 1. Total OSPs // 1. Total OSPs
this.drizzle this.drizzle
@@ -275,6 +277,31 @@ export class TrainingService {
}) })
.from(trainingSurveys) .from(trainingSurveys)
.where(whereCondition), .where(whereCondition),
// 17. Distribución por Municipio
this.drizzle
.select({
name: sql<string>`COALESCE(${municipalities.name}, 'Sin Asignar')`,
value: sql<number>`count(${trainingSurveys.id})`,
})
.from(trainingSurveys)
.leftJoin(
municipalities,
eq(trainingSurveys.municipality, municipalities.id),
)
.where(whereCondition)
.groupBy(municipalities.name),
// 18. Distribución por Parroquia
this.drizzle
.select({
name: sql<string>`COALESCE(${parishes.name}, 'Sin Asignar')`,
value: sql<number>`count(${trainingSurveys.id})`,
})
.from(trainingSurveys)
.leftJoin(parishes, eq(trainingSurveys.parish, parishes.id))
.where(whereCondition)
.groupBy(parishes.name),
]); ]);
return { return {
@@ -334,6 +361,14 @@ export class TrainingService {
{ name: 'Mujeres', value: Number(genderResult[0]?.women || 0) }, { name: 'Mujeres', value: Number(genderResult[0]?.women || 0) },
{ name: 'Hombres', value: Number(genderResult[0]?.men || 0) }, { name: 'Hombres', value: Number(genderResult[0]?.men || 0) },
], ],
municipalityDistribution: municipalityDistribution.map((item) => ({
...item,
value: Number(item.value),
})),
parishDistribution: parishDistribution.map((item) => ({
...item,
value: Number(item.value),
})),
}; };
} }

View File

@@ -117,6 +117,8 @@ export function TrainingStatistics() {
isOpenSpaceDistribution, isOpenSpaceDistribution,
hasTransportDistribution, hasTransportDistribution,
genderDistribution, genderDistribution,
municipalityDistribution,
parishDistribution,
} = data; } = data;
const COLORS = [ const COLORS = [
@@ -279,8 +281,8 @@ export function TrainingStatistics() {
</CardContent> </CardContent>
</Card> </Card>
{/* State Distribution */} {/* Location Distribution (Dynamic) */}
{/* <Card className="col-span-full"> <Card className="col-span-full">
<CardHeader> <CardHeader>
<CardTitle>Distribución por Estado</CardTitle> <CardTitle>Distribución por Estado</CardTitle>
<CardDescription>OSP registradas por estado</CardDescription> <CardDescription>OSP registradas por estado</CardDescription>
@@ -300,7 +302,69 @@ export function TrainingStatistics() {
</BarChart> </BarChart>
</ResponsiveContainer> </ResponsiveContainer>
</CardContent> </CardContent>
</Card> */} </Card>
{stateId > 0 ? (
<Card className="col-span-full">
<CardHeader>
<CardTitle>Distribución por Municipio</CardTitle>
<CardDescription>OSP registradas en este estado</CardDescription>
</CardHeader>
<CardContent className="h-[400px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={municipalityDistribution}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip wrapperStyle={{ color: '#000' }} />
<Legend />
<Bar dataKey="value" fill="#0088FE" name="Cantidad" />
</BarChart>
</ResponsiveContainer>
</CardContent>
</Card>
) : (
<Card className="col-span-full">
<CardHeader>
<CardTitle>Distribución por Municipio</CardTitle>
<CardDescription>Seleccione un estado</CardDescription>
</CardHeader>
</Card>
)}
{municipalityId > 0 ? (
<Card className="col-span-full">
<CardHeader>
<CardTitle>Distribución por Parroquia</CardTitle>
<CardDescription>OSP registradas en este municipio</CardDescription>
</CardHeader>
<CardContent className="h-[400px]">
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={parishDistribution}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip wrapperStyle={{ color: '#000' }} />
<Legend />
<Bar dataKey="value" fill="#FFBB28" name="Cantidad" />
</BarChart>
</ResponsiveContainer>
</CardContent>
</Card>
) : (
<Card className="col-span-full">
<CardHeader>
<CardTitle>Distribución por Parroquia</CardTitle>
<CardDescription>Seleccione un municipio</CardDescription>
</CardHeader>
</Card>
)}
{/* Year Distribution */} {/* Year Distribution */}
<Card className="col-span-full lg:col-span-1"> <Card className="col-span-full lg:col-span-1">
@@ -483,7 +547,7 @@ export function TrainingStatistics() {
</Card> </Card>
{/* STRUCTURE TYPE DISTRIBUTION */} {/* STRUCTURE TYPE DISTRIBUTION */}
<Card className="col-span-full lg:col-span-1"> <Card className="col-span-full lg:col-span-2">
<CardHeader> <CardHeader>
<CardTitle>Tipo de Estructura</CardTitle> <CardTitle>Tipo de Estructura</CardTitle>
<CardDescription>Distribución física</CardDescription> <CardDescription>Distribución física</CardDescription>

View File

@@ -25,6 +25,8 @@ export const trainingStatisticsSchema = z.object({
isOpenSpaceDistribution: z.array(statisticsItemSchema), isOpenSpaceDistribution: z.array(statisticsItemSchema),
hasTransportDistribution: z.array(statisticsItemSchema), hasTransportDistribution: z.array(statisticsItemSchema),
genderDistribution: z.array(statisticsItemSchema), genderDistribution: z.array(statisticsItemSchema),
municipalityDistribution: z.array(statisticsItemSchema),
parishDistribution: z.array(statisticsItemSchema),
}); });
export type TrainingStatisticsData = z.infer<typeof trainingStatisticsSchema>; export type TrainingStatisticsData = z.infer<typeof trainingStatisticsSchema>;