diff --git a/src/pages/Shelter/Shelter.tsx b/src/pages/Shelter/Shelter.tsx
index 78fea039..7b41487c 100644
--- a/src/pages/Shelter/Shelter.tsx
+++ b/src/pages/Shelter/Shelter.tsx
@@ -1,51 +1,51 @@
-import { useCallback, useMemo, useState } from 'react';
+import { Fragment, useCallback, useContext, useMemo, useState } from 'react';
import { ChevronLeft, Pencil } from 'lucide-react';
import { useNavigate, useParams } from 'react-router-dom';
-import { format } from 'date-fns';
import {
Authenticated,
CardAboutShelter,
+ Chip,
+ DonationCart,
+ DonationCartIcon,
Header,
LoadingScreen,
+ SearchInput,
} from '@/components';
import { useShelter } from '@/hooks';
-import { IShelterAvailabilityProps } from '@/pages/Home/components/ShelterListItem/types';
-import { cn, getAvailabilityProps, group } from '@/lib/utils';
+import {
+ cn,
+ getAvailabilityProps,
+ getSupplyPriorityProps,
+ group,
+ normalizedCompare,
+} from '@/lib/utils';
import { Button } from '@/components/ui/button';
-import { ShelterCategoryItems } from './components';
+import { VerifiedBadge } from '@/components/VerifiedBadge/VerifiedBadge.tsx';
import {
- IShelterCategoryItemsProps,
- ITagItem,
-} from './components/ShelterCategoryItems/types';
+ IUseShelterDataSupply,
+ ShelterCategory,
+} from '@/hooks/useShelter/types';
+import { IShelterAvailabilityProps } from '../Home/components/ShelterListItem/types';
import { SupplyPriority } from '@/service/supply/types';
-import { VerifiedBadge } from '@/components/VerifiedBadge/VerifiedBadge.tsx';
-import { ShelterSupplyServices } from '@/service';
-import { useToast } from '@/components/ui/use-toast';
-import { clearCache } from '@/api/cache';
-import { ShelterCategory } from '@/hooks/useShelter/types';
+import { ShelterCategoryList } from './components';
+import { Separator } from '@/components/ui/separator';
+import { DonationCartContext } from '@/contexts';
+import { ShelterCategoryListItemProps } from './components/ShelterCategoryList/types';
+
+const defaultPriorities: SupplyPriority[] = [
+ SupplyPriority.Urgent,
+ SupplyPriority.Needing,
+ SupplyPriority.Remaining,
+];
const Shelter = () => {
const params = useParams();
const { shelterId = '-1' } = params;
const navigate = useNavigate();
- const { data: shelter, loading, refresh } = useShelter(shelterId);
- const [selectedTags, setSelectedTags] = useState
([]);
- const shelterCategories: IShelterCategoryItemsProps[] = useMemo(() => {
- const grouped = group(shelter?.shelterSupplies ?? [], 'priority');
- delete grouped[SupplyPriority.NotNeeded];
-
- return Object.entries(grouped)
- .sort(([a], [b]) => (+a > +b ? -1 : 1))
- .map(([key, values]) => ({
- priority: +key,
- tags: values.map((v) => ({
- label: v.supply.name,
- value: v.supply.id,
- quantity: v.quantity,
- })),
- }));
- }, [shelter?.shelterSupplies]);
+ const { toggleOpened, addItem, opened, carts } =
+ useContext(DonationCartContext);
+ const { data: shelter, loading } = useShelter(shelterId);
const { availability, className: availabilityClassName } =
useMemo(
() =>
@@ -56,130 +56,170 @@ const Shelter = () => {
}),
[shelter?.capacity, shelter?.shelteredPeople, shelter?.category]
);
- const [loadingUpdateMany, setLoadingUpdateMany] = useState(false);
- const { toast } = useToast();
+ const [priorities, setPriorities] =
+ useState(defaultPriorities);
+ const [search, setSearch] = useState('');
+
+ const supplyGroups = useMemo(() => {
+ if (!shelter?.shelterSupplies) return {};
+ const groups = group(shelter.shelterSupplies, 'supply.supplyCategory.name');
+ return Object.entries(groups).reduce((prev, [name, list]) => {
+ const filtered = list.filter(
+ (l) =>
+ priorities.includes(l.priority) &&
+ (!search || normalizedCompare(l.supply.name, search))
+ );
+ if (filtered.length > 0) return { [name]: filtered, ...prev };
+ else return prev;
+ }, {} as Record);
+ }, [shelter, priorities, search]);
- const handleSelectTag = useCallback((v: ITagItem) => {
- setSelectedTags((prev) =>
- prev.includes(v) ? prev.filter((p) => p.value !== v.value) : [...prev, v]
+ const handleSelectPriority = (priority: SupplyPriority) => {
+ setPriorities((prev) =>
+ prev.includes(priority)
+ ? prev.filter((p) => p !== priority)
+ : [...prev, priority]
);
- }, []);
+ };
- const handleUpdateMany = useCallback(() => {
- setLoadingUpdateMany(true);
- ShelterSupplyServices.updateMany(
- shelterId,
- selectedTags.map((s) => s.value)
- )
- .then(() => {
- toast({
- title: 'Atualizado com sucesso',
- });
- clearCache(false);
- refresh();
- setSelectedTags([]);
- })
- .catch((err) => {
- toast({
- title: 'Erro ao atualizar',
- description: `${err?.response?.data?.message ?? err?.message ?? err}`,
- });
- })
- .finally(() => {
- setLoadingUpdateMany(false);
+ const handleDonate = useCallback(
+ (item: ShelterCategoryListItemProps) => {
+ if (!opened) {
+ const hasViewedCart =
+ localStorage.getItem('has-viewed-cart') === 'true';
+ if (!hasViewedCart) {
+ localStorage.setItem('has-viewed-cart', 'true');
+ toggleOpened();
+ }
+ }
+ addItem(shelterId, {
+ ...item,
+ quantity: item.quantity || 1,
});
- }, [refresh, selectedTags, shelterId, toast]);
+ },
+ [addItem, opened, shelterId, toggleOpened]
+ );
if (loading) return ;
return (
-
-
navigate('/')}
- >
-
-
- }
+
+
-
-
-
- {shelter.name}
-
- {shelter.verified && }
-
-
-
- {availability}
-
-
+
+
navigate(`/abrigo/${shelterId}/atualizar`)}
+ className="[&_svg]:stroke-white disabled:bg-red-500 hover:bg-red-400"
+ onClick={() => navigate('/')}
>
- Editar
-
+
-
-
-
-
-
-
-
Itens do abrigo
-
-
-
+
);
};
diff --git a/src/pages/Shelter/components/ShelterCategoryItems/ShelterCategoryItems.tsx b/src/pages/Shelter/components/ShelterCategoryItems/ShelterCategoryItems.tsx
deleted file mode 100644
index edc16d09..00000000
--- a/src/pages/Shelter/components/ShelterCategoryItems/ShelterCategoryItems.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import { useContext, useMemo, useState } from 'react';
-import { ChevronDown, ChevronUp } from 'lucide-react';
-import { cva } from 'class-variance-authority';
-
-import { IShelterCategoryItemsProps } from './types';
-import { cn, getSupplyPriorityProps } from '@/lib/utils';
-import { CircleStatus, Chip } from '@/components';
-import { Button } from '@/components/ui/button';
-import { SupplyPriority } from '@/service/supply/types';
-import { SessionContext } from '@/contexts';
-import { Badge } from '@/components/ui/badge';
-
-const ShelterCategoryItems = (props: IShelterCategoryItemsProps) => {
- const {
- priority = SupplyPriority.NotNeeded,
- tags,
- onSelectTag,
- selectedTags = [],
- } = props;
- const { session } = useContext(SessionContext);
- const [opened, setOpened] = useState(false);
- const maxVisibleSupplies: number = 10;
- const visibleSupplies = useMemo(
- () => (opened ? tags : tags.slice(0, maxVisibleSupplies)),
- [opened, tags]
- );
- const { className: circleClassName, label } = useMemo(
- () => getSupplyPriorityProps(priority),
- [priority]
- );
-
- const Icon = opened ? ChevronUp : ChevronDown;
- const btnLabel = opened ? 'Ver menos' : 'Ver todos';
-
- const variants = cva('cursor-pointer', {
- variants: {
- variant: {
- selected: 'border-4 border-blue-300',
- default: 'border-4 border-gray-100',
- },
- },
- defaultVariants: {
- variant: 'default',
- },
- });
-
- return (
-
-
-
-
- {label} ({tags.length})
-
-
-
- {visibleSupplies.map((tag, idx) => {
- const tagProps =
- session &&
- ['DistributionCenter', 'Admin'].includes(session.accessLevel)
- ? {
- onClick: () => (onSelectTag ? onSelectTag(tag) : undefined),
- className: variants({
- className: circleClassName,
- variant: selectedTags.includes(tag)
- ? 'selected'
- : 'default',
- }),
- }
- : {
- className: circleClassName,
- };
- return (
-
-
- {tag.quantity !== null &&
- tag.quantity !== undefined &&
- tag.quantity > 0 && (
-
- {tag.quantity > 99 ? '99+' : tag.quantity}
-
- )}
-
- );
- })}
-
-
- {tags.length > maxVisibleSupplies && (
-
- setOpened((v) => !v)}
- >
-
- {btnLabel}
-
-
-
-
- )}
-
- );
-};
-
-export { ShelterCategoryItems };
diff --git a/src/pages/Shelter/components/ShelterCategoryItems/index.ts b/src/pages/Shelter/components/ShelterCategoryItems/index.ts
deleted file mode 100644
index 78d7838d..00000000
--- a/src/pages/Shelter/components/ShelterCategoryItems/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { ShelterCategoryItems } from './ShelterCategoryItems';
-
-export { ShelterCategoryItems };
diff --git a/src/pages/Shelter/components/ShelterCategoryItems/types.ts b/src/pages/Shelter/components/ShelterCategoryItems/types.ts
deleted file mode 100644
index ebb2caa8..00000000
--- a/src/pages/Shelter/components/ShelterCategoryItems/types.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { SupplyPriority } from '@/service/supply/types';
-
-export interface ITagItem {
- label: string;
- value: string;
- quantity?: number | null;
-}
-
-export interface IShelterCategoryItemsProps {
- priority?: SupplyPriority;
- tags: ITagItem[];
- selectedTags?: ITagItem[];
- onSelectTag?: (v: ITagItem) => void;
-}
diff --git a/src/pages/Shelter/components/ShelterCategoryList/ShelterCategoryList.tsx b/src/pages/Shelter/components/ShelterCategoryList/ShelterCategoryList.tsx
new file mode 100644
index 00000000..a5ac597a
--- /dev/null
+++ b/src/pages/Shelter/components/ShelterCategoryList/ShelterCategoryList.tsx
@@ -0,0 +1,57 @@
+import clsx from 'clsx';
+
+import { SupplyMeasureMap, cn, getSupplyPriorityProps } from '@/lib/utils';
+import { ShelterCategoryListProps } from './types';
+import { CircleStatus } from '@/components';
+import { SupplyPriority } from '@/service/supply/types';
+
+const ShelterCategoryList = (props: ShelterCategoryListProps) => {
+ const { items, name, onDonate } = props;
+
+ return (
+
+
+ {name} ({items.length} items)
+
+
+ {items
+ .sort((a, b) => b.priority - a.priority)
+ .map((item) => {
+ const { className } = getSupplyPriorityProps(item.priority);
+ return (
+
+
+
+ {item.name}
+
+
+ {item.quantity && (
+ {`${
+ item.quantity
+ } ${SupplyMeasureMap[item.measure]}`}
+ )}
+ onDonate(item)}
+ className={clsx(
+ 'text-red-600 font-semibold hover:bg-red-50 active:bg-red-100 px-4 py-1 rounded-md',
+ {
+ invisible: item.priority === SupplyPriority.Remaining,
+ }
+ )}
+ >
+ doar
+
+
+
+ );
+ })}
+
+
+ );
+};
+
+export { ShelterCategoryList };
diff --git a/src/pages/Shelter/components/ShelterCategoryList/index.ts b/src/pages/Shelter/components/ShelterCategoryList/index.ts
new file mode 100644
index 00000000..eb8b93f0
--- /dev/null
+++ b/src/pages/Shelter/components/ShelterCategoryList/index.ts
@@ -0,0 +1,3 @@
+import { ShelterCategoryList } from './ShelterCategoryList';
+
+export { ShelterCategoryList };
diff --git a/src/pages/Shelter/components/ShelterCategoryList/types.ts b/src/pages/Shelter/components/ShelterCategoryList/types.ts
new file mode 100644
index 00000000..2c37df3a
--- /dev/null
+++ b/src/pages/Shelter/components/ShelterCategoryList/types.ts
@@ -0,0 +1,16 @@
+import { SupplyMeasure } from '@/hooks/useShelter/types';
+import { SupplyPriority } from '@/service/supply/types';
+
+export interface ShelterCategoryListItemProps {
+ id: string;
+ name: string;
+ quantity?: number | null;
+ priority: SupplyPriority;
+ measure: SupplyMeasure;
+}
+
+export interface ShelterCategoryListProps {
+ name: string;
+ onDonate: (item: ShelterCategoryListItemProps) => void;
+ items: ShelterCategoryListItemProps[];
+}
diff --git a/src/pages/Shelter/components/index.ts b/src/pages/Shelter/components/index.ts
index 78d7838d..eb8b93f0 100644
--- a/src/pages/Shelter/components/index.ts
+++ b/src/pages/Shelter/components/index.ts
@@ -1,3 +1,3 @@
-import { ShelterCategoryItems } from './ShelterCategoryItems';
+import { ShelterCategoryList } from './ShelterCategoryList';
-export { ShelterCategoryItems };
+export { ShelterCategoryList };
diff --git a/src/service/donationOrder/donationOrder.service.ts b/src/service/donationOrder/donationOrder.service.ts
new file mode 100644
index 00000000..717fd2c9
--- /dev/null
+++ b/src/service/donationOrder/donationOrder.service.ts
@@ -0,0 +1,34 @@
+import { api } from '@/api';
+import {
+ ICreateDonateResponse,
+ ICreateDonationOrderProps,
+ IDonateOrderItem,
+} from './types';
+import { IServerResponse } from '@/types';
+import { IPaginatedResponse } from '@/hooks/usePaginatedQuery/types';
+
+const DonationOrderServices = {
+ store: async (payload: ICreateDonationOrderProps) => {
+ const { data } = await api.post>(
+ '/donation/order',
+ payload
+ );
+ return data;
+ },
+ getAll: async (shelterId: string) => {
+ const { data } = await api.get<
+ IServerResponse>
+ >('/donation/order', {
+ params: { shelterId },
+ });
+ return data;
+ },
+ find: async (id: string) => {
+ const { data } = await api.get>(
+ `/donation/order/${id}`
+ );
+ return data;
+ },
+};
+
+export { DonationOrderServices };
diff --git a/src/service/donationOrder/index.ts b/src/service/donationOrder/index.ts
new file mode 100644
index 00000000..0ba66e4a
--- /dev/null
+++ b/src/service/donationOrder/index.ts
@@ -0,0 +1,3 @@
+import { DonationOrderServices } from './donationOrder.service';
+
+export { DonationOrderServices };
diff --git a/src/service/donationOrder/types.ts b/src/service/donationOrder/types.ts
new file mode 100644
index 00000000..cad3c9b7
--- /dev/null
+++ b/src/service/donationOrder/types.ts
@@ -0,0 +1,51 @@
+import { SupplyMeasure } from '@/hooks/useShelter/types';
+
+export interface IDonateItem {
+ id: string;
+ quantity: number;
+}
+
+export interface ICreateDonationOrderProps {
+ shelterId: string;
+ supplies: IDonateItem[];
+}
+
+export interface ICreateDonateResponse {
+ id: string;
+ userId: string;
+ shelterId: string;
+ status: string;
+ createdAt: string;
+ updatedAt?: string | null;
+}
+
+export enum DonateOrderStatus {
+ Pending = 'Pending',
+ Canceled = 'Canceled',
+ Complete = 'Complete',
+}
+
+export interface IDonateOrderItem {
+ id: string;
+ status: DonateOrderStatus;
+ userId: string;
+ shelter: IDonateOrderShelter;
+ donationOrderSupplies: IDonateOrderItemSupply[];
+ createdAt: string;
+ updatedAt?: string | null;
+}
+
+export interface IDonateOrderItemSupply {
+ quantity: number;
+ supply: IDonateOrderSupply;
+}
+
+export interface IDonateOrderShelter {
+ id: string;
+ name: string;
+}
+
+export interface IDonateOrderSupply {
+ name: string;
+ measure: SupplyMeasure;
+}
diff --git a/src/service/index.ts b/src/service/index.ts
index b9e629ca..50795f9d 100644
--- a/src/service/index.ts
+++ b/src/service/index.ts
@@ -3,6 +3,7 @@ import { ShelterServices } from './shelter';
import { SupplyServices } from './supply';
import { ShelterSupplyServices } from './shelterSupply';
import { UserServices } from './users';
+import { DonationOrderServices } from './donationOrder';
export {
UserServices,
@@ -10,4 +11,5 @@ export {
SupplyServices,
ShelterServices,
ShelterSupplyServices,
+ DonationOrderServices,
};
diff --git a/src/service/shelterSupply/shelter-supply.service.ts b/src/service/shelterSupply/shelter-supply.service.ts
index aef057cb..04c617f6 100644
--- a/src/service/shelterSupply/shelter-supply.service.ts
+++ b/src/service/shelterSupply/shelter-supply.service.ts
@@ -1,6 +1,10 @@
import { api } from '@/api';
import { IServerResponse } from '@/types';
-import { ICreateShelterSupply, IUpdateShelterSupply } from './types';
+import {
+ ICreateShelterSupply,
+ IShelterSupplyData,
+ IUpdateShelterSupply,
+} from './types';
const ShelterSupplyServices = {
update: async (
@@ -14,22 +18,18 @@ const ShelterSupplyServices = {
);
return data;
},
- updateMany: async (
- shelterId: string,
- supplyIds: string[]
- ): Promise => {
- const { data } = await api.put(
- `/shelter/supplies/${shelterId}/supplies/many`,
- {
- ids: supplyIds,
- }
- );
- return data;
- },
create: async (payload: ICreateShelterSupply): Promise => {
const { data } = await api.post('/shelter/supplies', payload);
return data;
},
+ getAll: async (
+ shelterId: string
+ ): Promise> => {
+ const { data } = await api.get>(
+ `/shelter/supplies/${shelterId}`
+ );
+ return data;
+ },
};
export { ShelterSupplyServices };
diff --git a/src/service/shelterSupply/types.ts b/src/service/shelterSupply/types.ts
index 6c4d48ba..c41da483 100644
--- a/src/service/shelterSupply/types.ts
+++ b/src/service/shelterSupply/types.ts
@@ -1,3 +1,4 @@
+import { SupplyMeasure } from '@/hooks/useShelter/types';
import { SupplyPriority } from '../supply/types';
export interface IShelterSupply {
@@ -18,3 +19,25 @@ export type ICreateShelterSupply = Pick<
IShelterSupply,
'priority' | 'shelterId' | 'supplyId' | 'quantity'
>;
+
+export interface IShelterSupplyData {
+ priority: number;
+ quantity: number;
+ supply: IShelterSupplyDataSupply;
+ createdAt: string;
+ updatedAt?: string | null;
+}
+
+export interface IShelterSupplyDataSupply {
+ id: string;
+ name: string;
+ measure: SupplyMeasure;
+ supplyCategory: IShelterSupplyDataSupplyCategory;
+ createdAt: string;
+ updatedAt?: string | null;
+}
+
+export interface IShelterSupplyDataSupplyCategory {
+ id: string;
+ name: string;
+}
diff --git a/src/service/users/types.ts b/src/service/users/types.ts
index 3977b590..28e76bfa 100644
--- a/src/service/users/types.ts
+++ b/src/service/users/types.ts
@@ -22,3 +22,7 @@ export interface ICreateUser {
lastName: string;
phone: string;
}
+
+export interface IFindUserResponse {
+ exists: boolean;
+}
diff --git a/src/service/users/user.service.ts b/src/service/users/user.service.ts
index a5d2f296..5e29658a 100644
--- a/src/service/users/user.service.ts
+++ b/src/service/users/user.service.ts
@@ -1,7 +1,7 @@
import { api } from '../../api';
import { IServerResponse } from '@/types';
-import { ICreateUser, IUpdateUser } from './types';
+import { ICreateUser, IFindUserResponse, IUpdateUser, IUser } from './types';
const UserServices = {
create: async (payload: ICreateUser): Promise => {
@@ -18,6 +18,15 @@ const UserServices = {
);
return data;
},
+ find: async (
+ field: keyof IUser,
+ value: string
+ ): Promise> => {
+ const { data } = await api.get>(
+ `/users/find/${field}/${value}`
+ );
+ return data;
+ },
};
export { UserServices };