Skip to content

Commit

Permalink
Merge pull request #4 from aulasoftwarelibre/feat-read-reviews
Browse files Browse the repository at this point in the history
Add reviews and stats
  • Loading branch information
sgomez authored Apr 28, 2024
2 parents 0dfac8f + 4c8cd7a commit 9ab6a98
Show file tree
Hide file tree
Showing 32 changed files with 767 additions and 68 deletions.
4 changes: 2 additions & 2 deletions e2e/books/adding-book.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test.describe('Adding a book', () => {
)
await bookPage.submit()
// Assert
await expect(page).toHaveURL(/books\/[\dA-Z]{26}$/)
await expect(page).toHaveURL(/books\/[\dA-Z]{26}/)
await expect(page.getByLabel('Título')).toContainText('A new book')
})

Expand All @@ -30,7 +30,7 @@ test.describe('Adding a book', () => {
)
await bookPage.submit()
// Assert
await expect(page).toHaveURL(/books\/[\dA-Z]{26}$/)
await expect(page).toHaveURL(/books\/[\dA-Z]{26}/)
await expect(page.getByLabel('Autores')).toContainText(
'Jenny Doe, John Doe',
)
Expand Down
20 changes: 20 additions & 0 deletions prisma/migrations/20240418141956_book_rating/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "Review" (
"id" TEXT NOT NULL,
"version" INTEGER NOT NULL DEFAULT 0,
"bookId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
"score" INTEGER NOT NULL,

CONSTRAINT "Review_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Review_userId_bookId_key" ON "Review"("userId", "bookId");

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_bookId_fkey" FOREIGN KEY ("bookId") REFERENCES "Book"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Review" ADD CONSTRAINT "Review_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions prisma/migrations/20240418162425_book_rating/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Review" ADD COLUMN "description" TEXT NOT NULL DEFAULT '',
ADD COLUMN "title" TEXT NOT NULL DEFAULT '';
17 changes: 17 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ model User {
sessions Session[]
Loan Loan[]
LoanRegistry LoanRegistry[]
Review Review[]
}

model VerificationToken {
Expand All @@ -67,6 +68,7 @@ model Book {
state BookState @default(AVAILABLE)
loan Loan?
loanRegistry LoanRegistry[]
Review Review[]
}

model Loan {
Expand Down Expand Up @@ -96,3 +98,18 @@ enum BookState {
AVAILABLE
LOANED
}

model Review {
id String @id @default(cuid())
version Int @default(0)
book Book @relation(fields: [bookId], references: [id], onDelete: Cascade)
bookId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime @default(now()) @db.Timestamptz()
score Int
title String @default("")
description String @default("")
@@unique([userId, bookId])
}
55 changes: 55 additions & 0 deletions src/app/books/[id]/(view)/components/book-view-tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client'

import { Tab, Tabs } from '@nextui-org/tabs'
import NextLink from 'next/link'
import { usePathname } from 'next/navigation'
import { ReactNode } from 'react'

const TABS = [
{
target: 'reviews',
title: 'Reseñas',
},
{
target: 'loans',
title: 'Préstamos',
},
] as ViewTab[]

interface ViewTab {
target: string
title: string
}

interface ViewTabsProperties {
children: ReactNode
id: string
}

export function BookViewTabs({ children, id }: ViewTabsProperties) {
const pathname = usePathname()

return (
<>
<Tabs
aria-label="Opciones"
classNames={{
tab: 'max-w-fit',
tabList: 'w-full',
}}
items={TABS}
selectedKey={pathname}
>
{({ target, title }) => (
<Tab
as={NextLink}
href={`/books/${id}/${target}`}
key={`/books/${id}/${target}`}
title={title}
/>
)}
</Tabs>
<div className="mt-4">{children}</div>
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
'use client'

import { Button } from '@nextui-org/button'
import {
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
} from '@nextui-org/table'
import { format } from 'date-fns'
import { es } from 'date-fns/locale'
import Link from 'next/link'

import { BookViewBreadcrumbs } from '@/app/books/[id]/(view)/components/book-view-breadcrumbs'
import { BookCard } from '@/app/books/components/book-card'
import { BookViewBreadcrumbs } from '@/app/books/components/book-view-breadcrumbs'
import { BookResponse } from '@/core/book/dto/responses/book.response'
import { HistoricalLoansResponse } from '@/core/loan/dto/responses/historical-loans.response'
import { UserResponse } from '@/core/user/dto/responses/user.response'

interface BookPageProperties {
book: BookResponse
historicalLoans: HistoricalLoansResponse[]
user?: UserResponse
}

export function BookView(properties: BookPageProperties) {
const { book, historicalLoans, user } = properties
const { book, user } = properties
return (
<>
<main className="flex flex-col gap-4 px-4 md:px-0">
<BookViewBreadcrumbs title={book.title} />
<div className="flex flex-col gap-4 md:flex-row">
<BookCard book={book} me={user} />
<BookCard book={book} key={book.id} me={user} />
<div className="flex flex-col gap-4">
<h1
aria-label="Título"
Expand All @@ -58,23 +48,6 @@ export function BookView(properties: BookPageProperties) {
<LoanBy book={book} />
</div>
</div>
<h2 className="mt-4 text-3xl font-bold">Histórico de préstamos</h2>
<Table aria-label="Listado historico" isStriped>
<TableHeader>
<TableColumn>Usuario</TableColumn>
<TableColumn>Fecha de préstamo</TableColumn>
<TableColumn>Fecha de devolución</TableColumn>
</TableHeader>
<TableBody items={historicalLoans}>
{(historicalLoan) => (
<TableRow key={historicalLoan.id}>
<TableCell>{historicalLoan.user.name}</TableCell>
<TableCell>{shortDate(historicalLoan.startsAt)}</TableCell>
<TableCell>{shortDate(historicalLoan.finishedAt)}</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</main>
</>
)
Expand All @@ -98,9 +71,3 @@ function longDate(date: string | Date) {
locale: es,
})
}

function shortDate(date: string | Date) {
return format(new Date(date), 'dd/MM/yyyy', {
locale: es,
})
}
38 changes: 38 additions & 0 deletions src/app/books/[id]/(view)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Divider } from '@nextui-org/divider'
import { notFound } from 'next/navigation'
import { ReactNode } from 'react'

import { BookView } from '@/app/books/[id]/(view)/components/book-view'
import { BookViewTabs } from '@/app/books/[id]/(view)/components/book-view-tabs'
import { findBook } from '@/core/book/infrastructure/actions/find-book'
import { UserResponse } from '@/core/user/dto/responses/user.response'
import { me } from '@/core/user/infrastructure/actions/me'

interface PageParameters {
id: string
}

export default async function Page({
children,
params,
}: {
children: ReactNode
params: PageParameters
}) {
const book = await findBook(params.id)
const user = (await me()) as UserResponse

if (!book) {
return notFound()
}

return (
<>
<div className="flex flex-col gap-4">
<BookView book={book} user={user} />
<Divider />
<BookViewTabs id={params.id}>{children}</BookViewTabs>
</div>
</>
)
}
50 changes: 50 additions & 0 deletions src/app/books/[id]/(view)/loans/components/book-view-tab-loans.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client'

import {
Table,
TableBody,
TableCell,
TableColumn,
TableHeader,
TableRow,
} from '@nextui-org/table'
import { format } from 'date-fns'
import { es } from 'date-fns/locale'

import { HistoricalLoansResponse } from '@/core/loan/dto/responses/historical-loans.response'

interface BookViewTabLoansProperties {
historicalLoans: HistoricalLoansResponse[]
}

export function BookViewTabLoans({
historicalLoans,
}: BookViewTabLoansProperties) {
return (
<>
<h2 className="mt-4 text-3xl font-bold">Histórico de préstamos</h2>
<Table aria-label="Listado historico" isStriped>
<TableHeader>
<TableColumn>Usuario</TableColumn>
<TableColumn>Fecha de préstamo</TableColumn>
<TableColumn>Fecha de devolución</TableColumn>
</TableHeader>
<TableBody items={historicalLoans}>
{(historicalLoan) => (
<TableRow key={historicalLoan.id}>
<TableCell>{historicalLoan.user.name}</TableCell>
<TableCell>{shortDate(historicalLoan.startsAt)}</TableCell>
<TableCell>{shortDate(historicalLoan.finishedAt)}</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</>
)
}

function shortDate(date: string | Date) {
return format(new Date(date), 'dd/MM/yyyy', {
locale: es,
})
}
12 changes: 12 additions & 0 deletions src/app/books/[id]/(view)/loans/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { BookViewTabLoans } from '@/app/books/[id]/(view)/loans/components/book-view-tab-loans'
import { getHistoricalLoans } from '@/core/loan/infrastructure/actions/get-historical-loans'

interface PageParameters {
id: string
}

export default async function Page({ params }: { params: PageParameters }) {
const historicalLoans = await getHistoricalLoans(params.id)

return <BookViewTabLoans historicalLoans={historicalLoans} />
}
13 changes: 13 additions & 0 deletions src/app/books/[id]/(view)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { redirect } from 'next/navigation'

interface PageParameters {
id: string
}

export default async function Page({
params: { id },
}: {
params: PageParameters
}) {
return redirect(`/books/${id}/reviews`)
}
Loading

0 comments on commit 9ab6a98

Please sign in to comment.