chore: run lint and prettier
This commit is contained in:
parent
9e0ad448c2
commit
d85c59b21e
11 changed files with 221 additions and 143 deletions
|
@ -14,7 +14,9 @@ const COLOUR_MAP: { [P in Style]: string } = {
|
|||
};
|
||||
|
||||
export default function Banner(props: Props) {
|
||||
return <div className={'border-2 rounded-lg p-3 ' + (COLOUR_MAP[props.style])}>
|
||||
{props.children}
|
||||
</div>
|
||||
return (
|
||||
<div className={'border-2 rounded-lg p-3 ' + COLOUR_MAP[props.style]}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@ import {
|
|||
AnchorHTMLAttributes,
|
||||
ButtonHTMLAttributes,
|
||||
DetailedHTMLProps,
|
||||
RefAttributes,
|
||||
} from 'react';
|
||||
import Link, { LinkProps } from 'next/link';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface CustomProps {
|
||||
noDefaultColous?: boolean;
|
||||
|
@ -44,7 +43,10 @@ type LinkButtonProps = AnchorHTMLAttributes<HTMLAnchorElement>;
|
|||
export function LinkButton({ className, children, ...props }: LinkButtonProps) {
|
||||
return (
|
||||
<Link href={props.href || ''}>
|
||||
<a className={'text-blue-200 hover:underline ' + (className || '')} {...props}>
|
||||
<a
|
||||
className={'text-blue-200 hover:underline ' + (className || '')}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
|
|
|
@ -6,7 +6,23 @@ interface Props {
|
|||
|
||||
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>
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
import { LinkButton } from "./Button";
|
||||
import { LinkButton } from './Button';
|
||||
|
||||
export default function Footer() {
|
||||
return <footer className='w-full h-12 bg-gray-900 p-4 gap-1 shadow'>
|
||||
<ul className='flex flex-row items-center dots-between'>
|
||||
<li>
|
||||
Created by <LinkButton href='https://ashhhleyyy.dev'>Ashhhleyyy</LinkButton>.
|
||||
</li>
|
||||
return (
|
||||
<footer className='w-full h-12 bg-gray-900 p-4 gap-1 shadow'>
|
||||
<ul className='flex flex-row items-center dots-between'>
|
||||
<li>
|
||||
Created by{' '}
|
||||
<LinkButton href='https://ashhhleyyy.dev'>
|
||||
Ashhhleyyy
|
||||
</LinkButton>
|
||||
.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
Comics are Copyright (c) their original authors
|
||||
</li>
|
||||
<li>Comics are Copyright (c) their original authors</li>
|
||||
|
||||
<li>
|
||||
<LinkButton href='/about'>About</LinkButton>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
<li>
|
||||
<LinkButton href='/about'>About</LinkButton>
|
||||
</li>
|
||||
</ul>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,9 @@ export default function NavBar() {
|
|||
{status === 'loading' && <>...</>}
|
||||
{status === 'unauthenticated' && (
|
||||
<LinkButton
|
||||
href={`/login?return=${encodeURIComponent(router.asPath)}`}
|
||||
href={`/login?return=${encodeURIComponent(
|
||||
router.asPath
|
||||
)}`}
|
||||
>
|
||||
Log in
|
||||
</LinkButton>
|
||||
|
@ -32,7 +34,6 @@ export default function NavBar() {
|
|||
)}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,43 +1,53 @@
|
|||
import Head from "next/head";
|
||||
import Banner from "../components/Banner";
|
||||
import { LinkButton } from "../components/Button";
|
||||
import Head from 'next/head';
|
||||
import Banner from '../components/Banner';
|
||||
import { LinkButton } from '../components/Button';
|
||||
|
||||
export default function About() {
|
||||
return <main className='p-4 max-w-2xl'>
|
||||
<Head>
|
||||
<title>About Comicbox</title>
|
||||
</Head>
|
||||
return (
|
||||
<main className='p-4 max-w-2xl'>
|
||||
<Head>
|
||||
<title>About Comicbox</title>
|
||||
</Head>
|
||||
|
||||
<section>
|
||||
<h1 className='text-4xl'>About Comicbox</h1>
|
||||
<section>
|
||||
<h1 className='text-4xl'>About Comicbox</h1>
|
||||
|
||||
<p>
|
||||
Comicbox is an <LinkButton href='https://git.ashhhleyyy.dev/ash/comicbox'>open-source</LinkButton> website for
|
||||
crowdsourcing transcriptions for webcomics. Anyone can <LinkButton href='/login'>log in</LinkButton> and help
|
||||
transcribing pages.
|
||||
</p>
|
||||
</section>
|
||||
<p>
|
||||
Comicbox is an{' '}
|
||||
<LinkButton href='https://git.ashhhleyyy.dev/ash/comicbox'>
|
||||
open-source
|
||||
</LinkButton>{' '}
|
||||
website for crowdsourcing transcriptions for webcomics.
|
||||
Anyone can <LinkButton href='/login'>log in</LinkButton> and
|
||||
help transcribing pages.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 className='text-3xl'>How to help?</h2>
|
||||
<section>
|
||||
<h2 className='text-3xl'>How to help?</h2>
|
||||
|
||||
<p>
|
||||
You can <LinkButton href='/login'>log in</LinkButton> with Discord or GitHub right away to start, just pick
|
||||
a comic on the <LinkButton href='/'>homepage</LinkButton> and use the 'Random Page' links from there.
|
||||
<p>
|
||||
You can <LinkButton href='/login'>log in</LinkButton> with
|
||||
Discord or GitHub right away to start, just pick a comic on
|
||||
the <LinkButton href='/'>homepage</LinkButton> and use the
|
||||
'Random Page' links from there.
|
||||
<br />
|
||||
On the transcription page, you can draw boxes around each
|
||||
bubble on the page, and once you have checked the order,
|
||||
click 'Extract text' to attempt to automatically
|
||||
extract the text from each bubble. This may take a few
|
||||
seconds to complete, and once it has, then all the boxes
|
||||
will be filled with the text. You can then select these to
|
||||
edit the text and assign the correct character. Once you are
|
||||
done with a page, you can click save to submit your changes.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
On the transcription page, you can draw boxes around each bubble on the page, and once you have checked
|
||||
the order, click 'Extract text' to attempt to automatically extract the text from each bubble. This may
|
||||
take a few seconds to complete, and once it has, then all the boxes will be filled with the text. You can
|
||||
then select these to edit the text and assign the correct character. Once you are done with a page, you can
|
||||
click save to submit your changes.
|
||||
</p>
|
||||
|
||||
<Banner style="warning">
|
||||
I would recommend that you have fully read any comics you choose before starting, as otherwise you may
|
||||
see pages you haven't read yet.
|
||||
</Banner>
|
||||
</section>
|
||||
</main>
|
||||
<Banner style='warning'>
|
||||
I would recommend that you have fully read any comics you
|
||||
choose before starting, as otherwise you may see pages you
|
||||
haven't read yet.
|
||||
</Banner>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -37,40 +37,55 @@ export default function ComicPage({ comic, completion }: Props) {
|
|||
{completion.totalPages} pages have been transcribed!
|
||||
</p>
|
||||
<div className='max-w-md'>
|
||||
<CompletionBar total={completion.totalPages} completed={completion.completePages} />
|
||||
<CompletionBar
|
||||
total={completion.totalPages}
|
||||
completed={completion.completePages}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<nav>
|
||||
{status === 'authenticated' && <ul>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random`}
|
||||
>
|
||||
Random page
|
||||
</LinkButton>
|
||||
</li>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random?completed=true`}
|
||||
>
|
||||
Random page (complete)
|
||||
</LinkButton>
|
||||
</li>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random?completed=false`}
|
||||
>
|
||||
Random page (incomplete)
|
||||
</LinkButton>
|
||||
</li>
|
||||
</ul>}
|
||||
{status === 'authenticated' && (
|
||||
<ul>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random`}
|
||||
>
|
||||
Random page
|
||||
</LinkButton>
|
||||
</li>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random?completed=true`}
|
||||
>
|
||||
Random page (complete)
|
||||
</LinkButton>
|
||||
</li>
|
||||
<li>
|
||||
<LinkButton
|
||||
href={`/comic/${comic.slug}/transcribe/random?completed=false`}
|
||||
>
|
||||
Random page (incomplete)
|
||||
</LinkButton>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{status === 'loading' && <>Loading...</>}
|
||||
|
||||
{status === 'unauthenticated' && <>
|
||||
Please <LinkButton href={`/login?return=${encodeURIComponent(router.asPath)}`}>Log in</LinkButton> to contribute.
|
||||
</>}
|
||||
{status === 'unauthenticated' && (
|
||||
<>
|
||||
Please{' '}
|
||||
<LinkButton
|
||||
href={`/login?return=${encodeURIComponent(
|
||||
router.asPath
|
||||
)}`}
|
||||
>
|
||||
Log in
|
||||
</LinkButton>{' '}
|
||||
to contribute.
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</main>
|
||||
);
|
||||
|
|
|
@ -173,18 +173,25 @@ export default function TranscribePage(props: Props) {
|
|||
{props.page.id > 1 && (
|
||||
<LinkButton
|
||||
className='flex-1 w-full text-center'
|
||||
href={`/comic/${props.page.comic.slug}/transcribe/${props.page.id - 1}`}>
|
||||
href={`/comic/${
|
||||
props.page.comic.slug
|
||||
}/transcribe/${props.page.id - 1}`}
|
||||
>
|
||||
← Previous page
|
||||
</LinkButton>
|
||||
)}
|
||||
<LinkButton
|
||||
className='flex-1 w-full text-center'
|
||||
href={`/comic/${props.page.comic.slug}/transcribe/random`}>
|
||||
href={`/comic/${props.page.comic.slug}/transcribe/random`}
|
||||
>
|
||||
Random page
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
className='flex-1 w-full text-center'
|
||||
href={`/comic/${props.page.comic.slug}/transcribe/${props.page.id + 1}`}>
|
||||
href={`/comic/${
|
||||
props.page.comic.slug
|
||||
}/transcribe/${props.page.id + 1}`}
|
||||
>
|
||||
Next page →
|
||||
</LinkButton>
|
||||
</nav>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Comic } from '@prisma/client';
|
||||
import type { GetServerSideProps, NextPage } from 'next';
|
||||
import type { GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { LinkButton } from '../components/Button';
|
||||
import CompletionBar from '../components/CompletionBar';
|
||||
|
@ -15,7 +15,7 @@ interface Props {
|
|||
}[];
|
||||
}
|
||||
|
||||
const Home: NextPage<Props> = (props) => {
|
||||
const Home = (props: Props) => {
|
||||
return (
|
||||
<main className='p-4'>
|
||||
<Head>
|
||||
|
@ -32,24 +32,34 @@ const Home: NextPage<Props> = (props) => {
|
|||
</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>
|
||||
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>
|
||||
|
@ -61,33 +71,36 @@ 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,
|
||||
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 {
|
||||
comic,
|
||||
completion: {
|
||||
totalPages: pages.length,
|
||||
completePages: pages.filter(
|
||||
(page) => page._count.bubbles !== 0
|
||||
).length,
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
props: {
|
||||
comics: comicsWithCompletions,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -16,8 +16,10 @@ export default function Login() {
|
|||
? router.query.callbackUrl[0]
|
||||
: router.query.callbackUrl) ||
|
||||
'/';
|
||||
|
||||
const error = Array.isArray(router.query.error) ? router.query.error[0] : router.query.error;
|
||||
|
||||
const error = Array.isArray(router.query.error)
|
||||
? router.query.error[0]
|
||||
: router.query.error;
|
||||
|
||||
return (
|
||||
<div className='p-4 flex flex-col items-center justify-center'>
|
||||
|
@ -30,14 +32,20 @@ export default function Login() {
|
|||
<h1 className='text-4xl'>Login</h1>
|
||||
</header>
|
||||
|
||||
{error && <>
|
||||
<hr className='border-slate-600 my-4' />
|
||||
{error && (
|
||||
<>
|
||||
<hr className='border-slate-600 my-4' />
|
||||
|
||||
<Banner style='error'>
|
||||
{error === 'SessionRequired' && <>You must login to view this page.</>}
|
||||
{error === 'Default' && <>An error occurred {':('} Please try again.</>}
|
||||
</Banner>
|
||||
</>}
|
||||
<Banner style='error'>
|
||||
{error === 'SessionRequired' && (
|
||||
<>You must login to view this page.</>
|
||||
)}
|
||||
{error === 'Default' && (
|
||||
<>An error occurred {':('} Please try again.</>
|
||||
)}
|
||||
</Banner>
|
||||
</>
|
||||
)}
|
||||
|
||||
<hr className='border-slate-600 my-4' />
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
@layer components {
|
||||
.dots-between > li:not(:last-child)::after {
|
||||
content: "⦁";
|
||||
content: '⦁';
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue