Skip to content

Commit 7a7c344

Browse files
kelvinsbmarcodmc
authored and
marcodmc
committed
Modificação nas telas de abrigos para adicionar cidades (SOS-RS#110)
* feat: filter shlters by cities * feat: city on shelter forms, info and list * feat: shelter city quantities on filter * fix: wrong conditional * fix: bug that removing city filter on home doesnt affect the filter * feat: new address fields on shelter forms * feat: useDebouncedValue, useViaCep * feat: adapt useFetch for conditional paths and different response * feat: address fields required and zipCode triggering viaCep * feat: address fields required on update shelter * feat: address fields on shelter details when theres no address field * fix: suggestions(shorthand, optional chaining, separated zipCode, formatted city on list) * feat: sticky footer on filter dialog * fix: reset errors on zipcode search; copy filter change * fix: timeoutId not assigned value; InfoRow copy can copy undefined
1 parent 3ba9c98 commit 7a7c344

File tree

28 files changed

+1071
-157
lines changed

28 files changed

+1071
-157
lines changed

src/components/CardAboutShelter/CardAboutShelter.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,34 @@ import {
55
PawPrint,
66
Landmark,
77
Smartphone,
8+
Building,
9+
MapPinned,
810
} from 'lucide-react';
911

1012
import { Card } from '../ui/card';
1113
import { ICardAboutShelter } from './types';
1214
import { InfoRow } from './components';
15+
import { checkAndFormatAddress } from './utils';
1316

1417
const CardAboutShelter = (props: ICardAboutShelter) => {
1518
const { shelter } = props;
1619

1720
const check = (v?: string | number | boolean | null) => {
1821
return v !== undefined && v !== null;
1922
};
23+
const formatAddress = checkAndFormatAddress(shelter, false);
2024

2125
return (
2226
<Card className="flex flex-col gap-2 p-4 bg-[#E8F0F8] text-sm">
2327
<div className="text-[#646870] font-medium">Sobre o abrigo</div>
2428
<div className="flex flex-col flex-wrap gap-3">
25-
<InfoRow icon={<Home />} label={shelter.address} />
29+
<InfoRow icon={<Home />} label={formatAddress} />
30+
{Boolean(shelter.city) && (
31+
<InfoRow icon={<Building />} label="Cidade:" value={shelter.city} />
32+
)}
33+
{Boolean(shelter.zipCode) && (
34+
<InfoRow icon={<MapPinned />} label="CEP:" value={shelter.zipCode} />
35+
)}
2636
<InfoRow
2737
icon={<PawPrint />}
2838
label={

src/components/CardAboutShelter/components/InfoRow/InfoRow.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,21 @@ import { IInfoRowProps } from './types';
44

55
const InfoRow = React.forwardRef<HTMLDivElement, IInfoRowProps>(
66
(props, ref) => {
7-
const { icon, label, value, clipboardButton = false, className = '', ...rest } = props;
7+
const {
8+
icon,
9+
label,
10+
value,
11+
clipboardButton = false,
12+
className = '',
13+
...rest
14+
} = props;
815
const isLink = value?.startsWith('http');
916
const ValueComp = !value ? (
1017
<Fragment />
1118
) : isLink ? (
12-
<a href={value} target='_blank'
19+
<a
20+
href={value}
21+
target="_blank"
1322
className="text-blue-500 break-all cursor-pointer hover:underline"
1423
>
1524
{value}
@@ -37,7 +46,7 @@ const InfoRow = React.forwardRef<HTMLDivElement, IInfoRowProps>(
3746
</span>
3847
<span className="md:flex">
3948
{ValueComp}
40-
{clipboardButton && (
49+
{clipboardButton && value && (
4150
<div
4251
className="text-blue-600 mx-2 hover:cursor-pointer active:text-blue-800"
4352
onClick={() => navigator.clipboard.writeText(value)}
@@ -46,7 +55,6 @@ const InfoRow = React.forwardRef<HTMLDivElement, IInfoRowProps>(
4655
</div>
4756
)}
4857
</span>
49-
5058
</div>
5159
</div>
5260
);
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
import { CardAboutShelter } from "./CardAboutShelter";
1+
import { CardAboutShelter } from './CardAboutShelter';
2+
import { checkAndFormatAddress } from './utils';
23

3-
export { CardAboutShelter };
4+
export { CardAboutShelter, checkAndFormatAddress };
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { IUseSheltersData } from '@/hooks/useShelters/types';
2+
import { ICardAboutShelter } from './types';
3+
4+
const formatShelterAddressFields = (
5+
shelter: ICardAboutShelter['shelter'] | IUseSheltersData
6+
) =>
7+
[shelter.street, shelter.streetNumber, shelter.neighbourhood]
8+
.filter(Boolean)
9+
.join(', ');
10+
11+
export const checkAndFormatAddress = (
12+
shelter: ICardAboutShelter['shelter'] | IUseSheltersData,
13+
showCity = true
14+
) =>
15+
shelter.address ??
16+
`${formatShelterAddressFields(shelter)}${
17+
showCity ? ` - ${shelter.city}` : ''
18+
}`;

src/components/SearchInput/SearchInput.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import { cn } from '@/lib/utils';
77

88
const SearchInput = React.forwardRef<HTMLDivElement, ISearchInputProps>(
99
(props, ref) => {
10-
const { value, onChange, className, ...rest } = props;
10+
const {
11+
value,
12+
onChange,
13+
className,
14+
placeholder = 'Buscar por abrigo ou endereço',
15+
...rest
16+
} = props;
1117

1218
return (
1319
<div ref={ref} className={cn(className, 'relative')} {...rest}>
1420
<Input
1521
value={value}
16-
placeholder="Buscar por abrigo ou endereço"
22+
placeholder={placeholder}
1723
className="h-12 text-md font-medium text-zinc-600 pl-10 pr-4"
1824
onChange={onChange}
1925
/>

src/hooks/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import { useFetch } from './useFetch';
33
import { usePaginatedQuery } from './usePaginatedQuery';
44
import { useThrottle } from './useThrottle';
55
import { useShelter } from './useShelter';
6+
import { useShelterCities } from './useShelterCities';
7+
import { useDebouncedValue } from './useDebouncedValue';
68
import { useSupplyCategories } from './useSupplyCategories';
79
import { useSupplies } from './useSupplies';
10+
import { useViaCep } from './useViaCep';
811
import { usePartners } from './usePartners';
912

1013
export {
@@ -13,7 +16,10 @@ export {
1316
usePaginatedQuery,
1417
useThrottle,
1518
useShelter,
19+
useShelterCities,
20+
useDebouncedValue,
1621
useSupplyCategories,
1722
useSupplies,
23+
useViaCep,
1824
usePartners,
1925
};

src/hooks/useDebouncedValue/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { useDebouncedValue } from './useDebouncedValue';
2+
3+
export { useDebouncedValue };
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useState, useEffect } from 'react';
2+
3+
export const useDebouncedValue = (value: string, delay: number): string => {
4+
const [debouncedValue, setDebouncedValue] = useState<string>(value);
5+
const [currentValue, setCurrentValue] = useState<string>(value);
6+
7+
useEffect(() => {
8+
setCurrentValue(value);
9+
}, [value]);
10+
11+
useEffect(() => {
12+
let timeoutId: NodeJS.Timeout | null = null;
13+
14+
if (currentValue !== debouncedValue) {
15+
if (timeoutId) clearTimeout(timeoutId);
16+
timeoutId = setTimeout(() => {
17+
setDebouncedValue(currentValue);
18+
}, delay);
19+
}
20+
21+
return () => {
22+
if (timeoutId) clearTimeout(timeoutId);
23+
};
24+
}, [currentValue, debouncedValue, delay]);
25+
26+
return debouncedValue;
27+
};

src/hooks/useFetch/useFetch.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { api } from '@/api';
55
import { IServerResponse } from '@/types';
66
import { IUseFetchOptions } from './types';
77

8-
function useFetch<T = any>(path: string, options: IUseFetchOptions<T> = {}) {
8+
function useFetch<T = any>(path?: string, options: IUseFetchOptions<T> = {}) {
99
const { cache, initialValue } = options;
1010
const [loading, setLoading] = useState<boolean>(true);
1111
const [data, setData] = useState<T>(initialValue || ({} as T));
@@ -15,10 +15,15 @@ function useFetch<T = any>(path: string, options: IUseFetchOptions<T> = {}) {
1515
const headers = config?.headers ?? {};
1616
if (cache) headers['x-app-cache'] = 'true';
1717
setLoading(true);
18-
api
19-
.get<IServerResponse<T>>(path, { ...config, headers })
20-
.then(({ data }) => setData(data.data))
21-
.finally(() => setLoading(false));
18+
19+
if (path) {
20+
api
21+
.get<IServerResponse<T>>(path, { ...config, headers })
22+
.then(({ data }) => setData(data.data ?? (data as T)))
23+
.finally(() => setLoading(false));
24+
} else {
25+
setLoading(false);
26+
}
2227
},
2328
[cache, path]
2429
);

src/hooks/usePaginatedQuery/paths.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export enum PaginatedQueryPath {
22
Shelters = '/shelters',
3+
ShelterCities = '/shelters/cities',
34
SupplyCategories = '/supply-categories',
45
Supplies = '/supplies',
56
}

src/hooks/useShelter/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
export interface IUseShelterData {
22
id: string;
33
name: string;
4+
street?: string;
5+
neighbourhood?: string;
6+
city?: string;
7+
streetNumber?: string | null;
8+
zipCode?: string;
49
address: string;
510
pix?: string | null;
611
shelteredPeople?: number | null;

src/hooks/useShelterCities/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { useShelterCities } from './useShelterCities';
2+
3+
export { useShelterCities };

src/hooks/useShelterCities/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface IShelterCitiesData {
2+
city: string;
3+
sheltersCount: string;
4+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { useFetch } from '../useFetch';
2+
import { PaginatedQueryPath } from '../usePaginatedQuery/paths';
3+
import { IShelterCitiesData } from './types';
4+
5+
export const useShelterCities = () => {
6+
return useFetch<IShelterCitiesData[]>(PaginatedQueryPath.ShelterCities, {
7+
cache: true,
8+
});
9+
};

src/hooks/useShelters/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { ShelterTagType } from '@/pages/Home/components/ShelterListItem/types';
33
export interface IUseSheltersData {
44
id: string;
55
name: string;
6+
street?: string;
7+
neighbourhood?: string;
8+
city?: string;
9+
streetNumber?: string | null;
10+
zipCode?: string;
611
address: string;
712
pix?: string | null;
813
shelteredPeople?: number | null;

src/hooks/useViaCep/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { useViaCep } from './useViaCep';
2+
3+
export { useViaCep };

src/hooks/useViaCep/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface IViaCepData {
2+
cep: string;
3+
logradouro: string;
4+
complemento: string;
5+
bairro: string;
6+
localidade: string;
7+
uf: string;
8+
ibge: string;
9+
gia: string;
10+
dd: string;
11+
siafi: string;
12+
}

src/hooks/useViaCep/useViaCep.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { useFetch } from '..';
2+
import { IViaCepData } from './types';
3+
4+
export const useViaCep = (cep: string | undefined) => {
5+
const createdPath =
6+
!cep || cep.length < 8
7+
? undefined
8+
: `https://viacep.com.br/ws/${cep}/json/`;
9+
return useFetch<IViaCepData>(createdPath);
10+
};

0 commit comments

Comments
 (0)