Skip to content

Commit

Permalink
Merge pull request #156 from pedrolivaresanchez/feat/filter-by-non-vo…
Browse files Browse the repository at this point in the history
…lunteers

Añadir filtro para solicitudes sin voluntarios
  • Loading branch information
Pinx0 authored Nov 8, 2024
2 parents ae73f81 + 04f2b0a commit 06e52ff
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 67 deletions.
157 changes: 94 additions & 63 deletions src/app/casos-activos/solicitudes/page.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Suspense, useEffect, useState } from 'react';
import { useEffect, useState, useCallback, useMemo, Suspense } from 'react';
import { HeartHandshake } from 'lucide-react';
import { supabase } from '@/lib/supabase/client';
import SolicitudCard from '@/components/SolicitudCard';
Expand All @@ -11,9 +11,15 @@ import { tiposAyudaOptions } from '@/helpers/constants';
import Modal from '@/components/Modal';
import { useModal } from '@/context/ModalProvider';
import { useTowns } from '@/context/TownProvider';
import { Toggle } from '@/components/Toggle';

const MODAL_NAME = 'solicitudes';

const itemsPerPage = 10;
const numPages = (count) => {
return Math.ceil(count / itemsPerPage) || 0;
};

export default function SolicitudesPage() {
return (
<Suspense>
Expand All @@ -35,38 +41,46 @@ function Solicitudes() {
const [currentCount, setCurrentCount] = useState(0);
const { toggleModal } = useModal();

const closeModal = () => {
const closeModal = useCallback(() => {
toggleModal(MODAL_NAME, false);
};
const itemsPerPage = 10;
const numPages = (count) => {
return Math.ceil(count / itemsPerPage) || 0;
};

const updateFilter = (filter, value) => {
const params = new URLSearchParams(searchParams.toString());
params.set(filter, value);
router.push(`?${params.toString()}`);
};
}, [toggleModal]);

const updateFilter = useCallback(
(filter, value) => {
const params = new URLSearchParams(searchParams.toString());
params.set(filter, value);
router.push(`?${params.toString()}`);
},
[searchParams, router],
);

const [filtroData, setFiltroData] = useState({
urgencia: searchParams.get('urgencia') || 'todas',
tipoAyuda: searchParams.get('tipoAyuda') || 'todas',
pueblo: searchParams.get('pueblo') || 'todos',
soloSinVoluntarios: searchParams.get('soloSinVoluntarios') || true,
});

const changeDataFilter = (type, newFilter) => {
setFiltroData((prev) => ({
...prev,
[type]: newFilter,
}));
updateFilter(type, newFilter);
};

function changePage(newPage) {
setCurrentPage(newPage);
updateFilter('page', newPage);
}
const changeDataFilter = useCallback(
(type, newFilter) => {
setFiltroData((prev) => ({
...prev,
[type]: newFilter,
}));
updateFilter(type, newFilter);
},
[updateFilter, setFiltroData],
);

const changePage = useCallback(
(newPage) => {
setCurrentPage(newPage);
updateFilter('page', newPage);
},
[updateFilter],
);

const handleToggleChange = useCallback((e) => changeDataFilter('soloSinVoluntarios', e.target.checked), []);

useEffect(() => {
async function fetchData() {
Expand All @@ -91,6 +105,12 @@ function Solicitudes() {
if (filtroData.urgencia !== 'todas') {
query.eq('urgency', filtroData.urgencia);
}

// Solo agregar filtro si es true
if (!!filtroData.soloSinVoluntarios) {
query.eq('asignees_count', 0);
}

query.neq('status', 'finished');
// Ejecutar la consulta con paginación
const { data, count, error } = await query
Expand All @@ -115,6 +135,8 @@ function Solicitudes() {
fetchData();
}, [filtroData, currentPage]);

const puebloSeleccionado = useMemo(() => getTownById(Number(filtroData.pueblo)), [filtroData, getTownById]);

if (loading) {
return (
<div className="flex justify-center items-center min-h-screen">
Expand All @@ -131,48 +153,57 @@ function Solicitudes() {
);
}

const puebloSeleccionado = getTownById(Number(filtroData.pueblo));

return (
<>
{/* FILTROS */}
<div className="flex flex-col sm:flex-row gap-2 items-center justify-between">
<p className="font-bold text-md">Filtros</p>
<div className="flex flex-col sm:flex-row gap-2 w-full justify-end">
<select
value={filtroData.tipoAyuda}
onChange={(e) => changeDataFilter('tipoAyuda', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todas">Todas las necesidades</option>
{Object.entries(tiposAyudaOptions).map(([key, label]) => (
<option key={key} value={key}>
{label}
</option>
))}
</select>
<select
value={filtroData.urgencia}
onChange={(e) => changeDataFilter('urgencia', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todas">Todas las prioridades</option>
<option value="alta">Alta prioridad</option>
<option value="media">Media prioridad</option>
<option value="baja">Baja prioridad</option>
</select>
<select
value={filtroData.pueblo}
onChange={(e) => changeDataFilter('pueblo', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todos">Todos los pueblos</option>
{towns.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
<div className="flex flex-col space-y-3 w-full">
<div className="flex flex-col sm:flex-row gap-2 w-full justify-end">
<div className="flex flex-col justify-center">
<p className="font-bold text-md">Filtros</p>
</div>
<select
value={filtroData.tipoAyuda}
onChange={(e) => changeDataFilter('tipoAyuda', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todas">Todas las necesidades</option>
{Object.entries(tiposAyudaOptions).map(([key, label]) => (
<option key={key} value={key}>
{label}
</option>
))}
</select>
<select
value={filtroData.urgencia}
onChange={(e) => changeDataFilter('urgencia', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todas">Todas las prioridades</option>
<option value="alta">Alta prioridad</option>
<option value="media">Media prioridad</option>
<option value="baja">Baja prioridad</option>
</select>
<select
value={filtroData.pueblo}
onChange={(e) => changeDataFilter('pueblo', e.target.value)}
className="px-4 py-2 rounded-lg w-full border border-gray-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white text-gray-900 shadow-sm"
>
<option value="todos">Todos los pueblos</option>
{towns.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</select>
</div>
<div className="flex flex-row flex-1 justify-end">
<Toggle
handleChange={handleToggleChange}
checked={filtroData.soloSinVoluntarios}
label="Sólo ofertas sin voluntarios"
/>
</div>
</div>
</div>
<div className="grid gap-4">
Expand Down
33 changes: 33 additions & 0 deletions src/components/Toggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';

type ToggleProps = { handleChange: React.ChangeEventHandler<HTMLInputElement>; checked: boolean; label: string };

export const Toggle = ({ checked, handleChange, label }: ToggleProps) => {
return (
<div className="flex items-center space-x-2">
<label htmlFor="toggle" className="font-medium">
{label}
</label>
<div
className={`relative inline-block w-10 h-6 rounded-full transition-colors duration-300 ${
checked ? 'bg-green-500' : 'bg-gray-300'
}`}
>
<input
type="checkbox"
id="toggle"
className="absolute w-0 h-0 opacity-0"
checked={checked}
onChange={handleChange}
/>
<label htmlFor="toggle">
<span
className={`absolute top-0.5 left-0.5 w-5 h-5 rounded-full bg-white transition-transform duration-300 transform ${
checked ? 'translate-x-4' : ''
}`}
></span>
</label>
</div>
</div>
);
};
40 changes: 37 additions & 3 deletions src/lib/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,48 @@ export const helpRequestService = {

async assign(requestData: HelpRequestAssignmentInsert) {
const { data, error } = await supabase.from('help_request_assignments').insert([requestData]).select();

if (error) throw error;

const { data: linkedRequestData, error: errorGettingLinkedData } = await supabase
.from('help_requests')
.select('*')
.eq('id', requestData.help_request_id);
if (errorGettingLinkedData) throw errorGettingLinkedData;
if (!linkedRequestData) throw new Error('No se puede encontrar esta tarea');

const { error: errorUpdatingAssigneesCount } = await supabase
.from('help_requests')
.update({ asignees_count: linkedRequestData[0].asignees_count + 1 });
if (errorUpdatingAssigneesCount) throw errorUpdatingAssigneesCount;

return data[0];
},
async unassign(id: number) {
const { error } = await supabase.from('help_request_assignments').delete().eq('id', id);
const { data, error: errorFindingRow } = await supabase.from('help_request_assignments').select('*').eq('id', id);
if (errorFindingRow || !data) {
throw new Error('No se puede encontrar la tarea');
}

if (error) throw error;
const requestId = data[0].help_request_id;

const { error: errorDeletingAssignment } = await supabase.from('help_request_assignments').delete().eq('id', id);
if (errorDeletingAssignment) throw errorDeletingAssignment;

const { data: linkedRequestData, error: errorGettingLinkedData } = await supabase
.from('help_requests')
.select('*')
.eq('id', requestId);

if (errorGettingLinkedData) throw errorGettingLinkedData;
if (!linkedRequestData) throw new Error('No se puede encontrar esta tarea');

const { asignees_count } = linkedRequestData[0];
const newNumberAssignees = asignees_count <= 0 ? 0 : asignees_count - 1;

const { error: errorUpdatingAssigneesCount } = await supabase
.from('help_requests')
.update({ asignees_count: newNumberAssignees });
if (errorUpdatingAssigneesCount) throw errorUpdatingAssigneesCount;
},

async getByType(type: any) {
Expand Down
3 changes: 3 additions & 0 deletions src/types/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export type Database = {
help_requests: {
Row: {
additional_info: Json | null;
asignees_count: number;
contact_info: string | null;
coordinates: unknown | null;
created_at: string | null;
Expand All @@ -192,6 +193,7 @@ export type Database = {
};
Insert: {
additional_info?: Json | null;
asignees_count?: number;
contact_info?: string | null;
coordinates?: unknown | null;
created_at?: string | null;
Expand All @@ -213,6 +215,7 @@ export type Database = {
};
Update: {
additional_info?: Json | null;
asignees_count?: number;
contact_info?: string | null;
coordinates?: unknown | null;
created_at?: string | null;
Expand Down
2 changes: 1 addition & 1 deletion supabase/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ enabled = true
# Specifies an ordered list of seed files to load during db reset.
# Supports glob patterns relative to supabase directory. For example:
# sql_paths = ['./seeds/*.sql', '../project-src/seeds/*-load-testing.sql']
sql_paths = ['./seed.sql']
sql_paths = ['./towns.sql', './help_requests.sql']

[realtime]
enabled = true
Expand Down
11 changes: 11 additions & 0 deletions supabase/help_requests.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
INSERT INTO "public"."help_requests" ("id","created_at","type","name","location","description","urgency","number_of_people","contact_info","additional_info","status","resources","latitude","longitude","help_type","people_needed","town_id","other_help")
VALUES ('10851', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Lorem Ipsum', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10852', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Lorem Ipsum', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10853', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Lorem Ipsum', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10854', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10855', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10856', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10857', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10858', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10859', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null),
('10860', '2024-11-07 18:31:47.83578+00', 'necesita', 'Rocío', '46200, P', 'Me traslado desde r con l', 'alta', '1', '123456789', '{"email": "[email protected]", "consent": true, "special_situations": null}', 'active', null, '39.1', '-0.4', '{"alojamiento"}', '1', '17', null);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table "public"."help_requests" add column "asignees_count" smallint not null default '0'::smallint;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
UPDATE "public"."help_requests"
SET "asignees_count" = (
SELECT COUNT(*)
FROM "public"."help_request_assignments"
WHERE "public"."help_request_assignments"."help_request_id" = "public"."help_requests"."id"
);
File renamed without changes.

0 comments on commit 06e52ff

Please sign in to comment.