Skip to content

Commit

Permalink
Closes #327: Cria tela "List Donations" (#343)
Browse files Browse the repository at this point in the history
### Nova Tela de Listagem de Doações
- Adiciona nova rota '/abrigo/:shelterId/doacoes' para display de
doações dos abrigos;
- Cria novo folder `DonationsHistory`;
- Cria Page `DonationsHistory` e components acessórios;
- Cria folder `useDonations`;

### Em aberto
- Falta fazer a autenticação para que apenas o doador possa Confirmar ou
Cancelar a doação. Essa funcionalidade necessita de uma tabela de join
users_shelters no backend.
- Após essa implementação, será refatorado para fazer a verificação. O
PR foi feito para já liberar para QA.

Closes #327
  • Loading branch information
rhuam authored Jun 4, 2024
2 parents 4b869f5 + afe4689 commit f7498f0
Show file tree
Hide file tree
Showing 13 changed files with 519 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,21 @@ import { SupplyMeasureMap, cn } from '@/lib/utils';
import { useDonationOrder } from '@/hooks';
import { format } from 'date-fns';
import { Button } from '@/components/ui/button';
import { useNavigate } from 'react-router-dom';

const DonationSuccess = React.forwardRef<HTMLDivElement, IDonationSuccessProps>(
(props, ref) => {
const { donationOrderId, className = '', ...rest } = props;
const { data: donation, loading } = useDonationOrder(donationOrderId);
const navigate = useNavigate();

if (loading)
return <Loader className="stroke-gray-500 w-6 h-6 animate-spin" />;

const handleRedirect = () => {
navigate(`/abrigo/${donation.shelter.id}/doacoes`);
};

return (
<div ref={ref} className={cn('contents', className)} {...rest}>
<SheetHeader className="px-4">
Expand Down Expand Up @@ -71,7 +77,11 @@ const DonationSuccess = React.forwardRef<HTMLDivElement, IDonationSuccessProps>(
</div>
</div>
<div className="px-4 py-6 flex justify-center items-center border-t-[1px] border-border">
<Button className="w-full" variant="destructive">
<Button
className="w-full"
variant="destructive"
onClick={handleRedirect}
>
Verificar histórico de doações
</Button>
</div>
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/useDonations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useDonations } from './useDonations';

export { useDonations };
25 changes: 25 additions & 0 deletions src/hooks/useDonations/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export interface IUseDonationsData {
page: number;
perPage: number;
count: number;
results: IDonationsData[];
}

export interface IDonationsData {
id: string;
userId: string;
status: string;
shelter: {
id: string;
name: string;
};
donationOrderSupplies: {
quantity: number;
supply: {
measure: string;
name: string;
};
};
createdAt: string;
updatedAt: string;
}
9 changes: 9 additions & 0 deletions src/hooks/useDonations/useDonations.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useFetch } from '../useFetch';
import { PaginatedQueryPath } from '../usePaginatedQuery/paths';
import { IUseDonationsData } from './types';

const useDonations = (shelterId: string) => {

Check failure on line 5 in src/hooks/useDonations/useDonations.tsx

View workflow job for this annotation

GitHub Actions / build

'shelterId' is declared but its value is never read.
return useFetch<IUseDonationsData>(`${PaginatedQueryPath.DonationOrder}`);
};

export { useDonations };
155 changes: 155 additions & 0 deletions src/pages/DonationsHistory/DonationsHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { Header, LoadingScreen } from '@/components';
import { Button } from '@/components/ui/button';
import { ChevronLeft } from 'lucide-react';
import { useNavigate, useParams } from 'react-router-dom';
import { useShelter } from '@/hooks';
import { IDonations, IDonationsPerDay, ViewOptions } from './types';
import { useDonations } from '@/hooks/useDonations';
import { useEffect, useState } from 'react';
import { DonationsPerDay } from './components/DonationsPerDay';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';

const DonationsHistory = () => {
const navigate = useNavigate();
const params = useParams();
const { shelterId = '-1' } = params;
const { data: shelter, loading: shelterLoading } = useShelter(shelterId);

Check failure on line 17 in src/pages/DonationsHistory/DonationsHistory.tsx

View workflow job for this annotation

GitHub Actions / build

'shelterLoading' is declared but its value is never read.
const { data: shelterDonations, loading: donationsLoading } =
useDonations(shelterId);
const [donationsReceivedPerDay, setDonationsReceivedPerDay] = useState<
IDonationsPerDay | {}
>([]);
const [donationsGivenPerDay, setDonationsGivenPerDay] = useState<
IDonationsPerDay | {}
>([]);

const [viewOption, setViewOption] = useState(ViewOptions.Donated);

const toggleViewOption = () => {
setViewOption((prevOption) =>
prevOption === ViewOptions.Donated
? ViewOptions.Received
: ViewOptions.Donated
);
};

const donationGroupedByDate = (donations: IDonations): IDonationsPerDay => {
return donations.reduce<IDonationsPerDay>((acc, donation) => {
const date = donation.createdAt.split('T')[0];

if (!acc[date]) {
acc[date] = [];
}
acc[date].push(donation);

return acc;
}, {});
};

const filterDonationsByCase = (
donations: IDonationsPerDay,
shelterId: string
) => {
const receivedDonations: IDonationsPerDay = {};
const givenDonations: IDonationsPerDay = {};

Object.keys(donations).forEach((date) => {
receivedDonations[date] = donations[date].filter(
(donation) => donation.shelter.id === shelterId
);
givenDonations[date] = donations[date].filter(
(donation) => donation.shelter.id !== shelterId
);
});

return { receivedDonations, givenDonations };
};

useEffect(() => {
if (!donationsLoading) {
const donationsPerDay = donationGroupedByDate(shelterDonations.results);
const { receivedDonations, givenDonations } = filterDonationsByCase(
donationsPerDay,
shelterId
);
setDonationsGivenPerDay(givenDonations);
setDonationsReceivedPerDay(receivedDonations);
}
}, [donationsLoading]);

if (!donationsLoading) {
const dailyDonations = {
donated: donationsGivenPerDay,
received: donationsReceivedPerDay,
};

const segmentedDonationsDisplay = Object.keys(
dailyDonations[viewOption]
).map((day) => {
return (
<div key={day} className="mb-4">
<h3 className="font-semibold text-lg">
{format(day, "dd 'de' MMMM yyyy ", { locale: ptBR })}
</h3>
<DonationsPerDay
donations={dailyDonations[viewOption][day]}

Check failure on line 96 in src/pages/DonationsHistory/DonationsHistory.tsx

View workflow job for this annotation

GitHub Actions / build

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{} | IDonationsPerDay'.
viewOption={viewOption}
key={`${viewOption}-${day}`}
/>
</div>
);
});

if (donationsLoading) return <LoadingScreen />;

return (
<div className="flex flex-col h-screen items-center">
<Header
title={shelter.name}
startAdornment={
<Button
size="sm"
variant="ghost"
className="[&_svg]:stroke-white disabled:bg-red-500 hover:bg-red-400"
onClick={() => navigate('/')}
>
<ChevronLeft size={20} />
</Button>
}
/>
<div className="p-4 flex flex-col max-w-5xl w-full h-full gap-4">
<div className="flex items-center gap-1">
<h1 className="text-[#2f2f2f] font-semibold text-2xl">
Suas doações
</h1>
</div>
<div className="flex p-0 m-0 gap-4">
<div
className={`flex items-center justify-center p-0 m-0 border-b-2 border-transparent ${
viewOption === ViewOptions.Donated ? 'border-red-500' : ''
}`}
onClick={() => toggleViewOption()}
>
<h3 className="font-semibold text-lg h-full hover:cursor-pointer hover:text-slate-500">
Doado
</h3>
</div>
<div
className={`flex items-center justify-center p-0 m-0 border-b-2 border-transparent ${
viewOption === ViewOptions.Received ? 'border-red-500' : ''
}`}
onClick={() => toggleViewOption()}
>
<h3 className="font-semibold text-lg h-full hover:cursor-pointer hover:text-slate-500">
Recebido
</h3>
</div>
</div>
{segmentedDonationsDisplay}
</div>
</div>
);
}
};
export { DonationsHistory };
86 changes: 86 additions & 0 deletions src/pages/DonationsHistory/components/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react';
import {
Dialog,
DialogTrigger,
DialogPortal,
DialogOverlay,
DialogContent,
DialogFooter,
DialogTitle,
DialogDescription,
} from '../../../components/ui/dialog';
import { Button } from '../../../components/ui/button';

interface ConfirmationDialogProps {
title: string;
description: string;
confirmLabel: string;
cancelLabel: string;
onConfirm: () => void;
onCancel: () => void;
triggerLabel: React.ReactNode;
Icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}

const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
title,
description,
confirmLabel,
cancelLabel,
onConfirm,

triggerLabel,
Icon,
}) => {
const [isOpen, setIsOpen] = React.useState(false);

return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button
variant="ghost"
size="sm"
className="disabled:bg-red-500 hover:bg-red-400 relative"
onClick={() => setIsOpen(true)}
>
<Icon className="w-6 h-6 stroke-red-500" />
{triggerLabel}
</Button>
</DialogTrigger>
<DialogPortal>
<DialogOverlay className="fixed inset-0 z-50 bg-black/10" />
<DialogContent className="fixed left-[50%] top-[50%] z-50 w-full max-w-md translate-x-[-50%] translate-y-[-50%] gap-4 bg-white p-6 shadow-lg rounded-lg">
<div className="flex justify-between items-center">
<DialogTitle className="text-lg font-semibold">{title}</DialogTitle>
</div>
<DialogDescription className="text-left text-sm text-gray-500 mb-4">
{description}
</DialogDescription>
<DialogFooter className="flex justify-center space-x-4">
<Button
variant="ghost"
onClick={() => {
onConfirm();
setIsOpen(false);
}}
className="bg-red-500 text-white px-6 py-2 rounded-md"
>
{confirmLabel}
</Button>
<Button
variant="ghost"
onClick={() => {
setIsOpen(false);
}}
className="bg-gray-200 text-black px-6 py-2 rounded-md"
>
{cancelLabel}
</Button>
</DialogFooter>
</DialogContent>
</DialogPortal>
</Dialog>
);
};

export { ConfirmationDialog };
Loading

0 comments on commit f7498f0

Please sign in to comment.