mirror of
				https://github.com/DerTyp7/explainegy-nextjs.git
				synced 2025-10-30 21:27:12 +01:00 
			
		
		
		
	refactor
This commit is contained in:
		| @@ -5,7 +5,6 @@ import styles from "@/styles/modules/AdminNav.module.scss"; | |||||||
| function AdminNav() { | function AdminNav() { | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.adminNav}> |     <div className={styles.adminNav}> | ||||||
|       <Link href={"/admin"}>Admin</Link> |  | ||||||
|       <Link href={"/admin/editor/article/0"}>New article</Link> |       <Link href={"/admin/editor/article/0"}>New article</Link> | ||||||
|       <Link href={"/admin/editor/category/0"}>New category</Link> |       <Link href={"/admin/editor/category/0"}>New category</Link> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								components/ArticleControl.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								components/ArticleControl.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import React from "react"; | ||||||
|  | import { apiUrl } from "@/global"; | ||||||
|  | import urlJoin from "url-join"; | ||||||
|  | import { useRouter } from "next/navigation"; | ||||||
|  |  | ||||||
|  | export default function ArticleControl({ articleId }: { articleId: string }) { | ||||||
|  |   const router = useRouter(); | ||||||
|  |   async function deleteArticle() { | ||||||
|  |     await fetch(urlJoin(apiUrl, `articles/${articleId}`), { method: "DELETE" }) | ||||||
|  |       .then((response) => response.json()) | ||||||
|  |       .then((result) => { | ||||||
|  |         console.log(result); | ||||||
|  |       }); | ||||||
|  |     router.push("/articles"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function editArticle() { | ||||||
|  |     router.push("/admin/editor/article/" + articleId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       <button className="danger" onClick={deleteArticle}> | ||||||
|  |         Delete | ||||||
|  |       </button> | ||||||
|  |       <button className="warning" onClick={editArticle}> | ||||||
|  |         Edit | ||||||
|  |       </button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								components/CategoryControl.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								components/CategoryControl.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import React from "react"; | ||||||
|  | import { apiUrl } from "@/global"; | ||||||
|  | import urlJoin from "url-join"; | ||||||
|  | import { useRouter } from "next/navigation"; | ||||||
|  |  | ||||||
|  | export default function CategoryControl({ categoryId }: { categoryId: string }) { | ||||||
|  |   const router = useRouter(); | ||||||
|  |   async function deleteCategory() { | ||||||
|  |     await fetch(urlJoin(apiUrl, `categories/${categoryId}`), { method: "DELETE" }) | ||||||
|  |       .then((response) => response.json()) | ||||||
|  |       .then((result) => { | ||||||
|  |         console.log(result); | ||||||
|  |       }); | ||||||
|  |     router.push("/articles"); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function editCategory() { | ||||||
|  |     router.push("/admin/editor/category/" + categoryId); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div> | ||||||
|  |       <button className="danger" onClick={deleteCategory}> | ||||||
|  |         Delete | ||||||
|  |       </button> | ||||||
|  |       <button className="warning" onClick={editCategory}> | ||||||
|  |         Edit | ||||||
|  |       </button> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -1,18 +1,16 @@ | |||||||
| import React from "react"; | import React from "react"; | ||||||
| import styles from "@/styles/modules/ArticleContentTable.module.scss"; | import styles from "@/styles/modules/ArticleContentTable.module.scss"; | ||||||
| import { IContentTableEntry } from "../types/contentTable"; | import { IContentTableEntry } from "../types/contentTable"; | ||||||
|  | import { CLIENT_RENEG_LIMIT } from "tls"; | ||||||
|  |  | ||||||
| export default function ContentTable({ | export default function ContentTable({ contentTableData }: { contentTableData: IContentTableEntry[] }) { | ||||||
| 	contentTableData, |   console.log(contentTableData); | ||||||
| }: { |  | ||||||
| 	contentTableData: any; |  | ||||||
| }) { |  | ||||||
|   return ( |   return ( | ||||||
|     <div className={styles.articleContentTable}> |     <div className={styles.articleContentTable}> | ||||||
|       <div className={styles.stickyContainer}> |       <div className={styles.stickyContainer}> | ||||||
|         <div className={styles.list}> |         <div className={styles.list}> | ||||||
|           <h2>Contents</h2> |           <h2>Contents</h2> | ||||||
| 					{contentTableData?.map((e: IContentTableEntry, i: number) => { |           {contentTableData.map((e: IContentTableEntry, i: number) => { | ||||||
|             return ( |             return ( | ||||||
|               <a key={i} href={"#" + e.anchor}> |               <a key={i} href={"#" + e.anchor}> | ||||||
|                 {e.title} |                 {e.title} | ||||||
| @@ -20,11 +18,7 @@ export default function ContentTable({ | |||||||
|             ); |             ); | ||||||
|           })} |           })} | ||||||
|         </div> |         </div> | ||||||
| 				{contentTableData?.length < 15 ? ( |         {contentTableData?.length < 15 ? <div className={styles.adContainer}>Future advertisement</div> : ""} | ||||||
| 					<div className={styles.adContainer}>Future advertisement</div> |  | ||||||
| 				) : ( |  | ||||||
| 					"" |  | ||||||
| 				)} |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ import Image from "next/image"; | |||||||
| import Link from "next/link"; | import Link from "next/link"; | ||||||
| import { useEffect, useState } from "react"; | import { useEffect, useState } from "react"; | ||||||
| import { Category } from "@prisma/client"; | import { Category } from "@prisma/client"; | ||||||
|  | import prisma, { CategoryWithIncludes } from "@/lib/prisma"; | ||||||
|  | import { CLIENT_RENEG_LIMIT } from "tls"; | ||||||
|  | import { apiUrl } from "@/global"; | ||||||
|  | import urlJoin from "url-join"; | ||||||
|  |  | ||||||
| function switchTheme(theme: string) { | function switchTheme(theme: string) { | ||||||
|   const bodyElement = document.getElementsByTagName("body")[0]; |   const bodyElement = document.getElementsByTagName("body")[0]; | ||||||
| @@ -39,10 +43,9 @@ function toggleTheme() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| export default function Nav({ categories }: { categories: Category[] }) { | export default function Nav() { | ||||||
| 	const [searchResults, setSearchResults] = useState< |   const [searchResults, setSearchResults] = useState<{ name: string; title: string }[]>([]); | ||||||
| 		{ name: string; title: string }[] |   const [categories, setCategories] = useState<Category[]>([]); | ||||||
| 	>([]); |  | ||||||
|  |  | ||||||
|   async function handleSearchInput(event: React.ChangeEvent<HTMLInputElement>) { |   async function handleSearchInput(event: React.ChangeEvent<HTMLInputElement>) { | ||||||
|     const query = event.target.value; |     const query = event.target.value; | ||||||
| @@ -68,6 +71,17 @@ export default function Nav({ categories }: { categories: Category[] }) { | |||||||
|     console.log(searchResults); |     console.log(searchResults); | ||||||
|   }, [searchResults]); |   }, [searchResults]); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     async function getCategories() { | ||||||
|  |       await fetch(urlJoin(apiUrl, "categories")) | ||||||
|  |         .then((response) => response.json()) | ||||||
|  |         .then((result: Category[]) => { | ||||||
|  |           setCategories(result); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getCategories(); | ||||||
|  |   }, []); | ||||||
|   return ( |   return ( | ||||||
|     <nav className={styles.nav}> |     <nav className={styles.nav}> | ||||||
|       <div className={styles.containerLeft}> |       <div className={styles.containerLeft}> | ||||||
| @@ -91,10 +105,7 @@ export default function Nav({ categories }: { categories: Category[] }) { | |||||||
|                 {categories?.map((cat, i) => { |                 {categories?.map((cat, i) => { | ||||||
|                   { |                   { | ||||||
|                     return ( |                     return ( | ||||||
| 											<Link |                       <Link key={i} href={`/articles/${cat.name.toLowerCase()}`}> | ||||||
| 												key={i} |  | ||||||
| 												href={`/articles/${cat.name.toLowerCase()}`} |  | ||||||
| 											> |  | ||||||
|                         {cat.title} |                         {cat.title} | ||||||
|                       </Link> |                       </Link> | ||||||
|                     ); |                     ); | ||||||
| @@ -118,11 +129,7 @@ export default function Nav({ categories }: { categories: Category[] }) { | |||||||
|           <div className={styles.content}> |           <div className={styles.content}> | ||||||
|             {searchResults.map((s) => { |             {searchResults.map((s) => { | ||||||
|               { |               { | ||||||
| 								return ( |                 return <Link href={`/articles/${s.name.toLowerCase()}`}>{s.title}</Link>; | ||||||
| 									<Link href={`/articles/${s.name.toLowerCase()}`}> |  | ||||||
| 										{s.title} |  | ||||||
| 									</Link> |  | ||||||
| 								); |  | ||||||
|               } |               } | ||||||
|             })} |             })} | ||||||
|           </div> |           </div> | ||||||
| @@ -135,11 +142,7 @@ export default function Nav({ categories }: { categories: Category[] }) { | |||||||
|             toggleTheme(); |             toggleTheme(); | ||||||
|           }} |           }} | ||||||
|         > |         > | ||||||
| 					<svg |           <svg id="themeSwitchSvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> | ||||||
| 						id="themeSwitchSvg" |  | ||||||
| 						xmlns="http://www.w3.org/2000/svg" |  | ||||||
| 						viewBox="0 0 512 512" |  | ||||||
| 					> |  | ||||||
|             <path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zm64 0c0 141.4-114.6 256-256 256S0 397.4 0 256S114.6 0 256 0S512 114.6 512 256z" /> |             <path d="M448 256c0-106-86-192-192-192V448c106 0 192-86 192-192zm64 0c0 141.4-114.6 256-256 256S0 397.4 0 256S114.6 0 256 0S512 114.6 512 256z" /> | ||||||
|           </svg> |           </svg> | ||||||
|         </div> |         </div> | ||||||
| @@ -147,3 +150,21 @@ export default function Nav({ categories }: { categories: Category[] }) { | |||||||
|     </nav> |     </nav> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export async function getServerSideProps() { | ||||||
|  |   let categories: Category[] = []; | ||||||
|  |   await prisma.category.findMany({ include: { articles: true, svg: true } }).then( | ||||||
|  |     (result: Category[]) => { | ||||||
|  |       if (result) { | ||||||
|  |         categories = JSON.parse(JSON.stringify(result)); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     (reason: any) => { | ||||||
|  |       console.log(reason); | ||||||
|  |     } | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     props: { categories: categories }, // will be passed to the page component as props | ||||||
|  |   }; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -6,12 +6,14 @@ import type { AppProps } from "next/app"; | |||||||
| import AdminNav from "@/components/AdminNav"; | import AdminNav from "@/components/AdminNav"; | ||||||
| import Footer from "@/components/Footer"; | import Footer from "@/components/Footer"; | ||||||
| import Nav from "@/components/Nav"; | import Nav from "@/components/Nav"; | ||||||
|  | import { Category } from "@prisma/client"; | ||||||
|  | import prisma from "@/lib/prisma"; | ||||||
|  |  | ||||||
| export default function App({ Component, pageProps }: AppProps) { | export default function App({ Component, pageProps }: AppProps) { | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <header> |       <header> | ||||||
|         <Nav categories={[]} /> |         <Nav /> | ||||||
|         <AdminNav /> |         <AdminNav /> | ||||||
|       </header> |       </header> | ||||||
|       <Component {...pageProps} /> |       <Component {...pageProps} /> | ||||||
|   | |||||||
| @@ -2,11 +2,10 @@ import { Html, Head, Main, NextScript } from "next/document"; | |||||||
|  |  | ||||||
| export default function Document() { | export default function Document() { | ||||||
|   return ( |   return ( | ||||||
|     <Html lang="en"> |     <Html lang="en" style={{ scrollBehavior: "smooth" }}> | ||||||
|       <Head /> |       <Head /> | ||||||
|       <body> |       <body> | ||||||
|         <Main /> |         <Main /> | ||||||
|  |  | ||||||
|         <NextScript /> |         <NextScript /> | ||||||
|       </body> |       </body> | ||||||
|     </Html> |     </Html> | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ export default function AdminArticlesEditorPage({ article, categories }: { artic | |||||||
|   function changeContentTableEntryTitle(index: number, newTitle: string) { |   function changeContentTableEntryTitle(index: number, newTitle: string) { | ||||||
|     setContentTable((prevArray: any) => { |     setContentTable((prevArray: any) => { | ||||||
|       let newArray = [...prevArray]; |       let newArray = [...prevArray]; | ||||||
|       newArray[index].anchor = newTitle; |       newArray[index].title = newTitle; | ||||||
|       return newArray; |       return newArray; | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -1,19 +1,19 @@ | |||||||
| import { Prisma, Article } from "@prisma/client"; | import { Prisma, Article } from "@prisma/client"; | ||||||
| import { ResponseError } from "../../../types/responseErrors"; | import { ResponseError } from "../../../types/responseErrors"; | ||||||
| import { formatTextToUrlName } from "../../../utils"; | import { formatTextToUrlName } from "../../../utils"; | ||||||
| import prisma from "../../../lib/prisma"; | import prisma, { ArticleWithIncludes } from "../../../lib/prisma"; | ||||||
|  |  | ||||||
| import type { NextApiRequest, NextApiResponse } from 'next' | import type { NextApiRequest, NextApiResponse } from 'next' | ||||||
| import { UpdateArticle } from "../../../types/api"; | import { UpdateArticle } from "../../../types/api"; | ||||||
| import { isValidText } from "../../../validators"; | import { isValidText } from "../../../validators"; | ||||||
|  |  | ||||||
| type ArticleWithIncludes = Prisma.ArticleGetPayload<{ include: { contentTableEntries: true, category: true, image: true } }> |  | ||||||
|  |  | ||||||
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||||||
|  |   const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "") | ||||||
|  |  | ||||||
|   if (req.method == "PUT") {//* PUT |   if (req.method == "PUT") {//* PUT | ||||||
|     console.log("PUT") |     console.log("PUT") | ||||||
|     const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "") |  | ||||||
|  |  | ||||||
|     console.log(`API articleId: ${articleId}`) |     console.log(`API articleId: ${articleId}`) | ||||||
|     const articleData: UpdateArticle = req.body; |     const articleData: UpdateArticle = req.body; | ||||||
| @@ -55,6 +55,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||||||
|         console.error(err); |         console.error(err); | ||||||
|         res.status(500).end(); |         res.status(500).end(); | ||||||
|       }); |       }); | ||||||
|  |   } else if (req.method == "DELETE") { | ||||||
|  |     console.log("DELETE article") | ||||||
|  |     prisma.article.delete({ where: { id: articleId }, include: { category: true } }).then((result: ArticleWithIncludes | null) => { | ||||||
|  |       if (result) { | ||||||
|  |         res.status(200).json(result) | ||||||
|  |       } else { | ||||||
|  |         res.status(500).json({ error: true, message: "No article found" }) | ||||||
|  |       } | ||||||
|  |     }, (err) => { | ||||||
|  |       console.log(err) | ||||||
|  |       res.status(500).end(err) | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import prisma from "../../../lib/prisma"; | import prisma, { CategoryWithIncludes } from "../../../lib/prisma"; | ||||||
| import { Category } from "@prisma/client"; | import { Category } from "@prisma/client"; | ||||||
| import { ResponseError } from "../../../types/responseErrors"; | import { ResponseError } from "../../../types/responseErrors"; | ||||||
| import { Prisma } from "@prisma/client"; | import { Prisma } from "@prisma/client"; | ||||||
| @@ -9,10 +9,10 @@ import { isValidText } from "../../../validators"; | |||||||
| import { UpdateCategory } from '../../../types/api'; | import { UpdateCategory } from '../../../types/api'; | ||||||
|  |  | ||||||
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||||||
|  |   const categoryId: string = req.query.categoryId?.toString() ?? ""; | ||||||
|  |  | ||||||
|   if (req.method == "PUT") { |   if (req.method == "PUT") { | ||||||
|     const categoryId: string = req.query.categoryId?.toString() ?? ""; |  | ||||||
|     const categoryData: UpdateCategory = req.body; |     const categoryData: UpdateCategory = req.body; | ||||||
|  |  | ||||||
|     if (categoryData.title && !isValidText(categoryData.title)) { |     if (categoryData.title && !isValidText(categoryData.title)) { | ||||||
| @@ -66,5 +66,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) | |||||||
|         console.error(err); |         console.error(err); | ||||||
|         res.status(500).end(); |         res.status(500).end(); | ||||||
|       }); |       }); | ||||||
|  |   } else if (req.method == "DELETE") { | ||||||
|  |     console.log("DELETE category") | ||||||
|  |     prisma.category.delete({ where: { id: categoryId }, include: { articles: true, svg: true } }).then((result: CategoryWithIncludes | null) => { | ||||||
|  |       if (result) { | ||||||
|  |         res.status(200).json(result) | ||||||
|  |       } else { | ||||||
|  |         res.status(500).json({ error: true, message: "No category found" }) | ||||||
|  |       } | ||||||
|  |     }, (err) => { | ||||||
|  |       console.log(err) | ||||||
|  |       res.status(500).end(err) | ||||||
|  |     }) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,17 @@ import type { NextApiRequest, NextApiResponse } from 'next' | |||||||
| import { CreateCategory } from "@/types/api"; | import { CreateCategory } from "@/types/api"; | ||||||
|  |  | ||||||
| export default async function handler(req: NextApiRequest, res: NextApiResponse) { | export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||||||
|  |   if (req.method == "GET") { | ||||||
|  |     console.log("API get categories") | ||||||
|  |     await prisma.category.findMany().then((result: Category[]) => { | ||||||
|  |       if (result) { | ||||||
|  |         res.status(200).json(result) | ||||||
|  |       } | ||||||
|  |     }, (errorReason) => { | ||||||
|  |       console.log(errorReason); | ||||||
|  |       res.status(500).end(errorReason); | ||||||
|  |     }) | ||||||
|  |   } else | ||||||
|     if (req.method == "POST") { |     if (req.method == "POST") { | ||||||
|       console.log("API new category") |       console.log("API new category") | ||||||
|       const categoryData: CreateCategory = req.body; |       const categoryData: CreateCategory = req.body; | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								pages/api/search.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								pages/api/search.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | import prisma from "../../lib/prisma"; | ||||||
|  | import type { NextApiRequest, NextApiResponse } from 'next' | ||||||
|  | import { formatTextToUrlName } from '../../utils'; | ||||||
|  | import { Prisma } from '@prisma/client'; | ||||||
|  |  | ||||||
|  | type SearchArticle = Prisma.ArticleGetPayload<{ select: { title: true, name: true } }> | ||||||
|  |  | ||||||
|  | export default async function handler(req: NextApiRequest, res: NextApiResponse) { | ||||||
|  |   let query: string = req.query.q?.toString() ?? ""; | ||||||
|  |   query = formatTextToUrlName(query) | ||||||
|  |   if (query.length > 0) { | ||||||
|  |     await prisma.article.findMany({ | ||||||
|  |       select: { title: true, name: true }, | ||||||
|  |       take: 5, | ||||||
|  |     }).then((result: SearchArticle[]) => { | ||||||
|  |       let searchResult: SearchArticle[] = [] | ||||||
|  |       result.forEach((a: SearchArticle) => { | ||||||
|  |         if (a.name.includes(query)) { | ||||||
|  |           searchResult.push(a); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |       res.status(200).json(searchResult); | ||||||
|  |     }, (err: any) => { | ||||||
|  |       console.log(err) | ||||||
|  |       res.status(200).json([]); | ||||||
|  |     }); | ||||||
|  |   } else { | ||||||
|  |     res.status(200).json([]); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -5,7 +5,9 @@ import Image from "next/image"; | |||||||
| import Markdown from "@/components/Markdown"; | import Markdown from "@/components/Markdown"; | ||||||
| import { formatTextToUrlName } from "@/utils"; | import { formatTextToUrlName } from "@/utils"; | ||||||
| import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma"; | import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma"; | ||||||
| import articles from "../.."; | import ArticleControl from "../../../../components/ArticleControl"; | ||||||
|  | import { IContentTableEntry } from "@/types/contentTable"; | ||||||
|  | import { Prisma } from "@prisma/client"; | ||||||
|  |  | ||||||
| //* MAIN | //* MAIN | ||||||
| export default function ArticlePage({ article }: { article: ArticleWithIncludes }) { | export default function ArticlePage({ article }: { article: ArticleWithIncludes }) { | ||||||
| @@ -14,8 +16,12 @@ export default function ArticlePage({ article }: { article: ArticleWithIncludes | |||||||
|   const dateOptions: Intl.DateTimeFormatOptions = { month: "long", day: "numeric", year: "numeric" }; |   const dateOptions: Intl.DateTimeFormatOptions = { month: "long", day: "numeric", year: "numeric" }; | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|  |     <> | ||||||
|  |       <ArticleControl articleId={article.id} /> | ||||||
|       <div className={styles.article}> |       <div className={styles.article}> | ||||||
|       <ContentTable contentTableData={article?.contentTable ? article?.contentTable : []} /> |         <ContentTable | ||||||
|  |           contentTableData={article?.contentTable ? Array.from(article.contentTable as Prisma.JsonArray).map((c: any) => ({ anchor: c.anchor, title: c.title })) : []} | ||||||
|  |         /> | ||||||
|         <div className={styles.tutorialContent}> |         <div className={styles.tutorialContent}> | ||||||
|           <div className={styles.header}> |           <div className={styles.header}> | ||||||
|             <p className={`${styles.dates} text-muted`}> |             <p className={`${styles.dates} text-muted`}> | ||||||
| @@ -35,6 +41,7 @@ export default function ArticlePage({ article }: { article: ArticleWithIncludes | |||||||
|         </div> |         </div> | ||||||
|         <Sidebar /> |         <Sidebar /> | ||||||
|       </div> |       </div> | ||||||
|  |     </> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,9 +3,12 @@ import Link from "next/link"; | |||||||
| import { formatTextToUrlName } from "@/utils"; | import { formatTextToUrlName } from "@/utils"; | ||||||
| import { Article, Category } from "@prisma/client"; | import { Article, Category } from "@prisma/client"; | ||||||
| import prisma, { CategoryWithIncludes } from "@/lib/prisma"; | import prisma, { CategoryWithIncludes } from "@/lib/prisma"; | ||||||
|  | import CategoryControl from "../../../components/CategoryControl"; | ||||||
|  |  | ||||||
| export default function CategoryPage({ category }: { category: CategoryWithIncludes | null }) { | export default function CategoryPage({ category }: { category: CategoryWithIncludes }) { | ||||||
|   return ( |   return ( | ||||||
|  |     <> | ||||||
|  |       <CategoryControl categoryId={category.id} /> | ||||||
|       <div className={styles.category}> |       <div className={styles.category}> | ||||||
|         <h1>{category?.title}</h1> |         <h1>{category?.title}</h1> | ||||||
|         <div className={styles.content}> |         <div className={styles.content}> | ||||||
| @@ -51,6 +54,7 @@ export default function CategoryPage({ category }: { category: CategoryWithInclu | |||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |     </> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| export async function getServerSideProps(context: any) { | export async function getServerSideProps(context: any) { | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								prisma/migrations/20230207142155_ondelete/migration.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								prisma/migrations/20230207142155_ondelete/migration.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | -- DropForeignKey | ||||||
|  | ALTER TABLE "Article" DROP CONSTRAINT "Article_categoryId_fkey"; | ||||||
|  |  | ||||||
|  | -- AddForeignKey | ||||||
|  | ALTER TABLE "Article" ADD CONSTRAINT "Article_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||||||
| @@ -16,7 +16,7 @@ model Article { | |||||||
|   markdown     String |   markdown     String | ||||||
|   contentTable Json? |   contentTable Json? | ||||||
|   categoryId   String |   categoryId   String | ||||||
|   category     Category @relation(fields: [categoryId], references: [id]) |   category     Category @relation(fields: [categoryId], references: [id], onDelete: Cascade) | ||||||
|   dateCreated  DateTime @default(now()) |   dateCreated  DateTime @default(now()) | ||||||
|   dateUpdated  DateTime @default(now()) |   dateUpdated  DateTime @default(now()) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Janis
					Janis