From 70d139e30b4b3b3c57b73084e7e684648e82ff29 Mon Sep 17 00:00:00 2001 From: Daniel Rocha Date: Sat, 11 May 2024 12:46:21 -0300 Subject: [PATCH 1/3] chore: add react-leaflet dependecy --- package-lock.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +++ 2 files changed, 49 insertions(+) diff --git a/package-lock.json b/package-lock.json index 479b79fc..6b3f6db4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,12 +24,14 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "formik": "^2.4.6", + "leaflet": "^1.9.4", "lucide-react": "^0.378.0", "qs": "^6.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.4", "react-input-mask": "^2.0.4", + "react-leaflet": "^4.2.1", "react-router-dom": "^6.23.0", "react-select": "^5.8.0", "tailwind-merge": "^2.3.0", @@ -38,6 +40,7 @@ "zod": "^3.23.6" }, "devDependencies": { + "@types/leaflet": "^1.9.12", "@types/node": "^20.12.8", "@types/qs": "^6.9.15", "@types/react": "^18.2.66", @@ -2069,6 +2072,16 @@ "@babel/runtime": "^7.13.10" } }, + "node_modules/@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@remix-run/router": { "version": "1.16.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", @@ -2332,6 +2345,12 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", @@ -2347,6 +2366,15 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/leaflet": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "20.12.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.10.tgz", @@ -4245,6 +4273,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4933,6 +4966,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "dependencies": { + "@react-leaflet/core": "^2.1.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", diff --git a/package.json b/package.json index 325b0666..3ae3b8fe 100644 --- a/package.json +++ b/package.json @@ -26,12 +26,14 @@ "clsx": "^2.1.1", "date-fns": "^3.6.0", "formik": "^2.4.6", + "leaflet": "^1.9.4", "lucide-react": "^0.378.0", "qs": "^6.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.51.4", "react-input-mask": "^2.0.4", + "react-leaflet": "^4.2.1", "react-router-dom": "^6.23.0", "react-select": "^5.8.0", "tailwind-merge": "^2.3.0", @@ -40,6 +42,7 @@ "zod": "^3.23.6" }, "devDependencies": { + "@types/leaflet": "^1.9.12", "@types/node": "^20.12.8", "@types/qs": "^6.9.15", "@types/react": "^18.2.66", From ec9e2229b06589eb97803aed029c3e5cab5e04ca Mon Sep 17 00:00:00 2001 From: Daniel Rocha Date: Sat, 11 May 2024 12:50:21 -0300 Subject: [PATCH 2/3] feat: add shelter map based in a front-end loop --- src/App.tsx | 1 + src/hooks/usePaginatedQuery/paths.ts | 1 + src/hooks/useShelters/types.ts | 1 + src/hooks/useShelters/useShelters.tsx | 57 +++++++++++- .../ShelterListView/ShelterListView.tsx | 14 ++- src/pages/Map/Map.tsx | 88 +++++++++++++++++++ src/pages/Map/index.ts | 3 + src/pages/Map/types.ts | 1 + src/pages/index.ts | 4 +- src/routes/Routes.tsx | 2 + 10 files changed, 165 insertions(+), 7 deletions(-) create mode 100644 src/pages/Map/Map.tsx create mode 100644 src/pages/Map/index.ts create mode 100644 src/pages/Map/types.ts diff --git a/src/App.tsx b/src/App.tsx index 478da36d..e4315e8c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { BrowserRouter } from 'react-router-dom'; import { Routes } from './routes/Routes'; import { SessionProvider } from './contexts'; import { Toaster } from './components/ui/toaster'; +import 'leaflet/dist/leaflet.css' const App = () => { return ( diff --git a/src/hooks/usePaginatedQuery/paths.ts b/src/hooks/usePaginatedQuery/paths.ts index d78cd81a..81da7224 100644 --- a/src/hooks/usePaginatedQuery/paths.ts +++ b/src/hooks/usePaginatedQuery/paths.ts @@ -2,4 +2,5 @@ export enum PaginatedQueryPath { Shelters = '/shelters', SupplyCategories = '/supply-categories', Supplies = '/supplies', + Map = '/map', } diff --git a/src/hooks/useShelters/types.ts b/src/hooks/useShelters/types.ts index 42eeceba..8d8c1896 100644 --- a/src/hooks/useShelters/types.ts +++ b/src/hooks/useShelters/types.ts @@ -29,4 +29,5 @@ export interface IUseSheltersDataSupplyData { export interface IUseShelterOptions { cache?: boolean; + getAllShelters?: boolean; } diff --git a/src/hooks/useShelters/useShelters.tsx b/src/hooks/useShelters/useShelters.tsx index 5341ffc9..c1c9531f 100644 --- a/src/hooks/useShelters/useShelters.tsx +++ b/src/hooks/useShelters/useShelters.tsx @@ -7,7 +7,7 @@ import { IPaginatedResponse } from '../usePaginatedQuery/types'; import { IUseShelterOptions, IUseSheltersData } from './types'; const useShelters = (options: IUseShelterOptions = {}) => { - const { cache } = options; + const { cache, getAllShelters } = options; const [loading, setLoading] = useState(false); const [data, setData] = useState>({ count: 0, @@ -15,7 +15,51 @@ const useShelters = (options: IUseShelterOptions = {}) => { perPage: 20, results: [], }); + const [allSheltersData, setAllSheltersData] = useState([]); + const fetchAllShelters = useCallback(async () => { + try { + setLoading(true); + const resultsPerPage = 100; + let page = 1; + let allShelters: IUseSheltersData[] = []; + let totalPages = 0; + + while (true) { + const response = await api.get>('/shelters', { + params: { + orderBy: 'prioritySum', + order: 'desc', + perPage: resultsPerPage, + page, + }, + }); + + const { results, count } = response.data.data; + + allShelters = [...allShelters, ...results]; + + if (page === 1) totalPages = Math.ceil(count / resultsPerPage); + + if (page === totalPages) { + break; + } + + + page++; + } + setAllSheltersData(allShelters); + + } catch (error) { + console.error('Error getting all shelters:', error); + } finally { + setLoading(false); + } + }, []); + + + + const refresh = useCallback( (config: AxiosRequestConfig = {}, append: boolean = false) => { const { search, ...rest } = (config ?? {}).params ?? {}; @@ -57,10 +101,15 @@ const useShelters = (options: IUseShelterOptions = {}) => { ); useEffect(() => { - refresh(); - }, [refresh]); + + if (getAllShelters) { + fetchAllShelters() + } else { + refresh(); + } + }, [fetchAllShelters, getAllShelters, refresh]); - return { data, loading, refresh }; + return { data, loading, refresh, allSheltersData }; }; export { useShelters }; diff --git a/src/pages/Home/components/ShelterListView/ShelterListView.tsx b/src/pages/Home/components/ShelterListView/ShelterListView.tsx index 44bb2ad4..d680d62a 100644 --- a/src/pages/Home/components/ShelterListView/ShelterListView.tsx +++ b/src/pages/Home/components/ShelterListView/ShelterListView.tsx @@ -1,5 +1,5 @@ import React, { Fragment } from 'react'; -import { CircleAlert, ListFilter, Loader } from 'lucide-react'; +import { CircleAlert, ListFilter, Loader, MapIcon } from 'lucide-react'; import { Alert, @@ -10,7 +10,7 @@ import { import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { IShelterListViewProps } from './types'; -import { useSearchParams } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; const ShelterListView = React.forwardRef( (props, ref) => { @@ -28,6 +28,7 @@ const ShelterListView = React.forwardRef( onClearSearch, ...rest } = props; + const navigate = useNavigate(); const [searchParams] = useSearchParams(); @@ -60,6 +61,15 @@ const ShelterListView = React.forwardRef( Filtros + {searchParams.toString() && ( + } + /> +
+

Abrigos com coordenadas cadastradas

+ + + {userLocation && ( + + Você está aqui + + )} + {allSheltersData?.map((item) => { + const shelterItemProps = getAvailabilityProps(item.capacity, item.shelteredPeople); + if (item.latitude && item.longitude) { + return ( + + +
+
+
{item.name}
+ {item.address} +
+
+ {item.petFriendly && } + {item.verified && } +
+
+ {shelterItemProps.availability} +
+
+
+
+ ); + } + return null; + })} +
+
+ + ); +}; + +export { MapComponent }; diff --git a/src/pages/Map/index.ts b/src/pages/Map/index.ts new file mode 100644 index 00000000..2e605978 --- /dev/null +++ b/src/pages/Map/index.ts @@ -0,0 +1,3 @@ +import { MapComponent } from './Map'; + +export { MapComponent }; diff --git a/src/pages/Map/types.ts b/src/pages/Map/types.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/src/pages/Map/types.ts @@ -0,0 +1 @@ +export {}; diff --git a/src/pages/index.ts b/src/pages/index.ts index d3c50f9b..a69198a7 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -7,6 +7,7 @@ import { CreateSupply } from './CreateSupply'; import { CreateShelter } from './CreateShelter'; import { UpdateShelter } from './UpdateShelter'; import { Filter } from './Home/components/Filter'; +import { MapComponent } from './Map'; export { SignIn, @@ -16,5 +17,6 @@ export { CreateSupply, CreateShelter, UpdateShelter, - Filter + Filter, + MapComponent }; diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx index fdca2311..3158a2ab 100644 --- a/src/routes/Routes.tsx +++ b/src/routes/Routes.tsx @@ -8,6 +8,7 @@ import { EditShelterSupply, SignIn, UpdateShelter, + MapComponent } from '@/pages'; const Routes = () => { @@ -24,6 +25,7 @@ const Routes = () => { } /> } /> } /> + } /> ); }; From e39708b7f23f99b1be352e4591ac1de396ed7a62 Mon Sep 17 00:00:00 2001 From: Daniel Rocha Date: Sat, 11 May 2024 13:58:06 -0300 Subject: [PATCH 3/3] refactor: use the new endpoint /shelters/all to retrieve the list of shelters --- src/hooks/useShelters/useShelters.tsx | 38 +++++++-------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/src/hooks/useShelters/useShelters.tsx b/src/hooks/useShelters/useShelters.tsx index c1c9531f..a00b591c 100644 --- a/src/hooks/useShelters/useShelters.tsx +++ b/src/hooks/useShelters/useShelters.tsx @@ -20,35 +20,15 @@ const useShelters = (options: IUseShelterOptions = {}) => { const fetchAllShelters = useCallback(async () => { try { setLoading(true); - const resultsPerPage = 100; - let page = 1; - let allShelters: IUseSheltersData[] = []; - let totalPages = 0; - - while (true) { - const response = await api.get>('/shelters', { - params: { - orderBy: 'prioritySum', - order: 'desc', - perPage: resultsPerPage, - page, - }, - }); - - const { results, count } = response.data.data; - - allShelters = [...allShelters, ...results]; - - if (page === 1) totalPages = Math.ceil(count / resultsPerPage); - - if (page === totalPages) { - break; - } - - - page++; - } - setAllSheltersData(allShelters); + const response = await api.get>('/shelters/all', { + params: { + orderBy: 'prioritySum', + order: 'desc', + }, + }); + const { results } = response.data.data; + + setAllSheltersData(results); } catch (error) { console.error('Error getting all shelters:', error);