-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
405 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
next.config.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
{ | ||
"upgrade": true, | ||
"reject": [ | ||
"vitest" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import Image from 'next/image' | ||
import { notFound } from 'next/navigation' | ||
|
||
import BookBreadcrumbs from '@/components/book-breadcrubs/book-breadcrubs' | ||
import BookResponse from '@/core/book/dto/responses/book.response' | ||
import { findBook } from '@/core/book/infrastructure/actions/find-book' | ||
|
||
interface PageParameters { | ||
id: string | ||
} | ||
|
||
export default async function Page({ params }: { params: PageParameters }) { | ||
const book = await findBook(params.id) | ||
|
||
if (!book) { | ||
return notFound() | ||
} | ||
|
||
return ( | ||
<main className="flex flex-col gap-4"> | ||
<BookBreadcrumbs title={book.title} /> | ||
<div className="flex gap-4"> | ||
<Image | ||
className="h-[297px] w-[320px] object-scale-down" | ||
alt={book.title} | ||
width={297} | ||
height={387} | ||
src={book.image} | ||
/> | ||
<div className="flex flex-col"> | ||
<div className="font-bold text-4xl text-default-800"> | ||
{book.title} | ||
</div> | ||
<div className="line-clamp-1 text-xl flex-grow"> | ||
{book.authors.join(', ')} | ||
</div> | ||
<LoanBy book={book} /> | ||
</div> | ||
</div> | ||
</main> | ||
) | ||
} | ||
|
||
function LoanBy({ book }: { book: BookResponse }) { | ||
if (!book.loan) { | ||
return null | ||
} | ||
|
||
return <div>Este libro se encuentra prestado a {book.loan.user.name}</div> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
import BookGrid from '@/components/book-grid' | ||
import { findBooks } from '@/core/book/infrastructure/actions/find-books' | ||
import me from '@/core/user/infrastructure/actions/me' | ||
|
||
export default async function Home() { | ||
const books = await findBooks() | ||
const user = await me() | ||
|
||
return ( | ||
<main> | ||
<BookGrid books={books} /> | ||
<BookGrid books={books} me={user} /> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use client' | ||
|
||
import { BreadcrumbItem, Breadcrumbs } from '@nextui-org/react' | ||
import { useRouter } from 'next/navigation' | ||
|
||
interface BookBreadcrumbsProperties { | ||
title: string | ||
} | ||
|
||
export default function BookBreadcrumbs(properties: BookBreadcrumbsProperties) { | ||
const router = useRouter() | ||
router.prefetch('/') | ||
|
||
const { title } = properties | ||
|
||
return ( | ||
<> | ||
<Breadcrumbs> | ||
<BreadcrumbItem onClick={() => router.push('/')}>Inicio</BreadcrumbItem> | ||
<BreadcrumbItem>{title}</BreadcrumbItem> | ||
</Breadcrumbs> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
'use client' | ||
|
||
import { BookmarkIcon, BookmarkSlashIcon } from '@heroicons/react/24/solid' | ||
import { useEffect } from 'react' | ||
import { useFormState } from 'react-dom' | ||
|
||
import SubmitButton from '@/components/submit-button' | ||
import { showToast } from '@/components/toast' | ||
import BookResponse from '@/core/book/dto/responses/book.response' | ||
import { loanBook } from '@/core/loan/infrastructure/actions/loan-book' | ||
import { returnBook } from '@/core/loan/infrastructure/actions/return-book' | ||
import UserResponse from '@/core/user/dto/responses/user.response' | ||
import FormResponse from '@/lib/zod/form-response' | ||
|
||
interface BookCardFormProperties { | ||
book: BookResponse | ||
me?: UserResponse | ||
} | ||
|
||
function useController(properties: BookCardFormProperties) { | ||
const { book, me } = properties | ||
|
||
const isLoaned = !!book.loan | ||
const isLogged = !!me | ||
const isOwned = isLoaned && isLogged && book.loan.user.id === me.id | ||
const isActive = isLogged && (!isLoaned || isOwned) | ||
|
||
const currentAction = useFormState( | ||
isOwned ? returnBook : loanBook, | ||
FormResponse.initialState({ bookId: book.id }), | ||
) | ||
|
||
return { currentAction, isActive, isOwned, ...properties } | ||
} | ||
|
||
export default function BookCardForm(properties: BookCardFormProperties) { | ||
const { | ||
currentAction: [state, action], | ||
isActive, | ||
isOwned, | ||
} = useController(properties) | ||
|
||
useEffect(() => { | ||
if (state?.success) { | ||
showToast(state.message) | ||
} | ||
}, [state]) | ||
|
||
if (!isActive) { | ||
return null | ||
} | ||
|
||
return ( | ||
<> | ||
<form action={action}> | ||
<input type="hidden" name="bookId" value={state.data.bookId} /> | ||
{isOwned ? <ReturnButton /> : <LoanButton />} | ||
</form> | ||
</> | ||
) | ||
} | ||
|
||
function LoanButton() { | ||
return ( | ||
<> | ||
<SubmitButton | ||
isIconOnly | ||
className="opacity-0 group-hover:opacity-100 transition group-hover:duration-300 group-hover:-translate-y-2 text-center w-14 h-14 hover:scale-105 bg-gradient-to-tr from-pink-500 to-yellow-500 shadow-2xl absolute bottom-4 right-5 z-10" | ||
radius="full" | ||
variant="flat" | ||
> | ||
<BookmarkIcon className="h-8 w-8 m-auto fill-white" /> | ||
</SubmitButton> | ||
</> | ||
) | ||
} | ||
|
||
function ReturnButton() { | ||
return ( | ||
<> | ||
<SubmitButton | ||
isIconOnly | ||
className="opacity-0 group-hover:opacity-100 transition group-hover:duration-300 group-hover:-translate-y-2 text-center w-14 h-14 hover:scale-105 bg-gradient-to-tr from-pink-500 to-yellow-500 shadow-2xl absolute bottom-4 left-5 z-10" | ||
radius="full" | ||
variant="flat" | ||
> | ||
<BookmarkSlashIcon className="h-8 w-8 m-auto fill-white" /> | ||
</SubmitButton> | ||
</> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Card, CardBody } from '@nextui-org/react' | ||
import Image from 'next/image' | ||
import Link from 'next/link' | ||
|
||
import BookCardForm from '@/components/book-card/book-card-form' | ||
import BookResponse from '@/core/book/dto/responses/book.response' | ||
import UserResponse from '@/core/user/dto/responses/user.response' | ||
|
||
interface BookCardProperties { | ||
book: BookResponse | ||
me?: UserResponse | ||
} | ||
|
||
const CardColor = { | ||
Available: 'border-t-green-600', | ||
Loaned: 'border-t-red-600', | ||
Owned: 'border-t-zinc-600', | ||
} | ||
|
||
export default function BookCard(properties: BookCardProperties) { | ||
const { book, me } = properties | ||
|
||
const isLoaned = !!book.loan | ||
const isLogged = !!me | ||
const isOwned = isLoaned && isLogged && book.loan.user.id === me.id | ||
|
||
const cardColor = isOwned | ||
? CardColor.Owned | ||
: isLoaned | ||
? CardColor.Loaned | ||
: CardColor.Available | ||
|
||
return ( | ||
<> | ||
<Card | ||
className={`max-w-[320px] space-y-4 p-4 group border-t-4 ${cardColor}`} | ||
radius="none" | ||
isHoverable | ||
as={Link} | ||
prefetch | ||
href={`/books/${book.id}`} | ||
> | ||
<div className="relative"> | ||
<Image | ||
className="h-[297px] w-[320px] object-cover" | ||
alt={book.title} | ||
width={297} | ||
height={387} | ||
src={book.image} | ||
/> | ||
<BookAvatar book={book} /> | ||
<BookCardForm book={book} me={me} /> | ||
</div> | ||
|
||
<CardBody className="p-0"> | ||
<div className="line-clamp-1 font-bold hyphens-auto" lang="en"> | ||
{book.title} | ||
</div> | ||
<div className="line-clamp-1 font-extralight text-sm"> | ||
{book.authors.join(', ')} | ||
</div> | ||
</CardBody> | ||
</Card> | ||
</> | ||
) | ||
} | ||
|
||
function BookAvatar({ book }: { book: BookResponse }) { | ||
if (!book.loan) { | ||
return null | ||
} | ||
|
||
return ( | ||
<Image | ||
alt="Avatar del poseedor del libro" | ||
src={book.loan.user.image} | ||
height={48} | ||
width={48} | ||
className="absolute top-5 right-5 z-10 rounded-full" | ||
/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.