Skip to content

Commit

Permalink
feat: you can now add books to collections from within the Book.tsx view
Browse files Browse the repository at this point in the history
  • Loading branch information
Franqsanz committed Dec 13, 2024
1 parent 55f43e6 commit 314a514
Show file tree
Hide file tree
Showing 15 changed files with 410 additions and 105 deletions.
4 changes: 3 additions & 1 deletion src/components/cards/RelatedCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export function RelatedCard({ title, authors, pathUrl, refetchQueries }: CardTyp
const handleEnterKey = useHandleEnterKey(pathUrl);
const borderCard = useColorModeValue('gray.200', 'gray.600');
const colorAuthorCard = useColorModeValue('gray.600', 'gray.300');
const bgRandomBookCardLink = useColorModeValue('gray.300', 'black');
const colorLink = useColorModeValue('green.900', 'green.50');
const bgRandomBookCardLink = useColorModeValue('green.50', 'green.900');
const outlineCard = useColorModeValue('black', 'white');

return (
Expand Down Expand Up @@ -65,6 +66,7 @@ export function RelatedCard({ title, authors, pathUrl, refetchQueries }: CardTyp
bg={bgRandomBookCardLink}
py='4'
px='7'
color={colorLink}
position={{ base: 'initial', md: 'absolute' }}
bottom='0'
tabIndex={-1}
Expand Down
179 changes: 179 additions & 0 deletions src/components/modals/ModalCollectionSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import React, { useEffect, useState } from 'react';
import {
Box,
Button,
Checkbox,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Spinner,
useColorModeValue,
VStack,
} from '@chakra-ui/react';
import { FaCheckCircle, FaExclamationCircle } from 'react-icons/fa';

import { ModalCollectionSelectorType } from '@components/types';
import { useCollectionBooks } from '@hooks/queries';
import { useMyToast } from '@hooks/useMyToast';

export function ModalCollectionSelector({
userId,
bookId,
data,
isPending,
isOpen,
onClose,
}: ModalCollectionSelectorType) {
const [selectedCollections, setSelectedCollections] = useState<{
[key: string]: boolean;
}>({});

const bgColorBox = useColorModeValue('white', 'gray.900');
const myToast = useMyToast();
const { mutate, isPending: isPendingMutate } = useCollectionBooks();

useEffect(() => {
// Inicializa el estado de selección basado en la propiedad 'checked' de los datos
const initialSelectedState =
data?.reduce(
(acc, collection) => {
acc[collection.id] = collection.checked || false;
return acc;
},
{} as { [key: string]: boolean },
) || {};

setSelectedCollections(initialSelectedState);
}, [bookId, data]);

function handleCheckboxChange(collectionId: string) {
setSelectedCollections((prev) => {
const newSelectedState = {
...prev,
[collectionId]: !prev[collectionId],
};
return newSelectedState;
});
}

function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();

if (!isPendingMutate) {
// Preparar los datos para el mutate
const collections = data?.map((collection) => ({
collectionId: collection.id,
collectionName: collection.name,
isInCollection: selectedCollections[collection.id] || false,
}));

const mutateData = {
userId,
collections,
bookId,
checked: true,
};

mutate(mutateData, {
onSuccess: () => {
// Obtener nombres de colecciones seleccionadas
const selectedCollectionNames = data
?.filter((collection) => selectedCollections[collection.id])
.map((collection) => collection.name)
.join(', ');

myToast({
title: `Se agregó a colección(es): ${selectedCollectionNames}`,
icon: FaCheckCircle,
iconColor: 'green.700',
bgColor: 'black',
width: '300px',
color: 'whitesmoke',
align: 'center',
padding: '1',
fntSize: 'md',
bxSize: 5,
});

onClose();
},
onError: () => {
myToast({
title: 'Error al actualizar colecciones',
description: 'Ocurrió un problema al modificar las colecciones',
icon: FaExclamationCircle,
iconColor: 'red.700',
bgColor: 'black',
width: '300px',
color: 'whitesmoke',
align: 'center',
padding: '1',
fntSize: 'md',
bxSize: 5,
});
},
});
}
}

return (
<>
<Modal
isOpen={isOpen}
onClose={onClose}
size={{ base: 'xs', sm: 'md' }}
isCentered
>
<ModalOverlay backdropFilter='blur(7px)' />
<ModalContent overflow='hidden'>
<ModalHeader bg={bgColorBox}>Agregar a colección</ModalHeader>
<ModalCloseButton />
<ModalBody bg={bgColorBox} p='5'>
<Box as='form' onSubmit={handleSubmit}>
<VStack spacing='4' align='stretch' mb='7'>
{isPending ? (
<Box m='auto' py='5'>
<Spinner size='lg' />
</Box>
) : (
data?.map((collection) => (
<Checkbox
key={collection.id}
isChecked={selectedCollections[collection.id] || false}
onChange={() => handleCheckboxChange(collection.id)}
colorScheme='green'
>
{collection.name}
</Checkbox>
))
)}
</VStack>
<Button
w='full'
type='submit'
size='lg'
bg='green.500'
color='black'
p='5'
fontWeight='normal'
border='1px'
rounded='lg'
isDisabled={
Object.values(selectedCollections).filter(Boolean).length === 0 ||
isPendingMutate
}
isLoading={isPendingMutate}
_hover={{ outline: 'none', bg: 'green.600' }}
>
Confirmar
</Button>
</Box>
</ModalBody>
</ModalContent>
</Modal>
</>
);
}
7 changes: 6 additions & 1 deletion src/components/modals/ModalOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ export function ModalOptions({

return (
<>
<Modal isOpen={isOpen} onClose={onClose} size='xs'>
<Modal
isOpen={isOpen}
onClose={onClose}
size={{ base: 'xs', md: 'sm' }}
isCentered
>
<ModalOverlay backdropFilter='blur(7px)' />
<ModalContent>
<ModalBody py='5'>
Expand Down
39 changes: 17 additions & 22 deletions src/components/nav/DesktopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,28 +100,23 @@ export function DesktopNav() {
</Box>
<Box>
<List display='flex' alignItems='center'>
{navLink
.filter(({ name }) => {
if (name === 'Mis favoritos' && !data) return false;
return true;
})
.map(({ name, href }) => (
<ListItem key={name}>
<Link
as={NavLink}
to={href as string}
ml='7'
fontWeight='medium'
_activeLink={{
borderBottom: '2px',
borderColor: 'green.500',
}}
_hover={{ color: 'green.500' }}
>
{name}
</Link>
</ListItem>
))}
{navLink.map(({ name, href }) => (
<ListItem key={name}>
<Link
as={NavLink}
to={href as string}
ml='7'
fontWeight='medium'
_activeLink={{
borderBottom: '2px',
borderColor: 'green.500',
}}
_hover={{ color: 'green.500' }}
>
{name}
</Link>
</ListItem>
))}
</List>
</Box>
</Flex>
Expand Down
49 changes: 22 additions & 27 deletions src/components/nav/MobileNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,33 +197,28 @@ export function MobileNav() {
<DrawerCloseButton />
<DrawerBody>
<List mt='10'>
{navLink
.filter(({ name }) => {
if (name === 'Mis favoritos' && !data) return false;
return true;
})
.map(({ icon, name, href }) => (
<ListItem key={name} my='2'>
<Link
display='flex'
alignItems='center'
onClick={onCloseMenu}
as={NavLink}
to={href as string}
p='3'
rounded='xl'
fontWeight='medium'
_hover={{
bg: 'gray.700',
border: 'none',
color: 'green.500',
}}
>
<Icon as={icon} boxSize='5' mr='5' />
{name}
</Link>
</ListItem>
))}
{navLink.map(({ icon, name, href }) => (
<ListItem key={name} my='2'>
<Link
display='flex'
alignItems='center'
onClick={onCloseMenu}
as={NavLink}
to={href as string}
p='3'
rounded='xl'
fontWeight='medium'
_hover={{
bg: 'gray.700',
border: 'none',
color: 'green.500',
}}
>
<Icon as={icon} boxSize='5' mr='5' />
{name}
</Link>
</ListItem>
))}
</List>
</DrawerBody>
<DrawerFooter
Expand Down
11 changes: 9 additions & 2 deletions src/components/nav/menu/MenuProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,21 @@ export function MenuProfile({ displayName, photoURL, username }: MenuType) {
Perfil
</MenuItem>
<MenuItem as={NavLink} to='/new-post' _hover={{ textDecoration: 'none' }}>
Crear Publicación
Publicar
</MenuItem>
<MenuItem
as={NavLink}
to='/my-collections'
_hover={{ textDecoration: 'none' }}
>
Mis Colecciones
Mis colecciones
</MenuItem>
<MenuItem
as={NavLink}
to='/my-favorites'
_hover={{ textDecoration: 'none' }}
>
Mis favoritos
</MenuItem>
<MenuItem
as={NavLink}
Expand Down
1 change: 1 addition & 0 deletions src/components/skeletons/SkeletonDBook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export function SkeletonDetailsBook() {
<Flex gap='2'>
<Skeleton w='40px' h='30px' rounded='lg'></Skeleton>
<Skeleton w='40px' h='30px' rounded='lg'></Skeleton>
<Skeleton w='40px' h='30px' rounded='lg'></Skeleton>
</Flex>
</Flex>
<Flex
Expand Down
14 changes: 14 additions & 0 deletions src/components/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,19 @@ interface AuthProviderType {
children: RNode;
}

interface Collection {
id: string;
name: string;
checked?: boolean;
}

interface ModalCollectionSelectorType extends DisclosureType {
userId: string | undefined;
bookId: string;
data: Collection[];
isPending: boolean;
}

export type {
MyChangeEvent,
AboutType,
Expand Down Expand Up @@ -223,4 +236,5 @@ export type {
AuthContextType,
AuthProviderType,
ModalOptionsAndConfirType,
ModalCollectionSelectorType,
};
7 changes: 1 addition & 6 deletions src/constant/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { GrHome } from 'react-icons/gr';
import { MdOutlineExplore, MdOutlineFavoriteBorder } from 'react-icons/md';
import { MdOutlineExplore } from 'react-icons/md';
import { ImEyePlus } from 'react-icons/im';
import { RiAccountCircleLine, RiLoginCircleLine } from 'react-icons/ri';

Expand All @@ -9,11 +9,6 @@ const navLink: Array<LinkType> = [
{ name: 'Inicio', href: '/', icon: GrHome },
{ name: 'Explorar', href: 'explore', icon: MdOutlineExplore },
{ name: 'Más vistos', href: 'most-viewed', icon: ImEyePlus },
{
name: 'Mis favoritos',
href: 'my-favorites',
icon: MdOutlineFavoriteBorder,
},
];

const accountLinks: Array<LinkType> = [
Expand Down
Loading

0 comments on commit 314a514

Please sign in to comment.