diff --git a/package-lock.json b/package-lock.json index 5614eae9..ff0a56ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,13 +12,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", @@ -1291,6 +1293,32 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.59.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.20.tgz", + "integrity": "sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.59.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.20.tgz", + "integrity": "sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.59.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@turf/boolean-clockwise": { "version": "5.1.5", "license": "MIT", @@ -6021,6 +6049,16 @@ "version": "0.6.1", "license": "MIT" }, + "node_modules/sonner": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.0.tgz", + "integrity": "sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/sortablejs": { "version": "1.15.3", "license": "MIT", diff --git a/package.json b/package.json index 219112dc..51a26afc 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" } diff --git a/src/app/layout.js b/src/app/layout.tsx similarity index 50% rename from src/app/layout.js rename to src/app/layout.tsx index ff84eee2..3513c0bf 100644 --- a/src/app/layout.js +++ b/src/app/layout.tsx @@ -2,19 +2,18 @@ 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(); @@ -22,18 +21,21 @@ const getSession = async () => { 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 ( + - - - {children} - - + + + + {children} + + + diff --git a/src/components/AsignarSolicitudButton.tsx b/src/components/AsignarSolicitudButton.tsx new file mode 100644 index 00000000..bdda3d70 --- /dev/null +++ b/src/components/AsignarSolicitudButton.tsx @@ -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({ + 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 ; + if (error || assignments === undefined) return <>; + + const userAssignment = assignments.find((x) => x.user_id === session.user?.id); + const userIsAssigned = !!userAssignment; + + if (!session || !session.user) + return ( + + Quiero ayudar + + ); + + return ( + <> + {userIsAssigned ? ( + + ) : ( + + )} + + ); +} diff --git a/src/components/SolicitudCard.js b/src/components/SolicitudCard.tsx similarity index 78% rename from src/components/SolicitudCard.js rename to src/components/SolicitudCard.tsx index a3b44e9b..12a8bc17 100644 --- a/src/components/SolicitudCard.js +++ b/src/components/SolicitudCard.tsx @@ -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 ( <>
+ Referencia: {caso.id} @@ -55,7 +78,7 @@ export default function SolicitudCard({
- Pueblo: {towns[caso.town_id - 1].name || ''} + Pueblo: {towns[caso.town_id - 1]?.name || ''}
)} @@ -69,9 +92,9 @@ export default function SolicitudCard({ Fecha:{' '} - {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', })} @@ -127,10 +150,10 @@ export default function SolicitudCard({
)} - {caso.additional_info?.special_situations && ( + {special_situations && (
Situaciones especiales: -

{caso.additional_info.special_situations}

+

{special_situations}

)} {caso.number_of_people && ( @@ -143,20 +166,16 @@ export default function SolicitudCard({ )}
- {session && - session.user && - session.user.email && - session.user.email === caso.additional_info.email && - !isEdit && ( - - Editar - - )} + {session && session.user && session.user.email && session.user.email === email && !isEdit && ( + + Editar + + )} {isHref && ( )} +
diff --git a/src/components/SolicitudHelpCount.tsx b/src/components/SolicitudHelpCount.tsx new file mode 100644 index 00000000..cb9846d4 --- /dev/null +++ b/src/components/SolicitudHelpCount.tsx @@ -0,0 +1,33 @@ +import { HelpRequestAssignmentData } from '@/types/Requests'; +import { useQuery } from '@tanstack/react-query'; +import { helpRequestService } from '@/lib/service'; +import { Spinner } from '@/components/Spinner'; + +type SolicitudHelpCountProps = { + id: number; +}; + +export default function SolicitudHelpCount({ id }: SolicitudHelpCountProps) { + const { + data: assignments, + isLoading, + error, + } = useQuery({ + queryKey: ['help_request_assignments', { id: id }], + queryFn: () => helpRequestService.getAssignments(id), + }); + + if (isLoading) return ; + + if (error || assignments === undefined) return <>; + + const volunteers = assignments.length; + + return ( + + Voluntarios: {volunteers} + + ); +} diff --git a/src/components/Spinner.tsx b/src/components/Spinner.tsx new file mode 100644 index 00000000..db435f18 --- /dev/null +++ b/src/components/Spinner.tsx @@ -0,0 +1,8 @@ +export function Spinner() { + return ( +
+ ); +} diff --git a/src/components/layout/EmergencyLayout.js b/src/components/layout/EmergencyLayout.tsx similarity index 87% rename from src/components/layout/EmergencyLayout.js rename to src/components/layout/EmergencyLayout.tsx index 7962a634..e7f3e4e1 100644 --- a/src/components/layout/EmergencyLayout.js +++ b/src/components/layout/EmergencyLayout.tsx @@ -1,12 +1,11 @@ -// components/layout/EmergencyLayout.js - 'use client'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, PropsWithChildren } from 'react'; +// @ts-ignore import Sidebar from './Sidebar'; import Footer from './Footer'; -export default function EmergencyLayout({ children }) { +export default function EmergencyLayout({ children }: PropsWithChildren) { const [isSidebarOpen, setIsSidebarOpen] = useState(true); // Por defecto abierto useEffect(() => { diff --git a/src/components/layout/Footer.js b/src/components/layout/Footer.tsx similarity index 100% rename from src/components/layout/Footer.js rename to src/components/layout/Footer.tsx diff --git a/src/components/map/map.tsx b/src/components/map/map.tsx index f93ec317..c2dcfcbf 100644 --- a/src/components/map/map.tsx +++ b/src/components/map/map.tsx @@ -54,7 +54,7 @@ const Map: FC = ({ center = [0, 0], zoom = 2, markers = [] }) => { className: 'map-popup', maxWidth: `${markerData.width}px`, anchor: 'left', - }).setHTML(markerData.descriptionHTML) + }).setHTML(markerData.descriptionHTML), ) .addTo(mapRef.current!); diff --git a/src/context/EmergencyProvider.js b/src/context/EmergencyProvider.js deleted file mode 100644 index d1e9226e..00000000 --- a/src/context/EmergencyProvider.js +++ /dev/null @@ -1,21 +0,0 @@ -'use client'; - -import React, { createContext, useContext, useState } from 'react'; - -const EmergencyContext = createContext(undefined); - -export const EmergencyProvider = ({ children }) => { - const [showModal, setShowModal] = useState(false); - - const toggleModal = (force) => setShowModal((prev) => (force !== undefined ? force : !prev)); - - return {children}; -}; - -export const useModal = () => { - const context = useContext(EmergencyContext); - if (!context) { - throw new Error('useModal must be used within an EmergencyProvider'); - } - return context; -}; diff --git a/src/context/EmergencyProvider.tsx b/src/context/EmergencyProvider.tsx new file mode 100644 index 00000000..fcd02baa --- /dev/null +++ b/src/context/EmergencyProvider.tsx @@ -0,0 +1,30 @@ +'use client'; + +import React, { createContext, ReactNode, useContext, useState } from 'react'; + +const EmergencyContext = createContext({ showModal: false, toggleModal: () => {} }); + +type EmergencyCtx = { + showModal: boolean; + toggleModal: (force: boolean) => void; +}; + +type SessionProviderProps = { + children: ReactNode; +}; + +export const EmergencyProvider: React.FC = ({ children }) => { + const [showModal, setShowModal] = useState(false); + + const toggleModal = (force: boolean) => setShowModal((prev) => (force !== undefined ? force : !prev)); + + return {children}; +}; + +export const useModal = () => { + const context = useContext(EmergencyContext); + if (!context) { + throw new Error('useModal must be used within an EmergencyProvider'); + } + return context; +}; diff --git a/src/context/QueryClientProvider.tsx b/src/context/QueryClientProvider.tsx new file mode 100644 index 00000000..d52a1c08 --- /dev/null +++ b/src/context/QueryClientProvider.tsx @@ -0,0 +1,10 @@ +'use client'; + +import { PropsWithChildren, useState } from 'react'; +import { QueryClient, QueryClientProvider as TanstackQueryClientProvider } from '@tanstack/react-query'; + +export const QueryClientProvider = ({ children }: PropsWithChildren) => { + const [client] = useState(new QueryClient()); + + return {children}; +}; diff --git a/src/context/SessionProvider.js b/src/context/SessionProvider.js deleted file mode 100644 index 561a1f24..00000000 --- a/src/context/SessionProvider.js +++ /dev/null @@ -1,10 +0,0 @@ -'use client'; -import { createContext, useContext } from 'react'; - -const SessionContext = createContext(); - -export const SessionProvider = ({ children, session }) => { - return {children}; -}; - -export const useSession = () => useContext(SessionContext); diff --git a/src/context/SessionProvider.tsx b/src/context/SessionProvider.tsx new file mode 100644 index 00000000..c94f7344 --- /dev/null +++ b/src/context/SessionProvider.tsx @@ -0,0 +1,18 @@ +'use client'; +import React, { createContext, ReactNode, useContext } from 'react'; +import { User } from '@supabase/auth-js'; + +const SessionContext = createContext({ user: null }); + +type UserSession = { user: User } | { user: null }; + +type SessionProviderProps = { + children: ReactNode; + session: UserSession; +}; + +export const SessionProvider: React.FC = ({ children, session }) => { + return {children}; +}; + +export const useSession = () => useContext(SessionContext); diff --git a/src/context/TownProvider.js b/src/context/TownProvider.js deleted file mode 100644 index f0b6e6cb..00000000 --- a/src/context/TownProvider.js +++ /dev/null @@ -1,10 +0,0 @@ -'use client'; -import { createContext, useContext } from 'react'; - -const TownsContext = createContext(); - -export const TownsProvider = ({ children, towns }) => { - return {children}; -}; - -export const useTowns = () => useContext(TownsContext); diff --git a/src/context/TownProvider.tsx b/src/context/TownProvider.tsx new file mode 100644 index 00000000..1e7ede0e --- /dev/null +++ b/src/context/TownProvider.tsx @@ -0,0 +1,16 @@ +'use client'; +import React, { createContext, ReactNode, useContext } from 'react'; +import { Town } from '@/types/Town'; + +const TownsContext = createContext([]); + +type TownsProviderProps = { + children: ReactNode; + towns: Town[]; +}; + +export const TownsProvider: React.FC = ({ children, towns }) => { + return {children}; +}; + +export const useTowns = () => useContext(TownsContext); diff --git a/src/helpers/constants.js b/src/helpers/constants.ts similarity index 88% rename from src/helpers/constants.js rename to src/helpers/constants.ts index 99eee37b..431ddc2f 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.ts @@ -7,11 +7,12 @@ export const tiposAyudaOptions = { medica: 'Asistencia médica', psicologico: 'Apoyo psicológico', logistico: 'Apoyo logístico', + otros: 'Ayuda general', }; export const tiposAyudaAcepta = ['Alimentos', 'Ropa', 'Mantas', 'Agua', 'Productos de higiene', 'Medicamentos']; -export const mapToIdAndLabel = (data) => { +export const mapToIdAndLabel = (data: any) => { return Object.keys(data).map((key) => ({ id: key, label: data[key], diff --git a/src/lib/service.ts b/src/lib/service.ts index ef45cfda..f551a2db 100644 --- a/src/lib/service.ts +++ b/src/lib/service.ts @@ -1,5 +1,6 @@ import { supabase } from './supabase/client'; -import { HelpRequestAssignmentData, HelpRequestData } from '@/types/Requests'; +import { HelpRequestAssignmentInsert, HelpRequestData, HelpRequestUpdate } from '@/types/Requests'; +import { createClient } from '@/lib/supabase/server'; export const helpRequestService = { async createRequest(requestData: HelpRequestData) { @@ -8,7 +9,7 @@ export const helpRequestService = { if (error) throw error; return data[0]; }, - async editRequest(requestData: any, id: any) { + async editRequest(requestData: HelpRequestUpdate, id: number) { const { data, error } = await supabase.from('help_requests').update(requestData).eq('id', id).select(); if (error) throw error; return data; @@ -20,12 +21,24 @@ export const helpRequestService = { return data; }, - async assign(requestData: HelpRequestAssignmentData) { + async getAssignments(id: number) { + const { data, error } = await supabase.from('help_request_assignments').select('*').eq('help_request_id', id); + + if (error) throw error; + return data; + }, + + async assign(requestData: HelpRequestAssignmentInsert) { const { data, error } = await supabase.from('help_request_assignments').insert([requestData]).select(); if (error) throw error; return data[0]; }, + async unassign(id: number) { + const { error } = await supabase.from('help_request_assignments').delete().eq('id', id); + + if (error) throw error; + }, async getByType(type: any) { const { data, error } = await supabase @@ -155,6 +168,15 @@ export const mapService = { }, }; +export const townsService = { + async getTowns() { + const supabase = await getSupabaseClient(); + const { data, error } = await supabase.from('towns').select(); + if (error) throw error; + return data; + }, +}; + // Add this function to test the connection export const testSupabaseConnection = async () => { try { @@ -177,7 +199,7 @@ export const authService = { async getSessionUser() { return supabase.auth.getUser(); }, - async signUp(email: any, password: any, nombre: any, telefono: any) { + async signUp(email: string, password: string, nombre: string, telefono: string) { return supabase.auth.signUp({ email, password, @@ -192,10 +214,20 @@ export const authService = { async signOut() { return supabase.auth.signOut(); }, - async signIn(email: any, password: any) { + async signIn(email: string, password: string) { return supabase.auth.signInWithPassword({ email, password }); }, async updateUser(metadata: any) { return supabase.auth.updateUser({ ...metadata }); }, }; + +const getSupabaseClient = async () => { + if (typeof window === 'undefined') { + // Si estamos en el servidor, usa el cliente del servidor + return await createClient(); + } else { + // Si estamos en el cliente, usa el cliente del navegador + return supabase; + } +}; diff --git a/src/lib/supabase/server.ts b/src/lib/supabase/server.ts index 75ab4181..5bb64f40 100644 --- a/src/lib/supabase/server.ts +++ b/src/lib/supabase/server.ts @@ -1,23 +1,30 @@ -import { createServerClient, type CookieOptions } from '@supabase/ssr'; +'use server'; + +import { createServerClient } from '@supabase/ssr'; import { cookies } from 'next/headers'; +import { Database } from '@/types/database'; export async function createClient() { const cookieStore = await cookies(); - return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { - cookies: { - getAll() { - return cookieStore.getAll(); - }, - setAll(cookiesToSet) { - try { - cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); - } catch { - // The `setAll` method was called from a Server Component. - // This can be ignored if you have middleware refreshing - // user sessions. - } + return createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return cookieStore.getAll(); + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); + } catch { + // The `setAll` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } + }, }, }, - }); + ); } diff --git a/src/types/Requests.ts b/src/types/Requests.ts index 8c52aaff..c875a3af 100644 --- a/src/types/Requests.ts +++ b/src/types/Requests.ts @@ -4,7 +4,14 @@ export type HelpRequestType = 'necesita' | 'ofrece'; export type HelpRequestUrgencyType = 'alta' | 'media' | 'baja'; export type HelpRequestStatusType = 'pending' | 'in_progress' | 'active'; export type HelpRequestData = Database['public']['Tables']['help_requests']['Row']; +export type HelpRequestUpdate = Database['public']['Tables']['help_requests']['Update']; export type HelpRequestAssignmentData = Database['public']['Tables']['help_request_assignments']['Row']; +export type HelpRequestAssignmentInsert = Database['public']['Tables']['help_request_assignments']['Insert']; + +export type HelpRequestAdditionalInfo = { + special_situations?: string; + email?: string; +}; export type CollectionPointType = 'permanente' | 'temporal'; export type CollectionPointStatus = 'active' | 'inactive'; diff --git a/src/types/Town.ts b/src/types/Town.ts new file mode 100644 index 00000000..160382a6 --- /dev/null +++ b/src/types/Town.ts @@ -0,0 +1,3 @@ +import { Database } from '@/types/database'; + +export type Town = Database['public']['Tables']['towns']['Row']; diff --git a/supabase/migrations/20241106110421_add-missing-policy-help-request-assignments.sql b/supabase/migrations/20241106110421_add-missing-policy-help-request-assignments.sql new file mode 100644 index 00000000..532177af --- /dev/null +++ b/supabase/migrations/20241106110421_add-missing-policy-help-request-assignments.sql @@ -0,0 +1,9 @@ +CREATE POLICY "authenticated_can_insert_own_records" +ON public.help_request_assignments +FOR INSERT +WITH CHECK ((select auth.uid()) = user_id); + +CREATE POLICY "Enable read access for all users" +ON public.help_request_assignments +FOR SELECT +USING (true); \ No newline at end of file diff --git a/supabase/seed.sql b/supabase/seed.sql new file mode 100644 index 00000000..9f1ace76 --- /dev/null +++ b/supabase/seed.sql @@ -0,0 +1,33 @@ +INSERT INTO "public"."towns" ("id", "created_at", "name", "people_helping", "help_needed") + VALUES ('1', '2024-11-02 17:52:17.645308+00', 'Aldaia', '0', '0'), + ('2', '2024-11-02 17:52:17.645308+00', 'Alfafar', '0', '0'), + ('3', '2024-11-02 17:52:17.645308+00', 'Albal', '0', '0'), + ('4', '2024-11-02 17:52:17.645308+00', 'Alcudia', '0', '0'), + ('5', '2024-11-02 17:52:17.645308+00', 'Algemesí', '0', '0'), + ('6', '2024-11-02 17:52:17.645308+00', 'Bugarra', '0', '0'), + ('7', '2024-11-02 17:52:17.645308+00', 'Catarroja', '0', '0'), + ('8', '2024-11-02 17:52:17.645308+00', 'Castelló', '0', '0'), + ('9', '2024-11-02 17:52:17.645308+00', 'Cheste', '0', '0'), + ('10', '2024-11-02 17:52:17.645308+00', 'Chiva', '0', '0'), + ('11', '2024-11-02 17:52:17.645308+00', 'Gestalgar', '0', '0'), + ('12', '2024-11-02 17:52:17.645308+00', 'Guadassuar', '0', '0'), + ('14', '2024-11-02 17:52:17.645308+00', 'Manuel', '0', '0'), + ('15', '2024-11-02 17:52:17.645308+00', 'Massanassa', '0', '0'), + ('16', '2024-11-02 17:52:17.645308+00', 'Montserrat', '0', '0'), + ('17', '2024-11-02 17:52:17.645308+00', 'Paiporta', '0', '0'), + ('18', '2024-11-02 17:52:17.645308+00', 'Pedralba', '0', '0'), + ('19', '2024-11-02 17:52:17.645308+00', 'Riba-roja de Túria', '0', '0'), + ('20', '2024-11-02 17:52:17.645308+00', 'Sedaví', '0', '0'), + ('21', '2024-11-02 17:52:17.645308+00', 'Sot de Chera', '0', '0'), + ('22', '2024-11-02 17:52:17.645308+00', 'Torrent', '0', '0'), + ('23', '2024-11-02 17:52:17.645308+00', 'Utiel', '0', '0'), + ('24', '2024-11-02 17:52:17.645308+00', 'Villar del Arzobispo', '0', '0'), + ('25', '2024-11-03 09:34:00.825499+00', 'Alzira', '0', '0'), + ('26', '2024-11-03 08:44:02.724841+00', 'Benetusser', '0', '0'), + ('27', '2024-11-03 08:44:34.023343+00', 'Turís', '0', '0'), + ('28', '2024-11-03 08:47:46.2913+00', 'Picanya', '0', '0'), + ('29', '2024-11-03 08:57:38.119705+00', 'La Torre, Valencia', '0', '0'), + ('30', '2024-11-03 09:05:24.008619+00', 'Benimaclet', '0', '0'), + ('31', '2024-11-03 09:21:27.852067+00', 'Godelleta', '0', '0'), + ('32', '2024-11-05 10:38:38.669406+00', 'Llocnou de la Corona', '0', '0'), + ('33', '2024-11-05 10:38:52.085488+00', 'Beniparrell', '0', '0'); \ No newline at end of file