Skip to content

Commit 2b513c7

Browse files
committed
feat: sprint 3
1 parent 8a990c4 commit 2b513c7

File tree

10 files changed

+141
-179
lines changed

10 files changed

+141
-179
lines changed

src/app/(main)/chapter/edit/page.tsx

+115-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,123 @@
1-
import { useRouter } from 'next/router';
1+
'use client';
2+
3+
import { yupResolver } from '@hookform/resolvers/yup';
4+
import { useRouter, useSearchParams } from 'next/navigation';
5+
import { useEffect, useState } from 'react';
6+
import { SubmitHandler, useForm } from 'react-hook-form';
7+
import * as yup from 'yup';
8+
9+
import { FormValues } from './types';
10+
11+
import { Input } from '@/shared/components/FormComponents';
12+
import { Button } from '@/shared/components/ui/button';
13+
import { useToast } from '@/shared/components/ui/use-toast';
14+
import useAxiosAuth from '@/shared/hooks/useAxiosAuth';
15+
import Chapter from '@/shared/models/chapter';
16+
17+
const validationSchema: yup.ObjectSchema<FormValues> = yup.object({
18+
title: yup
19+
.string()
20+
.required('No title provided.')
21+
.min(10, 'Title too short.')
22+
.max(124, 'Title too long.'),
23+
chapterNumber: yup.number().required('No chapter number provided.'),
24+
createdOn: yup.string().required('No release date provided.'),
25+
});
226

327
function Page() {
28+
const [chapter, setChapter] = useState<Chapter | null>(null);
29+
430
const router = useRouter();
5-
const { mangaId, chapterId } = router.query;
31+
const queryParams = useSearchParams();
32+
33+
const axiosAuth = useAxiosAuth();
34+
const { toast } = useToast();
35+
const {
36+
register,
37+
handleSubmit,
38+
setValue,
39+
formState: { errors },
40+
} = useForm({
41+
resolver: yupResolver(validationSchema),
42+
});
43+
44+
const mangaId = queryParams.get('mangaId');
45+
const chapterId = queryParams.get('chapterId');
46+
47+
const fetchChapter = async () => {
48+
const res = await axiosAuth.get<Chapter>(`Chapters`, {
49+
params: { chapterId },
50+
});
51+
52+
setChapter(res.data);
53+
setValue('title', res.data.title);
54+
setValue('chapterNumber', res.data.chapterNumber);
55+
setValue(
56+
'createdOn',
57+
new Date(res.data.createdOn).toISOString().slice(0, 10),
58+
);
59+
};
60+
61+
useEffect(() => {
62+
fetchChapter();
63+
}, []);
64+
65+
const onSubmit: SubmitHandler<FormValues> = async (data) => {
66+
try {
67+
await axiosAuth.post<unknown, unknown, unknown>('Chapters', {
68+
...chapter,
69+
...data,
70+
chapterId,
71+
mangaId,
72+
});
73+
74+
toast({
75+
title: 'Success',
76+
description: `Chapter "${data.title}" was successfully created!`,
77+
});
78+
79+
router.push(`/manga/${mangaId}`);
80+
} catch (error) {
81+
toast({
82+
title: 'Error occurred!',
83+
variant: 'destructive',
84+
});
85+
}
86+
};
87+
88+
if (!chapter) {
89+
return <div>Loading...</div>;
90+
}
691

792
return (
8-
<div>
9-
<h1>Page</h1>
10-
<p>Param1: {mangaId}</p>
11-
<p>Param2: {chapterId}</p>
93+
<div className="flex flex-col items-center gap-6 pt-10">
94+
<p className="text-2xl">Edit chapter</p>
95+
<form
96+
className="flex w-[50%] flex-col gap-6 lg:w-[50rem]"
97+
onSubmit={handleSubmit(onSubmit)}
98+
>
99+
<Input
100+
label="title"
101+
register={register}
102+
error={errors?.title?.message}
103+
/>
104+
<Input
105+
label="chapterNumber"
106+
type="number"
107+
defaultValue={1}
108+
min={0}
109+
register={register}
110+
error={errors?.chapterNumber?.message}
111+
/>
112+
<Input
113+
label="createdOn"
114+
type="date"
115+
register={register}
116+
error={errors?.createdOn?.message}
117+
// defaultValue={new Date().toISOString().slice(0, 10)}
118+
/>
119+
<Button type="submit">Submit</Button>
120+
</form>
12121
</div>
13122
);
14123
}

src/app/(main)/chapter/edit/types.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type FormValues = {
2+
title: string;
3+
chapterNumber: number;
4+
createdOn: string;
5+
};
6+
7+
export type CreateChapterDto = {
8+
title: string;
9+
chapterNumber: number;
10+
createdOn: Date;
11+
};

src/app/(main)/manga/[mangaId]/components/ChapterCard.tsx

+8-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ChapterDTO } from '../types';
88
import { Button } from '@/shared/components/ui/button';
99
import { Card } from '@/shared/components/ui/card';
1010
import { useToast } from '@/shared/components/ui/use-toast';
11+
import { ROUTE } from '@/shared/constants/routes';
1112
import useAxiosAuth from '@/shared/hooks/useAxiosAuth';
1213

1314
type ChapterListProps = {
@@ -30,7 +31,11 @@ function ChapterCard({
3031

3132
const { data: session } = useSession();
3233

33-
// const handleEdit = async () => {};
34+
const handleEdit = async () => {
35+
router.push(
36+
`${ROUTE.EDIT_CHAPTER}?chapterId=${chapter.chapterId}&mangaId=${mangaId}`,
37+
);
38+
};
3439

3540
const handleDelete = async () => {
3641
try {
@@ -89,7 +94,7 @@ function ChapterCard({
8994
<Card
9095
className="z-0 flex w-full flex-row items-center justify-between p-2 px-8"
9196
onClick={() => {
92-
router.push(`/manga/${mangaId}/${chapter.chapterId}`);
97+
router.push(`${ROUTE.MANGA}/${mangaId}/${chapter.chapterId}`);
9398
}}
9499
>
95100
<p>
@@ -110,15 +115,7 @@ function ChapterCard({
110115
>
111116
<LucideUpload size={20} />
112117
</Button>
113-
<Button
114-
variant="outline"
115-
onClick={(e) => {
116-
e.stopPropagation();
117-
e.nativeEvent.stopImmediatePropagation();
118-
e.preventDefault();
119-
fileInputRef?.current.click();
120-
}}
121-
>
118+
<Button variant="outline" onClick={handleEdit}>
122119
<Edit size={20} />
123120
</Button>
124121
<Button variant="destructive" onClick={handleDelete}>

src/app/(main)/manga/[mangaId]/edit/[chapterId]/page.tsx

-125
This file was deleted.

src/app/(main)/manga/[mangaId]/edit/[chapterId]/types.ts

-17
This file was deleted.

src/app/(main)/manga/components/MangaCard.tsx

+3-11
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function MangaCard({ data }: MangaCardProps) {
2323

2424
function getGenreName(value: GenreType): string | undefined {
2525
for (const prop in Genre) {
26-
if (Genre[prop as keyof typeof Genre] === value) {
26+
if (Genre[prop as keyof typeof Genre] == value) {
2727
return prop;
2828
}
2929
}
@@ -39,17 +39,9 @@ function MangaCard({ data }: MangaCardProps) {
3939
className="flex h-[200px] w-[400px] justify-between px-4"
4040
>
4141
<div className="flex flex-col gap-4 pt-4">
42-
<CardTitle>{truncateTitle(data.title, 20)}</CardTitle>
42+
<CardTitle>{truncateTitle(data.title, 15)}</CardTitle>
4343
<CardDescription className="text-primary">
44-
{getGenreName(data.genre)}
45-
</CardDescription>
46-
<CardDescription className="text-primary">
47-
Genre:{' '}
48-
{capitalizedWords(
49-
getGenreName(
50-
parseInt(data.genre as unknown as string) as GenreType,
51-
),
52-
)}
44+
Genre: {capitalizedWords(getGenreName(data.genre))}
5345
</CardDescription>
5446
<CardDescription>Rating: {data.rating} / 5</CardDescription>
5547
</div>

src/app/(main)/manga/page.tsx

-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ function MangaPage() {
5757
}, []);
5858

5959
const onSubmit: SubmitHandler<SearchParams> = async (data) => {
60-
// setManga(null);
61-
6260
const manga = await fetchManga(data.title, data.genre, data.rating, page);
6361

6462
setManga(manga);

src/app/api/auth/[...nextauth]/options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Role } from '@/shared/types/auth';
88
import { axiosAuth } from '@/shared/utils/axios';
99

1010
export const authOptions: AuthOptions = {
11+
secret: process.env.NEXTAUTH_SECRET,
1112
session: { strategy: 'jwt' },
1213
pages: {
1314
signIn: '/sign-in',

src/middleware.ts

+1-7
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,5 @@ export default withAuth(
2929
);
3030

3131
export const config = {
32-
matcher: [
33-
'/profile',
34-
'/admin',
35-
'/admin/set-admin',
36-
'/manga/add-manga',
37-
'/manga/edit-manga',
38-
],
32+
matcher: ['/profile', '/admin'],
3933
};

0 commit comments

Comments
 (0)