From d02a07541bd25b5f433c51d9caaf3f4f47dab71d Mon Sep 17 00:00:00 2001 From: Yehoshua Sandler Date: Wed, 16 Apr 2025 10:35:47 -0500 Subject: [PATCH] feat: added book list preview for home page --- src/app/(frontend)/layout.tsx | 3 +- src/app/(frontend)/page.tsx | 19 +++- src/app/(frontend)/styles.css | 164 ----------------------------- src/app/globals.css | 5 + src/app/layout.tsx | 17 +++ src/collections/Authors/Authors.ts | 1 - src/collections/Books/Books.ts | 9 ++ src/components/BookList/index.tsx | 84 +++++++++++++++ src/globals/header/config.ts | 5 + src/payload-types.ts | 6 ++ 10 files changed, 145 insertions(+), 168 deletions(-) delete mode 100644 src/app/(frontend)/styles.css create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/components/BookList/index.tsx diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index e7681f7..b99896f 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -1,5 +1,4 @@ import React from 'react' -import './styles.css' export const metadata = { description: 'A blank template using Payload in a Next.js app.', @@ -12,7 +11,7 @@ export default async function RootLayout(props: { children: React.ReactNode }) { return ( -
{children}
+
{children}
) diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx index d0e8d64..46c9536 100644 --- a/src/app/(frontend)/page.tsx +++ b/src/app/(frontend)/page.tsx @@ -5,7 +5,8 @@ import React from 'react' import { fileURLToPath } from 'url' import config from '@/payload.config' -import './styles.css' +import BookList from '@/components/BookList' +import { Book } from '@/payload-types' export default async function HomePage() { const headers = await getHeaders() @@ -15,6 +16,21 @@ export default async function HomePage() { const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}` + const books = await payload.find({ + collection: 'books', + depth: 2, + limit: 12, + overrideAccess: false, + select: { + title: true, + authors: true, + publication: true, + lcc: true, + genre: true, + isbn: true, + }, + }) + return (
@@ -48,6 +64,7 @@ export default async function HomePage() {
+

Update this page by editing

diff --git a/src/app/(frontend)/styles.css b/src/app/(frontend)/styles.css deleted file mode 100644 index d1fb941..0000000 --- a/src/app/(frontend)/styles.css +++ /dev/null @@ -1,164 +0,0 @@ -:root { - --font-mono: 'Roboto Mono', monospace; -} - -* { - box-sizing: border-box; -} - -html { - font-size: 18px; - line-height: 32px; - - background: rgb(0, 0, 0); - -webkit-font-smoothing: antialiased; -} - -html, -body, -#app { - height: 100%; -} - -body { - font-family: system-ui; - font-size: 18px; - line-height: 32px; - - margin: 0; - color: rgb(1000, 1000, 1000); - - @media (max-width: 1024px) { - font-size: 15px; - line-height: 24px; - } -} - -img { - max-width: 100%; - height: auto; - display: block; -} - -h1 { - margin: 40px 0; - font-size: 64px; - line-height: 70px; - font-weight: bold; - - @media (max-width: 1024px) { - margin: 24px 0; - font-size: 42px; - line-height: 42px; - } - - @media (max-width: 768px) { - font-size: 38px; - line-height: 38px; - } - - @media (max-width: 400px) { - font-size: 32px; - line-height: 32px; - } -} - -p { - margin: 24px 0; - - @media (max-width: 1024px) { - margin: calc(var(--base) * 0.75) 0; - } -} - -a { - color: currentColor; - - &:focus { - opacity: 0.8; - outline: none; - } - - &:active { - opacity: 0.7; - outline: none; - } -} - -svg { - vertical-align: middle; -} - -.home { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - height: 100vh; - padding: 45px; - max-width: 1024px; - margin: 0 auto; - overflow: hidden; - - @media (max-width: 400px) { - padding: 24px; - } - - .content { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - flex-grow: 1; - - h1 { - text-align: center; - } - } - - .links { - display: flex; - align-items: center; - gap: 12px; - - a { - text-decoration: none; - padding: 0.25rem 0.5rem; - border-radius: 4px; - } - - .admin { - color: rgb(0, 0, 0); - background: rgb(1000, 1000, 1000); - border: 1px solid rgb(0, 0, 0); - } - - .docs { - color: rgb(1000, 1000, 1000); - background: rgb(0, 0, 0); - border: 1px solid rgb(1000, 1000, 1000); - } - } - - .footer { - display: flex; - align-items: center; - gap: 8px; - - @media (max-width: 1024px) { - flex-direction: column; - gap: 6px; - } - - p { - margin: 0; - } - - .codeLink { - text-decoration: none; - padding: 0 0.5rem; - background: rgb(60, 60, 60); - border-radius: 4px; - } - } -} diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..599ab3d --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,5 @@ +@import "tailwindcss"; + +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..fb015b7 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,17 @@ +import { ReactNode } from 'react' + +type LayoutProps = { + children: ReactNode +} + +import './globals.css' + +const Layout = ({ children }: LayoutProps) => { + return ( + + {children} + + ) +} + +export default Layout diff --git a/src/collections/Authors/Authors.ts b/src/collections/Authors/Authors.ts index f9fdedf..bec6ec6 100644 --- a/src/collections/Authors/Authors.ts +++ b/src/collections/Authors/Authors.ts @@ -1,4 +1,3 @@ -import { access } from 'node:fs/promises' import type { CollectionConfig } from 'payload' export const Authors: CollectionConfig = { diff --git a/src/collections/Books/Books.ts b/src/collections/Books/Books.ts index 33cc743..b578547 100644 --- a/src/collections/Books/Books.ts +++ b/src/collections/Books/Books.ts @@ -40,6 +40,14 @@ export const Books: CollectionConfig = { name: 'lcc', type: 'text', }, + { + name: 'isbn', + type: 'text', + }, + { + name: 'asin', + type: 'text', + }, { name: 'publication', type: 'text', @@ -54,6 +62,7 @@ export const Books: CollectionConfig = { type: 'relationship', relationTo: 'genre', hasMany: true, + maxDepth: 3, admin: { allowEdit: true, allowCreate: true diff --git a/src/components/BookList/index.tsx b/src/components/BookList/index.tsx new file mode 100644 index 0000000..786a716 --- /dev/null +++ b/src/components/BookList/index.tsx @@ -0,0 +1,84 @@ +import { Badge } from '@/components/badge' +import { Author, Book, Genre } from '@/payload-types' +import { Avatar } from '../avatar' + +type Props = { + books: Book[] +} + +const makeAuthorsLabel = (book: Book) => { + const authors = book.authors as Author[] // TODO: endure this type safety + + const translators = authors?.filter((a) => a.role === 'Translator').map((t) => t.lf) + const editors = authors?.filter((a) => a.role === 'Editor').map((e) => e.lf) + const actualAuthors = authors + ?.filter((a) => !editors.includes(a.lf) && !translators.includes(a.lf)) + .map((a) => a.lf) + + return ( +
    +
  • {actualAuthors.join(', ')}
  • + {!!translators?.length &&
  • Translators: {translators.join(', ')}
  • } + {!!editors?.length &&
  • Editors: {editors.join(', ')}
  • } +
+ ) +} + +const makeGenreBadges = (book: Book) => { + return (book.genre as Genre[])?.map((g) => ( + + {g.name} + + )) +} + +export default function BookList(props: Props) { + const { books } = props + + return ( +
+
    + {books.map((b) => ( +
  • +
    + +
    +

    + {b.title} + {b.lcc} +

    +

    {makeGenreBadges(b)}

    +
    +
    +
    +

    {makeAuthorsLabel(b)}

    + { + /*b.lastBorrowed*/ false ? ( +

    + Last Borrowed{' '} + +

    + ) : ( +
    +
    +
    +
    +

    Available

    +
    + ) + } +
    +
  • + ))} +
+
+ ) +} diff --git a/src/globals/header/config.ts b/src/globals/header/config.ts index 594c24e..6afa6bd 100644 --- a/src/globals/header/config.ts +++ b/src/globals/header/config.ts @@ -5,6 +5,11 @@ export const Header: GlobalConfig = { slug: 'header', label: 'Header Nav', fields: [ + { + name: 'logo', + type: 'relationship', + relationTo: 'media', + }, { name: 'headerLinks', type: 'array', diff --git a/src/payload-types.ts b/src/payload-types.ts index 9593b9d..e7931f5 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -181,6 +181,8 @@ export interface Book { authors?: (number | Author)[] | null; pages?: number | null; lcc?: string | null; + isbn?: string | null; + asin?: string | null; publication?: string | null; date?: string | null; genre?: (number | Genre)[] | null; @@ -420,6 +422,8 @@ export interface BooksSelect { authors?: T; pages?: T; lcc?: T; + isbn?: T; + asin?: T; publication?: T; date?: T; genre?: T; @@ -535,6 +539,7 @@ export interface PayloadMigrationsSelect { */ export interface Header { id: number; + logo?: (number | null) | Media; headerLinks?: | { label?: string | null; @@ -551,6 +556,7 @@ export interface Header { * via the `definition` "header_select". */ export interface HeaderSelect { + logo?: T; headerLinks?: | T | {