feat: add comic list to homepage
This commit is contained in:
parent
7e8d2da41b
commit
d0d070e867
3 changed files with 124 additions and 19 deletions
12
components/CompletionBar.tsx
Normal file
12
components/CompletionBar.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
interface Props {
|
||||
total: number;
|
||||
completed: number;
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
export default function CompletionBar(props: Props) {
|
||||
const progress = (props.completed / props.total) * 100;
|
||||
return <span className={`w-full h-2 bg-red-400 ${props.inline ? 'inline-block' : 'block'} relative overflow-hidden rounded-full`} role="progressbar" aria-valuenow={props.completed} aria-valuemin={0} aria-valuemax={props.total}>
|
||||
<span className='w-full h-2 absolute top-0 left-0 block bg-green-400' style={{ transform: `translateX(-${100 - progress}%)`, transformOrigin: 'left center' }} />
|
||||
</span>
|
||||
}
|
|
@ -7,21 +7,32 @@ export default function NavBar() {
|
|||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<nav className='flex flex-row h-12 bg-gray-900 justify-end items-center p-4 gap-1 shadow mb-4'>
|
||||
{status === 'loading' && <>...</>}
|
||||
{status === 'unauthenticated' && (
|
||||
<LinkButton
|
||||
href={`/login?return=${encodeURIComponent(router.asPath)}`}
|
||||
>
|
||||
Log in
|
||||
</LinkButton>
|
||||
)}
|
||||
{status === 'authenticated' && (
|
||||
<>
|
||||
<span>{data?.user?.name}</span>
|
||||
<button onClick={() => signOut()}>(Log out)</button>
|
||||
</>
|
||||
)}
|
||||
<nav>
|
||||
<ul className='flex flex-row h-12 bg-gray-900 justify-start items-center p-4 gap-1 shadow mb-4'>
|
||||
<li>
|
||||
<LinkButton href='/'>Comicbox</LinkButton>
|
||||
</li>
|
||||
|
||||
<li className='flex-1' aria-hidden />
|
||||
|
||||
<li>
|
||||
{status === 'loading' && <>...</>}
|
||||
{status === 'unauthenticated' && (
|
||||
<LinkButton
|
||||
href={`/login?return=${encodeURIComponent(router.asPath)}`}
|
||||
>
|
||||
Log in
|
||||
</LinkButton>
|
||||
)}
|
||||
{status === 'authenticated' && (
|
||||
<>
|
||||
<span>{data?.user?.name}</span>
|
||||
<button onClick={() => signOut()}>(Log out)</button>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,93 @@
|
|||
import type { NextPage } from 'next';
|
||||
import { Comic } from '@prisma/client';
|
||||
import type { GetServerSideProps, NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { LinkButton } from '../components/Button';
|
||||
import CompletionBar from '../components/CompletionBar';
|
||||
import { prisma } from '../src/db';
|
||||
|
||||
const Home: NextPage = () => {
|
||||
interface Props {
|
||||
comics: {
|
||||
comic: Comic;
|
||||
completion: {
|
||||
completePages: number;
|
||||
totalPages: number;
|
||||
};
|
||||
}[];
|
||||
}
|
||||
|
||||
const Home: NextPage<Props> = (props) => {
|
||||
return (
|
||||
<main>
|
||||
<h1 className='text-4xl'>Hello, World!</h1>
|
||||
<main className='p-4'>
|
||||
<Head>
|
||||
<title>Comicbox</title>
|
||||
</Head>
|
||||
|
||||
<table className='border-separate border-spacing-x-2'>
|
||||
<thead>
|
||||
<tr className='text-left'>
|
||||
<th>Comic</th>
|
||||
<th>Completion</th>
|
||||
<th>Links</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.comics.map(({ comic, completion }) => {
|
||||
return <tr key={comic.id}>
|
||||
<td>
|
||||
<LinkButton href={`/comic/${comic.slug}`}>
|
||||
{comic.title}
|
||||
</LinkButton>
|
||||
</td>
|
||||
<td className='w-96 flex flex-row items-center'>
|
||||
<CompletionBar inline total={completion.totalPages} completed={completion.completePages} />
|
||||
<span className='whitespace-nowrap pl-2'>
|
||||
({completion.completePages}/{completion.totalPages} pages)
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<LinkButton href={comic.url} target='_blank'>
|
||||
{new URL(comic.url).host}
|
||||
</LinkButton>
|
||||
</td>
|
||||
</tr>
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<Props> = async () => {
|
||||
const comics = await prisma.comic.findMany();
|
||||
const comicsWithCompletions = await Promise.all(comics.map(async (comic) => {
|
||||
const pages = await prisma.comicPage.findMany({
|
||||
where: {
|
||||
comicId: comic.id,
|
||||
},
|
||||
select: {
|
||||
_count: {
|
||||
select: {
|
||||
bubbles: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
comic,
|
||||
completion: {
|
||||
totalPages: pages.length,
|
||||
completePages: pages.filter((page) => page._count.bubbles !== 0)
|
||||
.length,
|
||||
},
|
||||
}
|
||||
}));
|
||||
|
||||
return {
|
||||
props: {
|
||||
comics: comicsWithCompletions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue