Skip to content

Commit

Permalink
feat: preview to recently added cards
Browse files Browse the repository at this point in the history
  • Loading branch information
sayinmehmet47 committed Nov 26, 2023
1 parent 181c39c commit 5e94f2c
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 55 deletions.
86 changes: 86 additions & 0 deletions client/src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react';

import { cn } from '../../lib/utils';

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
className
)}
{...props}
/>
));
Card.displayName = 'Card';

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('flex flex-col space-y-1.5 p-6', className)}
{...props}
/>
));
CardHeader.displayName = 'CardHeader';

const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
'text-2xl font-semibold leading-none tracking-tight',
className
)}
{...props}
/>
));
CardTitle.displayName = 'CardTitle';

const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
));
CardDescription.displayName = 'CardDescription';

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
));
CardContent.displayName = 'CardContent';

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn('flex items-center p-6 pt-0', className)}
{...props}
/>
));
CardFooter.displayName = 'CardFooter';

export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
CardContent,
};
174 changes: 119 additions & 55 deletions client/src/pages/RecentlyAdded.tsx
Original file line number Diff line number Diff line change
@@ -1,71 +1,135 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
Button,
Card,
CardBody,
CardFooter,
CardImg,
CardText,
CardTitle,
Row,
} from 'reactstrap';
import Loading from '../components/Loading';
import styled from 'styled-components';
import Layout from '../components/Layout';
import { useFetchRecentlyAddedQuery } from '../redux/services/book.api';
import {
useDeleteBookMutation,
useFetchRecentlyAddedQuery,
} from '../redux/services/book.api';
import { Card, CardTitle } from '../components/ui/card';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '../components/ui/dropdown-menu';
import { DropdownMenuLabel } from '../components/ui/dropdown-menu';
import { DropdownMenuItem } from '../components/ui/dropdown-menu';
import { DropdownMenuSeparator } from '../components/ui/dropdown-menu';
import { DownloadIcon, Eye, MoreHorizontal } from 'lucide-react';
import { Button } from '../components/ui/button';
import { AiOutlineDelete } from 'react-icons/ai';
import { useSelector } from 'react-redux';
import { RootState } from '@/redux/store';
import { toast } from 'sonner';
import { Link } from 'react-router-dom';

const RecentlyAdded = () => {
const { data: recentlyAddedBooks, isLoading } = useFetchRecentlyAddedQuery();
const { user, isLoggedIn } = useSelector(
(state: RootState) => state.authSlice
);
const [deleteBook, { isSuccess, isError }] = useDeleteBookMutation();

const Container = styled.div`
margin-top: 55px;
display: flex;
flex-direction: row;
align-items: center;
`;
const downloadBook = async (url: string | undefined, name: string) => {
if (!url) {
console.error('URL is undefined');
return;
}

type Props = {};
const response = await fetch(url);
const data = await response.blob();
const blobUrl = window.URL.createObjectURL(data);

const RecentlyAdded = (props: Props) => {
const { data: recentlyAddedBooks, isLoading } = useFetchRecentlyAddedQuery();
console.log('test');
const a = document.createElement('a');
a.href = blobUrl;
a.download = name;
document.body.appendChild(a);
a.click();
a.remove();
};

const isAdmin = isLoggedIn && user.user.isAdmin;
const handleDelete = async ({ id }: { id: string }) => {
deleteBook({ id }).catch((err) => {
isError && toast.error('Something went wrong');
});

isSuccess && toast.success('Book deleted successfully');
};

if (isLoading || !recentlyAddedBooks) {
return <Loading />;
}

return (
<Layout>
<Container>
<Row lg={5} md={3} sm={3} className="d-flex justify-content-center">
{recentlyAddedBooks?.map((book) => (
<Card className="m-2" key={book._id}>
<div className="w-50 d-flex justify-center mx-auto mt-3">
<CardImg
alt="Card image cap"
src={
book.url?.includes('pdf')
? book.url?.replace('pdf', 'jpg')
: 'https://images.pexels.com/photos/8594539/pexels-photo-8594539.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2'
}
top
/>
</div>
<CardBody>
<CardTitle tag="h5">{book.name}</CardTitle>
<CardText>{book.size}</CardText>
</CardBody>
<CardFooter>
{book.url && (
<Button color="primary">
<Link to={book?.url} className="text-white">
Download
</Link>
<div className="mt-5 grid xl:grid-cols-6 lg:grid-cols-4 gap-12 m-4 md:grid-cols-3 sm:grid-cols-2">
{recentlyAddedBooks?.map((book) => (
<Card className="h-full w-full p-12 pb-20 relative" key={book._id}>
<img
src={
book.url?.includes('pdf')
? book.url?.replace('pdf', 'jpg')
: 'https://images.pexels.com/photos/8594539/pexels-photo-8594539.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2'
}
className="h-3/4 w-full rounded-t-lg"
alt="Book Cover"
/>

<CardTitle
title={book.name}
className="text-left ps-2 text-lg mt-2 text-gray-800"
>
{book.name}
</CardTitle>
<div
data-testid="book-options"
className="flex justify-between items-center mt-4 absolute -top-3 right-2"
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
)}
</CardFooter>
</Card>
))}
</Row>
</Container>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem className="cursor-pointer">
<Link
to={`/book/${book._id}`}
className="flex items-center"
>
<Eye className="h-4 w-4 mr-2 " />
Preview
</Link>
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onClick={() => downloadBook(book.url, book.name)}
>
<DownloadIcon className="h-4 w-4 mr-2 " />
Download Book
</DropdownMenuItem>
<DropdownMenuSeparator />
{isAdmin && (
<DropdownMenuItem
onClick={() =>
handleDelete({
id: book._id,
})
}
>
<AiOutlineDelete className="h-4 w-4 mr-2 text-red-500" />
<span className="cursor-pointer text-red-500">
Delete Book
</span>
</DropdownMenuItem>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
</Card>
))}
</div>
</Layout>
);
};
Expand Down

1 comment on commit 5e94f2c

@vercel
Copy link

@vercel vercel bot commented on 5e94f2c Nov 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.