From 2233c422b76b437bdc25964dca2d5222586917b8 Mon Sep 17 00:00:00 2001 From: Troleomotor10 Date: Thu, 7 Nov 2024 18:48:49 +0100 Subject: [PATCH 01/17] docs: add google SSO supabase + update supabase docs --- .env.example | 5 ++-- CONTRIBUTING.md | 66 ++++++++++++++++++++++++++++++++++---------- supabase/config.toml | 8 +++--- 3 files changed, 58 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index 8b8552c1..2c15a743 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ NEXT_PUBLIC_SUPABASE_URL= NEXT_PUBLIC_SUPABASE_ANON_KEY= - -API_KEY= +SUPABASE_GOOGLE_AUTH_ID= +SUPABASE_GOOGLE_AUTH_SECRET= +API_KEY= \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ec724c1..67f17ebd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,33 +46,69 @@ Este mensaje indica que se han añadido instrucciones de instalación local en e ¡Gracias por contribuir! Tu ayuda hace una gran diferencia para el proyecto. -
- ## Desarrollo local - levantar db de desarrollo local ### Pre requisitos -- [docker](https://docs.docker.com/engine/install/) y [docker compose](https://docs.docker.com/compose/install/) -- [cli de supabase](https://supabase.com/docs/guides/local-development/cli/getting-started) +- [Docker](https://docs.docker.com/engine/install/) y [Docker Compose](https://docs.docker.com/compose/install/) +- [Supabase CLI](https://supabase.com/docs/guides/local-development/cli/getting-started) -### Instalar self hosted supabase +Si ya tienes cualquiera de los dos, **actualizalos para evitar errores**. -``` +Si usas docker desktop en Windows, esta es la configuración que deberías tener. + + +### Iniciar supabase en local + +Entramos en la carpeta del repositorio + +```bash cd ${DIRECTORIO_DE_EMERGENCY_CV} -supabase login -supabase init -supabase link --project-ref nmvcsenkfqbdlfdtiqdo +``` + +Iniciamos la base de datos (**tener docker encendido**) + +```bash supabase start ``` -### Para hacer cambios en el schema +Si no vemos las tablas ni los datos de ejemplo cargados podemos refrescar la base de datos con: + +```bash +supabase db reset +``` + +### Hacer cambios en el schema -- Editar como queremos que sea en local (studio de supabase) -- Ejecutar el comando: +#### Crear migracion automatica +Nos interesa usar esta opción, cuando queremos **editar la base de datos desde el studio web** + +Cuando acabemos de realizar los cambios en el studio web, ejecutaremos el siguiente comando para generar la migration. + +```bash +supabase db diff -f nombre_migracion ``` -// nombre de la migracion es indicativo, no tiene nigun efecto -supabase db diff -f ${NOMBRE_DE_LA_MIGRACION} + +#### Crear migración manual + +Nos interesa usar esta opción, cuando queremos **editar la base de datos con codigo SQL manual**. + +Primero, creamos la el archivo migration con + +```bash +supabase migration new nombre_migracion +``` + +Se creara un **nuevo fichero** con el nombre que hemos usado **en supabase/migrations** + +En ese archivo añadiremos todo el código SQL que necesitemos + +Si queremos visualizar nuestros cambios en local, podemos usar + +```bash +supabase db reset ``` -- Esto generara una migracion en el local, hay que añadir esto al PR y github actions lo pondra en produccion. +
+La migration que generemos, la añadiremos en el PR. diff --git a/supabase/config.toml b/supabase/config.toml index 0172bd1c..a79ce9fc 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -190,11 +190,11 @@ verify_enabled = true # Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, # `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, # `twitter`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" +[auth.external.google] +enabled = true +client_id = "env(SUPABASE_GOOGLE_AUTH_ID)" # DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: -secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" +secret = "env(SUPABASE_GOOGLE_AUTH_SECRET)" # Overrides the default auth redirectUrl. redirect_uri = "" # Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, From 9e8dab506566d8c4cc69724ead04cc23591d1192 Mon Sep 17 00:00:00 2001 From: Troleomotor10 Date: Thu, 7 Nov 2024 18:49:16 +0100 Subject: [PATCH 02/17] feat: add privacy policy google auth --- src/components/auth/PhoneNumberDialog.js | 60 +++++++++++++++++++----- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/src/components/auth/PhoneNumberDialog.js b/src/components/auth/PhoneNumberDialog.js index 94da5dee..53404a42 100644 --- a/src/components/auth/PhoneNumberDialog.js +++ b/src/components/auth/PhoneNumberDialog.js @@ -12,27 +12,41 @@ import { isValidPhone } from '@/helpers/utils'; const MODAL_NAME = 'phone-number'; const PhoneForm = ({ onSubmit }) => { - const [phoneNumber, setPhoneNumber] = useState(''); + const [formData, setFormData] = useState({ + phoneNumber: '', + privacyPolicy: '', + }); const handleSubmit = useCallback( (e) => { e.preventDefault(); - /* Form validation */ - if (!isValidPhone(phoneNumber)) { + /* PHONE VALIDATION */ + if (!isValidPhone(formData.phoneNumber)) { alert('El teléfono de contacto no es válido.'); return; } - const formatedPhoneNumber = formatPhoneNumber(phoneNumber); - onSubmit(formatedPhoneNumber); - setPhoneNumber(''); + /* POLICY PRIVACY VALIDATION */ + if (!formData.privacyPolicy) { + alert('Para continuar, debes aceptar la Política de Privacidad.'); + return; + } + + const formatedPhoneNumber = formatPhoneNumber(formData.phoneNumber); + + onSubmit(formData.phoneNumber, formData.privacyPolicy); + + setFormData({ + phoneNumber: '', + privacyPolicy: '', + }); }, - [onSubmit, phoneNumber], + [onSubmit, formData], ); const handleChange = useCallback((phoneNumber) => { - setPhoneNumber(phoneNumber); + setFormData((formData) => ({ ...formData, phoneNumber })) }, []); return ( @@ -51,9 +65,31 @@ const PhoneForm = ({ onSubmit }) => { Tu número de teléfono no será usado con ningún otro propósito ni compartido con terceras personas.

- + + {/* PHONE NUMBER */} + + + {/* PRIVACY POLICY */} +
+ setFormData({ ...formData, privacyPolicy: e.target.checked })} + className="min-w-4 min-h-4 cursor-pointer" + id="privacyPolicy" + required + /> + +
-
+ {/* ACCEPT AND SAVE */} +
{/* Toggle button for desktop */} From 63f14809eb2c5de7e8a16ca90b79a17aef6c125e Mon Sep 17 00:00:00 2001 From: Pinx0 Date: Thu, 7 Nov 2024 19:42:58 +0100 Subject: [PATCH 04/17] feat: save user_id when creating a help_request --- .../_components/Form/FormContainer.tsx | 13 ++++++++----- src/components/OfferHelp.js | 16 +++++++++------- src/components/RequestHelp.js | 6 +++++- src/types/database.ts | 3 +++ ...241107182626_Add user_id in help_requests.sql | 6 ++++++ 5 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 supabase/migrations/20241107182626_Add user_id in help_requests.sql diff --git a/src/app/solicitar-ayuda/_components/Form/FormContainer.tsx b/src/app/solicitar-ayuda/_components/Form/FormContainer.tsx index ce94b157..fb796ca9 100644 --- a/src/app/solicitar-ayuda/_components/Form/FormContainer.tsx +++ b/src/app/solicitar-ayuda/_components/Form/FormContainer.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { FormEvent, useCallback, useEffect, useMemo, useState } from 'react'; import { FormRenderer } from './FormRenderer'; import { FormData, Status } from '../types'; @@ -32,8 +32,10 @@ export function FormContainer() { const router = useRouter(); const session = useSession(); + const userId = session.user?.id; + const [formData, setFormData] = useState({ - nombre: session?.user?.user_metadata?.full_name.split(" ")[0]|| '', + nombre: session?.user?.user_metadata?.full_name.split(' ')[0] || '', ubicacion: '', coordinates: null, tiposDeAyuda: new Map(TIPOS_DE_AYUDA.map(({ id }) => [id, false])), @@ -62,7 +64,7 @@ export function FormContainer() { }); const handleSubmit = useCallback( - async (e: any) => { + async (e: FormEvent) => { e.preventDefault(); /* Form validation */ @@ -91,7 +93,7 @@ export function FormContainer() { try { const helpRequestData: Database['public']['Tables']['help_requests']['Insert'] = { type: 'necesita', - name: formData.nombre.split(" ")[0], + name: formData.nombre.split(' ')[0], location: formData.ubicacion, latitude: formData.coordinates ? parseFloat(formData.coordinates.lat) : null, longitude: formData.coordinates ? parseFloat(formData.coordinates.lng) : null, @@ -107,6 +109,7 @@ export function FormContainer() { }, town_id: parseInt(formData.pueblo), status: 'active', + user_id: userId, }; await helpRequestService.createRequest(helpRequestData); @@ -139,7 +142,7 @@ export function FormContainer() { }); } }, - [formData, router], + [userId, formData, router], ); const handleInputElementChange: React.ChangeEventHandler = useCallback((e) => { diff --git a/src/components/OfferHelp.js b/src/components/OfferHelp.js index 54b00a76..aafaf0b9 100644 --- a/src/components/OfferHelp.js +++ b/src/components/OfferHelp.js @@ -1,7 +1,7 @@ 'use client'; -import { useState, useEffect, useCallback } from 'react'; -import { HeartHandshake, Check, Mail } from 'lucide-react'; +import { useState, useCallback } from 'react'; +import { HeartHandshake, Check } from 'lucide-react'; import { helpRequestService } from '@/lib/service'; import AddressAutocomplete from '@/components/AddressAutocomplete'; import { mapToIdAndLabel, tiposAyudaOptions as _tiposAyudaOptions } from '@/helpers/constants'; @@ -9,9 +9,9 @@ import { isValidPhone } from '@/helpers/utils'; import { PhoneInput } from '@/components/PhoneInput'; import { formatPhoneNumber } from '@/helpers/utils'; -import { useTowns } from '../context/TownProvider'; +import { useTowns } from '@/context/TownProvider'; import { useRouter } from 'next/navigation'; -import { useSession } from '../context/SessionProvider'; +import { useSession } from '@/context/SessionProvider'; export default function OfferHelp({ town, @@ -29,6 +29,8 @@ export default function OfferHelp({ const router = useRouter(); + const userId = session.user?.id; + const [formData, setFormData] = useState({ nombre: data.name || session?.user?.user_metadata?.full_name || '', telefono: data.contact_info || session?.user?.user_metadata?.telefono || '', @@ -41,7 +43,7 @@ export default function OfferHelp({ radio: data.resources?.radius || 1, experiencia: data.additional_info?.experience || '', comentarios: data.description || '', - aceptaProtocolo: submitType ? true : false, + aceptaProtocolo: !!submitType, pueblo: submitType === 'edit' ? data?.town_id : town ? town.id : '', status: data?.status || '', }); @@ -110,7 +112,6 @@ export default function OfferHelp({ email: formData.email, experience: formData.experiencia, }, - status: 'active', resources: { vehicle: formData.vehiculo, availability: formData.disponibilidad, @@ -121,7 +122,8 @@ export default function OfferHelp({ help_type: formData.tiposAyuda, other_help: formData.otraAyuda, town_id: formData.pueblo, - status: formData.status, + status: formData.status || 'active', + user_id: userId, }; if (submitType === 'create') { diff --git a/src/components/RequestHelp.js b/src/components/RequestHelp.js index 0e861d76..76559c27 100644 --- a/src/components/RequestHelp.js +++ b/src/components/RequestHelp.js @@ -10,8 +10,9 @@ import { helpRequestService } from '@/lib/service'; import { PhoneInput } from '@/components/PhoneInput'; import Image from 'next/image'; import { useRouter } from 'next/navigation'; -import { useTowns } from '../context/TownProvider'; +import { useTowns } from '@/context/TownProvider'; import { CallCenterLink } from '@/components/CallCenterLink'; +import { useSession } from '@/context/SessionProvider'; export default function RequestHelp({ data = {}, @@ -23,6 +24,8 @@ export default function RequestHelp({ }) { const towns = useTowns(); const router = useRouter(); + const session = useSession(); + const userId = session.user?.id; const [formData, setFormData] = useState({ nombre: data.name || '', ubicacion: data.location || '', @@ -93,6 +96,7 @@ export default function RequestHelp({ }, town_id: formData.pueblo, status: formData.status, + user_id: userId, }; if (submitType === 'create') { const { error } = await helpRequestService.createRequest(helpRequestData); diff --git a/src/types/database.ts b/src/types/database.ts index c831508a..2a181cf2 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -188,6 +188,7 @@ export type Database = { town_id: number | null; type: string | null; urgency: string | null; + user_id: string | null; }; Insert: { additional_info?: Json | null; @@ -208,6 +209,7 @@ export type Database = { town_id?: number | null; type?: string | null; urgency?: string | null; + user_id?: string | null; }; Update: { additional_info?: Json | null; @@ -228,6 +230,7 @@ export type Database = { town_id?: number | null; type?: string | null; urgency?: string | null; + user_id?: string | null; }; Relationships: [ { diff --git a/supabase/migrations/20241107182626_Add user_id in help_requests.sql b/supabase/migrations/20241107182626_Add user_id in help_requests.sql new file mode 100644 index 00000000..43f14c5a --- /dev/null +++ b/supabase/migrations/20241107182626_Add user_id in help_requests.sql @@ -0,0 +1,6 @@ +alter table "public"."help_requests" add column "user_id" uuid; + +UPDATE help_requests hr +SET user_id = i.user_id +FROM auth.identities i +WHERE hr.additional_info->>'email' = i.email; \ No newline at end of file From 6028f79e4bc5972cd16b0658428d71956f848ca1 Mon Sep 17 00:00:00 2001 From: Pinx0 Date: Thu, 7 Nov 2024 19:43:30 +0100 Subject: [PATCH 05/17] feat: add search help_requests by user_id --- src/lib/service.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lib/service.ts b/src/lib/service.ts index 613dd318..9d17ac96 100644 --- a/src/lib/service.ts +++ b/src/lib/service.ts @@ -22,6 +22,34 @@ export const helpRequestService = { return data; }, + async getRequestsByUser(user_id: string | undefined) { + if (user_id === undefined) return []; + const { data: assignments, error: assignmentsError } = await supabase + .from('help_request_assignments') + .select('help_request_id') + .eq('user_id', user_id); + if (assignmentsError) throw assignmentsError; + const helpRequestIds = assignments.map((assignment) => assignment.help_request_id); + const { data: requests, error: requestsError } = await supabase + .from('help_requests') + .select('*') + .eq('type', 'necesita') + .or(`user_id.eq.${user_id},id.in.(${helpRequestIds.join(',')})`); + if (requestsError) throw requestsError; + return requests; + }, + + async getOffersByUser(user_id: string | undefined) { + if (user_id === undefined) return []; + const { data: requests, error: requestsError } = await supabase + .from('help_requests') + .select('*') + .eq('type', 'ofrece') + .eq('user_id', user_id); + if (requestsError) throw requestsError; + return requests; + }, + async getAssignments(id: number) { const { data, error } = await supabase.from('help_request_assignments').select('*').eq('help_request_id', id); From 6a9c0fd2294a641d9944b34721e46f87be611895 Mon Sep 17 00:00:00 2001 From: Pinx0 Date: Thu, 7 Nov 2024 19:46:55 +0100 Subject: [PATCH 06/17] refactor: change emergencyprovider to modalprovider to suit its real purpose --- src/app/casos-activos/solicitudes/page.js | 2 +- src/app/layout.tsx | 6 +++--- src/components/AsignarSolicitudButton.tsx | 2 +- src/components/CookieBanner/CookieBanner.tsx | 2 +- src/components/Modal.tsx | 2 +- src/components/auth/PhoneNumberDialog.js | 2 +- src/components/map/map.tsx | 2 +- src/context/{EmergencyProvider.tsx => ModalProvider.tsx} | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) rename src/context/{EmergencyProvider.tsx => ModalProvider.tsx} (89%) diff --git a/src/app/casos-activos/solicitudes/page.js b/src/app/casos-activos/solicitudes/page.js index c1f78bdf..5a86151e 100644 --- a/src/app/casos-activos/solicitudes/page.js +++ b/src/app/casos-activos/solicitudes/page.js @@ -9,7 +9,7 @@ import OfferHelp from '@/components/OfferHelp'; import { useRouter, useSearchParams } from 'next/navigation'; import { tiposAyudaOptions } from '@/helpers/constants'; import Modal from '@/components/Modal'; -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; import { useTowns } from '@/context/TownProvider'; const MODAL_NAME = 'solicitudes'; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3513c0bf..8dc4b0ea 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,7 +1,7 @@ import './globals.css'; import 'leaflet/dist/leaflet.css'; import EmergencyLayout from '@/components/layout/EmergencyLayout'; -import { EmergencyProvider } from '@/context/EmergencyProvider'; +import { ModalProvider } from '@/context/ModalProvider'; import { TownsProvider } from '@/context/TownProvider'; import { createClient } from '@/lib/supabase/server'; import { SessionProvider } from '@/context/SessionProvider'; @@ -31,9 +31,9 @@ export default async function RootLayout({ children }: PropsWithChildren) { - + {children} - + diff --git a/src/components/AsignarSolicitudButton.tsx b/src/components/AsignarSolicitudButton.tsx index 38da6778..c0ae3f30 100644 --- a/src/components/AsignarSolicitudButton.tsx +++ b/src/components/AsignarSolicitudButton.tsx @@ -9,7 +9,7 @@ import Link from 'next/link'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import Modal from '@/components/Modal'; -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; type AsignarSolicitudButtonProps = { helpRequest: HelpRequestData; diff --git a/src/components/CookieBanner/CookieBanner.tsx b/src/components/CookieBanner/CookieBanner.tsx index d99de933..88180564 100644 --- a/src/components/CookieBanner/CookieBanner.tsx +++ b/src/components/CookieBanner/CookieBanner.tsx @@ -2,7 +2,7 @@ import { FC, useEffect, useState } from 'react'; import Modal from '@/components/Modal'; -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index e7b8950e..7f9b036d 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -1,4 +1,4 @@ -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; import { MouseEvent, FC, ReactNode } from 'react'; type TailwindMaxWidth = diff --git a/src/components/auth/PhoneNumberDialog.js b/src/components/auth/PhoneNumberDialog.js index 94da5dee..8de2da5e 100644 --- a/src/components/auth/PhoneNumberDialog.js +++ b/src/components/auth/PhoneNumberDialog.js @@ -4,7 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import Modal from '@/components/Modal'; import { authService } from '@/lib/service'; -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; import { PhoneInput } from '@/components/PhoneInput'; import { formatPhoneNumber } from '@/helpers/utils'; import { isValidPhone } from '@/helpers/utils'; diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index 956a2ba3..dd115b84 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -4,7 +4,7 @@ import { FC, ReactNode, useState } from 'react'; import ReactMap from 'react-map-gl/maplibre'; import 'maplibre-gl/dist/maplibre-gl.css'; import { Marker } from 'react-map-gl/maplibre'; -import { useModal } from '@/context/EmergencyProvider'; +import { useModal } from '@/context/ModalProvider'; import Modal from '@/components/Modal'; import { MapPinFilled } from '@/components/icons/MapPinFilled'; diff --git a/src/context/EmergencyProvider.tsx b/src/context/ModalProvider.tsx similarity index 89% rename from src/context/EmergencyProvider.tsx rename to src/context/ModalProvider.tsx index 76a109fe..21614782 100644 --- a/src/context/EmergencyProvider.tsx +++ b/src/context/ModalProvider.tsx @@ -12,11 +12,11 @@ const EmergencyContext = createContext({ toggleModal: () => {}, }); -type EmergencyProviderProps = { +type ModalProviderProps = { children: ReactNode; }; -export const EmergencyProvider: FC = ({ children }) => { +export const ModalProvider: FC = ({ children }) => { const [isModalOpen, setIsModalOpen] = useState<{ [key: string]: boolean }>({}); const toggleModal = (id: string, force?: boolean) => { From fa6de2a965800b020bc0430583a36d79ed398b5e Mon Sep 17 00:00:00 2001 From: Pinx0 Date: Thu, 7 Nov 2024 19:48:27 +0100 Subject: [PATCH 07/17] refactor: change emergencyLayout to SidebarLayout to suit its real purpose --- src/app/layout.tsx | 4 +- .../layout/{Sidebar.js => Sidebar.tsx} | 263 ++++++++++-------- ...{EmergencyLayout.tsx => SidebarLayout.tsx} | 2 +- 3 files changed, 144 insertions(+), 125 deletions(-) rename src/components/layout/{Sidebar.js => Sidebar.tsx} (54%) rename src/components/layout/{EmergencyLayout.tsx => SidebarLayout.tsx} (94%) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8dc4b0ea..bac0251c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,6 @@ import './globals.css'; import 'leaflet/dist/leaflet.css'; -import EmergencyLayout from '@/components/layout/EmergencyLayout'; +import SidebarLayout from '@/components/layout/SidebarLayout'; import { ModalProvider } from '@/context/ModalProvider'; import { TownsProvider } from '@/context/TownProvider'; import { createClient } from '@/lib/supabase/server'; @@ -32,7 +32,7 @@ export default async function RootLayout({ children }: PropsWithChildren) { - {children} + {children} diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.tsx similarity index 54% rename from src/components/layout/Sidebar.js rename to src/components/layout/Sidebar.tsx index 34887bfb..883ff459 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.tsx @@ -1,5 +1,3 @@ -// components/Sidebar.js - 'use client'; import React from 'react'; @@ -24,129 +22,150 @@ import { CarTaxiFront, } from 'lucide-react'; import UserInfo from '../UserInfo'; -import { useSession } from '../../context/SessionProvider'; - -const menuItems = [ - { - icon: Home, - title: 'Inicio', - path: '/', - color: 'text-gray-600', - isHome: true, - }, - { - icon: AlertCircle, - title: 'Casos Activos', - description: 'Ver todos los casos activos', - path: '/casos-activos', - color: 'text-orange-600', - highlight: true, - }, - { - icon: Inbox, - title: 'Mis solicitudes', - description: 'Edita o elimina tus solicitudes', - path: '/solicitudes', - color: 'text-red-500', - isAuth: true, - }, - { - icon: Inbox, - title: 'Mis ofertas', - description: 'Edita o elimina tus ofertas', - path: '/ofertas', - color: 'text-green-500', - isAuth: true, - }, - { - icon: Thermometer, - title: 'Voluntómetro', - description: 'Medidor de voluntarios por localidad', - path: '/voluntometro', - color: 'text-yellow-500', - }, - { - icon: Search, - title: 'Solicitar Ayuda', - description: 'Si necesitas asistencia', - path: '/solicitar-ayuda', - color: 'text-red-600', - }, - { - icon: HeartHandshake, - title: 'Ofrecer Ayuda', - description: 'Si puedes ayudar a otros', - path: '/ofrecer-ayuda', - color: 'text-green-600', - }, - { - icon: UserSearch, - title: 'Desaparecidos', - description: 'Reportar personas', - path: 'https://desaparecidosdana.pythonanywhere.com/', - color: 'text-purple-600', - isHref: true, - }, - { - icon: Package, - title: 'Punto de Recogida', - description: 'Gestionar donaciones', - path: '/punto-recogida', - color: 'text-blue-600', - }, - { - icon: Truck, - title: 'Puntos de Entrega', - description: 'Para transportistas y logística', - path: '/puntos-entrega', - color: 'text-gray-800', - }, - { - icon: Scale, - title: 'Servicio Notarial', - description: 'Servicio notarial gratuito', - path: 'https://valencia.notariado.org/portal/-/20241031-servicio-notarial-de-ayuda-gratuito-para-los-afectados-por-la-dana-noticia-p%C3%BAblica-', - color: 'text-indigo-600', - isHref: true, - }, - { - icon: Landmark, - title: 'Reclamar a Consorcio', - description: 'Seguro de riesgos extraordinarios', - path: 'https://www.consorseguros.es/ambitos-de-actividad/seguros-de-riesgos-extraordinarios/solicitud-de-indemnizacion', - color: 'text-pink-600', - isHref: true, - }, - { - icon: MessageCircleQuestion, - title: 'Ayuda Psicológica', - description: 'Conecta con psicólogos voluntarios', - path: 'https://ayudana.org/', - color: 'text-teal-600', - isHref: true, - }, - { - icon: CarTaxiFront, - title: 'Compartir Coche', - description: 'Viaja u ofrece viajes con otros', - path: 'https://anem.guruwalk.com/', - color: 'text-amber-600', - isHref: true, - }, - { - icon: Car, - title: 'Encontrar tu Coche', - description: 'Sistema de registro y consulta de vehículos perdidos', - path: 'https://tucochedana.es/index.php/', - color: 'text-blue-600', - isHref: true, - }, -]; +import { useSession } from '@/context/SessionProvider'; +import { useQuery } from '@tanstack/react-query'; +import { HelpRequestData } from '@/types/Requests'; +import { helpRequestService } from '@/lib/service'; -export default function Sidebar({ isOpen, toggle }) { +type SidebarProps = { + isOpen: boolean; + toggle: () => void; +}; +export default function Sidebar({ isOpen, toggle }: SidebarProps) { const router = useRouter(); const pathname = usePathname(); const session = useSession(); + + const userId = session.user?.id; + + const { data: requests } = useQuery({ + queryKey: ['help_requests', { user_id: userId }], + queryFn: () => helpRequestService.getRequestsByUser(userId), + }); + const { data: offers } = useQuery({ + queryKey: ['help_requests', { user_id: userId }], + queryFn: () => helpRequestService.getOffersByUser(userId), + }); + const hasRequests = (requests?.length ?? 0) > 0; + const hasOffers = (offers?.length ?? 0) > 0; + + const menuItems = [ + { + icon: Home, + title: 'Inicio', + path: '/', + color: 'text-gray-600', + isHome: true, + }, + { + icon: AlertCircle, + title: 'Casos Activos', + description: 'Ver todos los casos activos', + path: '/casos-activos', + color: 'text-orange-600', + highlight: true, + }, + { + icon: Inbox, + title: 'Mis solicitudes', + description: 'Edita o elimina tus solicitudes', + path: '/solicitudes', + color: 'text-red-500', + hide: !hasRequests, + }, + { + icon: Inbox, + title: 'Mis ofertas', + description: 'Edita o elimina tus ofertas', + path: '/ofertas', + color: 'text-green-500', + hide: !hasOffers, + }, + { + icon: Thermometer, + title: 'Voluntómetro', + description: 'Medidor de voluntarios por localidad', + path: '/voluntometro', + color: 'text-yellow-500', + }, + { + icon: Search, + title: 'Solicitar Ayuda', + description: 'Si necesitas asistencia', + path: '/solicitar-ayuda', + color: 'text-red-600', + }, + { + icon: HeartHandshake, + title: 'Ofrecer Ayuda', + description: 'Si puedes ayudar a otros', + path: '/ofrecer-ayuda', + color: 'text-green-600', + }, + { + icon: UserSearch, + title: 'Desaparecidos', + description: 'Reportar personas', + path: 'https://desaparecidosdana.pythonanywhere.com/', + color: 'text-purple-600', + isHref: true, + }, + { + icon: Package, + title: 'Punto de Recogida', + description: 'Gestionar donaciones', + path: '/punto-recogida', + color: 'text-blue-600', + }, + { + icon: Truck, + title: 'Puntos de Entrega', + description: 'Para transportistas y logística', + path: '/puntos-entrega', + color: 'text-gray-800', + }, + { + icon: Scale, + title: 'Servicio Notarial', + description: 'Servicio notarial gratuito', + path: 'https://valencia.notariado.org/portal/-/20241031-servicio-notarial-de-ayuda-gratuito-para-los-afectados-por-la-dana-noticia-p%C3%BAblica-', + color: 'text-indigo-600', + isHref: true, + }, + { + icon: Landmark, + title: 'Reclamar a Consorcio', + description: 'Seguro de riesgos extraordinarios', + path: 'https://www.consorseguros.es/ambitos-de-actividad/seguros-de-riesgos-extraordinarios/solicitud-de-indemnizacion', + color: 'text-pink-600', + isHref: true, + }, + { + icon: MessageCircleQuestion, + title: 'Ayuda Psicológica', + description: 'Conecta con psicólogos voluntarios', + path: 'https://ayudana.org/', + color: 'text-teal-600', + isHref: true, + }, + { + icon: CarTaxiFront, + title: 'Compartir Coche', + description: 'Viaja u ofrece viajes con otros', + path: 'https://anem.guruwalk.com/', + color: 'text-amber-600', + isHref: true, + }, + { + icon: Car, + title: 'Encontrar tu Coche', + description: 'Sistema de registro y consulta de vehículos perdidos', + path: 'https://tucochedana.es/index.php/', + color: 'text-blue-600', + isHref: true, + }, + ]; + return ( <> {/* Quitamos el overlay con fondo negro */} @@ -179,7 +198,7 @@ export default function Sidebar({ isOpen, toggle }) {