Skip to content

Commit

Permalink
feat: add info in voluntometro
Browse files Browse the repository at this point in the history
  • Loading branch information
Pinx0 committed Nov 10, 2024
1 parent c4d6edd commit 38d5618
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 99 deletions.
40 changes: 14 additions & 26 deletions src/app/voluntometro/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import { helpRequestService } from '@/lib/service';
export const dynamic = 'force-dynamic';

export default async function Voluntometro() {
const pueblos = await helpRequestService.getTodaysCountByTown();
const count = await helpRequestService.getTodaysCount();
const towns = await helpRequestService.getTodaysCountByTown();

const getFechaHoy = () => {
const fecha = new Date();
Expand All @@ -17,23 +16,11 @@ export default async function Voluntometro() {
});
};

const getTopAndBottomPueblos = () => {
const sortedPueblos = [...pueblos].sort((a, b) => {
const volunteersDiffA = a.count - a.needHelp;
const volunteersDiffB = b.count - b.needHelp;
if (volunteersDiffA !== volunteersDiffB) {
return volunteersDiffB - volunteersDiffA;
} else {
return b.count - a.count;
}
});
const totalOffers = towns.reduce((sum, pueblo) => sum + (pueblo.offers_last_24h ?? 0), 0);
const totalSolicitudes = towns.reduce((sum, pueblo) => sum + (pueblo.needs_last_24h ?? 0), 0);

const sortedTowns = towns.sort((a, b) => (b.unassigned_needs ?? 0) - (a.unassigned_needs ?? 0));

return {
top: sortedPueblos.slice(0, 2),
bottom: sortedPueblos.slice(-2).reverse(),
all: sortedPueblos.reverse(),
};
};
return (
<div className="mx-auto p-4">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
Expand All @@ -51,7 +38,7 @@ export default async function Voluntometro() {
<HeartHandshake className="w-16 h-16 text-green-600" />
</div>
<div className="flex flex-col justify-center">
<h1 className="text-3xl font-bold text-gray-900">{count.ofertas}+</h1>
<h1 className="text-3xl font-bold text-gray-900">{totalOffers}+</h1>
<p className="text-lg font-medium text-gray-600">Nuevos voluntarios hoy</p>
</div>
</div>
Expand All @@ -60,18 +47,19 @@ export default async function Voluntometro() {
<Search className="w-16 h-16 text-red-600" />
</div>
<div className="flex flex-col justify-center">
<h1 className="text-3xl font-bold text-gray-900">{count.solicitudes}+</h1>
<h1 className="text-3xl font-bold text-gray-900">{totalSolicitudes}+</h1>
<p className="text-lg font-medium text-gray-600">Nuevas solicitudes hoy</p>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{getTopAndBottomPueblos().all.map(
(pueblo) =>
pueblo.needHelp > 0 && (
<TownCardInfo key={pueblo.id} pueblo={pueblo} route="/casos-activos/solicitudes/?pueblo=" />
),
)}
{sortedTowns.map((townSummary) => (
<TownCardInfo
key={townSummary.town_id}
townSummary={townSummary}
route="/casos-activos/solicitudes/?pueblo="
/>
))}
</div>
</div>
);
Expand Down
28 changes: 15 additions & 13 deletions src/components/TownCardInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
'use client';
import { useRouter } from 'next/navigation';
import React from 'react';
import { TownSummary } from '@/types/Town';

type PuebloInfo = {
pueblo: {
id: number;
name: string;
count: number;
needHelp: string;
};
townSummary: TownSummary;
route: string;
};

export default function TownCardInfo({ pueblo, route }: PuebloInfo) {
export default function TownCardInfo({ townSummary, route }: PuebloInfo) {
const router = useRouter();
return (
<div key={pueblo.name} className="bg-white rounded-lg flex flex-col justify-between shadow-lg p-6">
<div key={townSummary.town_id} className="bg-white rounded-lg flex flex-col justify-between shadow-lg p-6">
<div className="flex justify-between flex-col items-start gap-1 mb-4">
<h2 className="text-xl font-bold mb-1">{pueblo.name}</h2>
<h2 className="text-xl font-bold mb-1">{townSummary.town_name}</h2>
<div className="flex flex-col xl:flex-row items-start xl:items-center">
<div>
<span className="text-black font-bold">{pueblo.count}</span>
<span className="text-black font-bold">{townSummary.offers_last_24h}</span>
<span className="text-gray-500 ml-1">voluntarios</span>
</div>
<span className="text-semibold px-2 hidden xl:block">|</span>
<div>
<span className="text-black font-bold">{pueblo.needHelp}</span>
<span className="text-black font-bold">{townSummary.needs_last_24h}</span>
<span className="text-gray-500 ml-1">solicitudes de ayuda</span>
</div>
</div>
<div className="flex flex-col xl:flex-row items-start xl:items-center">
<div>
<span className="text-black font-bold">{townSummary.unassigned_needs}</span>
<span className="text-gray-500 ml-1">solicitudes sin atender</span>
</div>
</div>
</div>
<button
onClick={() => {
router.push(route + pueblo.id);
router.push(route + townSummary.town_id);
}}
className="bg-green-500 text-white px-4 py-2 rounded w-full hover:bg-green-600"
>
Ofrecer ayuda a alguien aqui
Ayudar a alguien aquí
</button>
</div>
);
Expand Down
65 changes: 5 additions & 60 deletions src/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,75 +125,20 @@ export const helpRequestService = {
if (error) throw error;
return data;
},
async getTodaysCount() {
const today = new Date().toISOString().split('T')[0];
const supabase = await getSupabaseClient();
const { count: solicitaCount, error: solicitaError } = await supabase
.from('help_requests')
.select('id', { count: 'exact' })
.eq('type', 'necesita')
.gte('created_at', today)
.lte('created_at', `${today}T23:59:59.999Z`);

const { count: ofreceCount, error: ofreceError } = await supabase
.from('help_requests')
.select('id', { count: 'exact' })
.eq('type', 'ofrece')
.gte('created_at', today)
.lte('created_at', `${today}T23:59:59.999Z`);

if (solicitaError) {
throw new Error('Error fetching solicita:', solicitaError);
}
if (ofreceError) {
throw new Error('Error fetching ofrece:', ofreceError);
}
return {
solicitudes: solicitaCount || 0,
ofertas: ofreceCount || 0,
};
},
async getTodaysCountByTown() {
const supabase = await getSupabaseClient();
const today = new Date().toISOString().split('T')[0];

const { data: towns, error: townError } = await supabase.from('towns').select('id, name');
const { data: towns, error: townError } = await supabase
.from('town_help_request_summary')
.select('*')
.or('offers_last_24h.gt.0,needs_last_24h.gt.0,unassigned_needs.gt.0');

if (townError) {
console.log('Error fetching towns:', townError);
throw townError;
}

const { data, error } = await supabase
.from('help_requests')
.select('*')
.in('type', ['ofrece', 'necesita'])
.gte('created_at', today)
.lte('created_at', `${today}T23:59:59.999Z`);

if (error) {
console.log('Error fetching help requests:', error);
throw error;
}

const volunteersCount = new Map();
const needHelpCount = new Map();

data.forEach((person) => {
const townId = person.town_id;
if (person.type === 'ofrece') {
volunteersCount.set(townId, (volunteersCount.get(townId) || 0) + 1);
} else if (person.type === 'necesita') {
needHelpCount.set(townId, (needHelpCount.get(townId) || 0) + 1);
}
});

return towns.map((town) => ({
id: town.id,
name: town.name ?? 'N/A',
count: volunteersCount.get(town.id) || 0,
needHelp: needHelpCount.get(town.id) || 0,
}));
return towns;
},
};

Expand Down
2 changes: 2 additions & 0 deletions src/types/Town.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Database } from '@/types/database';

export type Town = Database['public']['Tables']['towns']['Row'];

export type TownSummary = Database['public']['Views']['town_help_request_summary']['Row'];
24 changes: 24 additions & 0 deletions src/types/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ export type Database = {
user_id?: string | null;
};
Relationships: [
{
foreignKeyName: 'help_requests_town_id_fkey';
columns: ['town_id'];
isOneToOne: false;
referencedRelation: 'town_help_request_summary';
referencedColumns: ['town_id'];
},
{
foreignKeyName: 'help_requests_town_id_fkey';
columns: ['town_id'];
Expand Down Expand Up @@ -397,6 +404,13 @@ export type Database = {
user_id: string | null;
};
Relationships: [
{
foreignKeyName: 'help_requests_town_id_fkey';
columns: ['town_id'];
isOneToOne: false;
referencedRelation: 'town_help_request_summary';
referencedColumns: ['town_id'];
},
{
foreignKeyName: 'help_requests_town_id_fkey';
columns: ['town_id'];
Expand All @@ -406,6 +420,16 @@ export type Database = {
},
];
};
town_help_request_summary: {
Row: {
needs_last_24h: number | null;
offers_last_24h: number | null;
town_id: number | null;
town_name: string | null;
unassigned_needs: number | null;
};
Relationships: [];
};
};
Functions: {
[_ in never]: never;
Expand Down
22 changes: 22 additions & 0 deletions supabase/migrations/20241110204442_add view for voluntometro.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
create view town_help_request_summary as
select
t.id as town_id,
t.name as town_name,

-- Número de help_requests de type = "ofrece" en las últimas 24 horas
coalesce(sum(case when hr.type = 'ofrece' and hr.created_at >= now() - interval '24 hours' then 1 else 0 end), 0) as offers_last_24h,

-- Número de help_requests de type = "necesita" en las últimas 24 horas
coalesce(sum(case when hr.type = 'necesita' and hr.created_at >= now() - interval '24 hours' then 1 else 0 end), 0) as needs_last_24h,

-- Número de help_requests de type = "necesita" sin help_request_assignments
coalesce(sum(case when hr.type = 'necesita' and hr.status <> 'finished' and hra.help_request_id is null then 1 else 0 end), 0) as unassigned_needs

from
towns t
left join
help_requests hr on t.id = hr.town_id
left join
help_request_assignments hra on hr.id = hra.help_request_id
group by
t.id, t.name;

0 comments on commit 38d5618

Please sign in to comment.