Skip to content

Commit

Permalink
Merge pull request #86 from pedrolivaresanchez/feature/asignar-volunt…
Browse files Browse the repository at this point in the history
…ario

Asignar voluntario a petición de ayuda
  • Loading branch information
robertobobby1 authored Nov 6, 2024
2 parents 0e2ce95 + ec53a46 commit 00e5032
Show file tree
Hide file tree
Showing 23 changed files with 438 additions and 107 deletions.
40 changes: 39 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
"@react-google-maps/api": "^2.20.3",
"@supabase/ssr": "^0.5.1",
"@supabase/supabase-js": "^2.46.1",
"@tanstack/react-query": "^5.59.19",
"deck.gl": "^9.0.34",
"leaflet": "^1.9.4",
"lucide-react": "^0.454.0",
"maplibre-gl": "^4.7.1",
"next": "15.0.2",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"sonner": "^1.7.0"
},
"devDependencies": {
"@types/node": "^22.8.7",
Expand All @@ -32,9 +34,9 @@
"eslint-config-prettier": "^9.1.0",
"postcss": "^8",
"prettier": "3.3.3",
"supabase": "^1.215.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.6.3",
"supabase": "^1.215.0"
"typescript": "^5.6.3"
},
"packageManager": "[email protected]+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
}
30 changes: 16 additions & 14 deletions src/app/layout.js → src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,40 @@ import './globals.css';
import 'leaflet/dist/leaflet.css';
import EmergencyLayout from '@/components/layout/EmergencyLayout';
import { EmergencyProvider } from '@/context/EmergencyProvider';
import { TownsProvider } from '../context/TownProvider';
import { TownsProvider } from '@/context/TownProvider';
import { createClient } from '@/lib/supabase/server';
import { SessionProvider } from '../context/SessionProvider';
import { SessionProvider } from '@/context/SessionProvider';
import { townsService } from '@/lib/service';
import { Toaster } from 'sonner';
import { PropsWithChildren } from 'react';
import { QueryClientProvider } from '@/context/QueryClientProvider';

export const metadata = {
title: 'Ajuda Dana - Sistema de Coordinación',
description: 'Sistema de coordinación para emergencias en la Comunidad Valenciana',
};
const getTowns = async () => {
const supabase = await createClient();
const { data, error } = await supabase.from('towns').select('id, name');
return data;
};

const getSession = async () => {
const supabase = await createClient();
const { data, error } = await supabase.auth.getUser();
return data;
};

export default async function RootLayout({ children }) {
export default async function RootLayout({ children }: PropsWithChildren) {
const session = await getSession();
const towns = await getTowns();
const towns = await townsService.getTowns();
return (
<html lang="es">
<body suppressHydrationWarning={true}>
<Toaster position="bottom-left" richColors />
<SessionProvider session={session}>
<TownsProvider towns={towns}>
<EmergencyProvider>
<EmergencyLayout>{children}</EmergencyLayout>
</EmergencyProvider>
</TownsProvider>
<QueryClientProvider>
<TownsProvider towns={towns}>
<EmergencyProvider>
<EmergencyLayout>{children}</EmergencyLayout>
</EmergencyProvider>
</TownsProvider>
</QueryClientProvider>
</SessionProvider>
</body>
</html>
Expand Down
104 changes: 104 additions & 0 deletions src/components/AsignarSolicitudButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';

import { useSession } from '@/context/SessionProvider';
import { HelpRequestAssignmentData, HelpRequestData } from '@/types/Requests';
import { helpRequestService } from '@/lib/service';
import { MouseEvent } from 'react';
import { Spinner } from '@/components/Spinner';
import Link from 'next/link';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';

type AsignarSolicitudButtonProps = {
helpRequest: HelpRequestData;
};

export default function AsignarSolicitudButton({ helpRequest }: AsignarSolicitudButtonProps) {
const session = useSession();

const {
data: assignments,
isLoading,
error,
} = useQuery<HelpRequestAssignmentData[]>({
queryKey: ['help_request_assignments', { id: helpRequest.id }],
queryFn: () => helpRequestService.getAssignments(helpRequest.id),
});

const queryClient = useQueryClient();

const assignMutation = useMutation({
mutationFn: async () => {
if (!session.user) return;
await helpRequestService.assign({
help_request_id: helpRequest.id,
user_id: session.user.id,
phone_number: session.user.user_metadata.telefono!,
});
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['help_request_assignments'] });
},
onError: (e) => {
console.error('Error al asignarte a la petición de ayuda', e);
toast.error('Error al asignarte :(');
},
});

const unassignMutation = useMutation({
mutationFn: async () => {
if (!session.user) return;
if (!userAssignment) return;
await helpRequestService.unassign(userAssignment.id);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['help_request_assignments'] });
},
onError: (e) => {
console.error('Error al asignarte a la petición de ayuda', e);
toast.error('Error al asignarte :(');
},
});

async function handleSubmit(e: MouseEvent) {
e.preventDefault();
assignMutation.mutate();
}
async function handleCancel(e: MouseEvent) {
e.preventDefault();
unassignMutation.mutate();
}

if (isLoading) return <Spinner />;
if (error || assignments === undefined) return <></>;

const userAssignment = assignments.find((x) => x.user_id === session.user?.id);
const userIsAssigned = !!userAssignment;

if (!session || !session.user)
return (
<Link href="/auth" className={`rounded-lg text-white py-2 px-4 w-full sm:w-auto text-center bg-green-500`}>
Quiero ayudar
</Link>
);

return (
<>
{userIsAssigned ? (
<button
onClick={handleCancel}
className={`rounded-lg text-white py-2 px-4 w-full sm:w-auto text-center bg-red-500`}
>
Cancelar mi ayuda
</button>
) : (
<button
onClick={handleSubmit}
className={`rounded-lg text-white py-2 px-4 w-full sm:w-auto text-center bg-green-500`}
>
Quiero ayudar
</button>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
import { AlertTriangle, Calendar, MapPin, MapPinned, Megaphone, Phone, Users } from 'lucide-react';
import { tiposAyudaOptions } from '@/helpers/constants';
import Link from 'next/link';
import { useSession } from '../context/SessionProvider';
import { useSession } from '@/context/SessionProvider';
import { HelpRequestAdditionalInfo, HelpRequestData } from '@/types/Requests';
import { Town } from '@/types/Town';
import AsignarSolicitudButton from '@/components/AsignarSolicitudButton';
import SolicitudHelpCount from '@/components/SolicitudHelpCount';

type SolicitudCardProps = {
caso: HelpRequestData;
towns: Town[];
isHref?: boolean;
button?: SolicitudCardButton;
isEdit?: boolean;
};

type SolicitudCardButton = {
text: string;
link: string;
};

export default function SolicitudCard({
caso,
isHref,
towns,
isHref = true,
button = { text: 'Ver solicitud', link: '/solicitud/' },
isEdit = false,
towns,
}) {
}: SolicitudCardProps) {
const session = useSession();

const additionalInfo = caso.additional_info as HelpRequestAdditionalInfo;

const special_situations = 'special_situations' in additionalInfo ? additionalInfo.special_situations : undefined;
const email = 'email' in additionalInfo ? additionalInfo.email : undefined;
return (
<>
<div
Expand All @@ -32,6 +54,7 @@ export default function SolicitudCard({
{caso.name || 'Necesita Ayuda'}
</h3>
<div>
<SolicitudHelpCount id={caso.id} />
<span className={`px-3 py-1 rounded-full text-sm font-medium whitespace-nowrap mr-2 bg-purple-300`}>
Referencia: {caso.id}
</span>
Expand All @@ -55,7 +78,7 @@ export default function SolicitudCard({
<div className="flex items-start gap-2">
<MapPinned className="h-4 w-4 text-gray-500 flex-shrink-0 mt-1" />
<span className="break-words">
<span className="font-semibold">Pueblo:</span> {towns[caso.town_id - 1].name || ''}
<span className="font-semibold">Pueblo:</span> {towns[caso.town_id - 1]?.name || ''}
</span>
</div>
)}
Expand All @@ -69,9 +92,9 @@ export default function SolicitudCard({
<Calendar className="h-4 w-4 text-gray-500 flex-shrink-0 mt-1" />
<span className="break-words">
<span className="font-semibold">Fecha:</span>{' '}
{new Date(caso.created_at).toLocaleDateString() +
{new Date(caso.created_at!).toLocaleDateString() +
' ' +
new Date(caso.created_at).toLocaleTimeString([], {
new Date(caso.created_at!).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
})}
Expand Down Expand Up @@ -127,10 +150,10 @@ export default function SolicitudCard({
</span>
</div>
)}
{caso.additional_info?.special_situations && (
{special_situations && (
<div className="mt-2 bg-gray-50 p-3 rounded">
<span className="font-semibold block mb-1">Situaciones especiales:</span>
<p className="text-gray-700 break-words">{caso.additional_info.special_situations}</p>
<p className="text-gray-700 break-words">{special_situations}</p>
</div>
)}
{caso.number_of_people && (
Expand All @@ -143,20 +166,16 @@ export default function SolicitudCard({
)}
</div>
<div className="flex flex-col sm:flex-row w-full sm:w-auto justify-end gap-2">
{session &&
session.user &&
session.user.email &&
session.user.email === caso.additional_info.email &&
!isEdit && (
<Link
href={'/solicitudes/editar/' + caso.id}
className={`rounded-lg text-white py-2 px-4 w-full sm:w-auto text-center ${
caso.urgency === 'alta' ? 'bg-red-500' : caso.urgency === 'media' ? 'bg-yellow-500' : 'bg-green-500'
}`}
>
Editar
</Link>
)}
{session && session.user && session.user.email && session.user.email === email && !isEdit && (
<Link
href={'/solicitudes/editar/' + caso.id}
className={`rounded-lg text-white py-2 px-4 w-full sm:w-auto text-center ${
caso.urgency === 'alta' ? 'bg-red-500' : caso.urgency === 'media' ? 'bg-yellow-500' : 'bg-green-500'
}`}
>
Editar
</Link>
)}
{isHref && (
<Link
href={button.link + caso.id}
Expand All @@ -167,6 +186,7 @@ export default function SolicitudCard({
{button.text}
</Link>
)}
<AsignarSolicitudButton helpRequest={caso} />
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 00e5032

Please sign in to comment.