This commit is contained in:
Janis
2023-02-08 20:56:22 +01:00
parent 97d8de1a44
commit 730f33879b
17 changed files with 794 additions and 452 deletions

View File

@@ -8,16 +8,17 @@ import Footer from "@/components/Footer";
import Nav from "@/components/Nav";
import { Category } from "@prisma/client";
import prisma from "@/lib/prisma";
import { SessionProvider } from "next-auth/react";
export default function App({ Component, pageProps }: AppProps) {
export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
return (
<>
<SessionProvider session={session}>
<header>
<Nav />
<AdminNav />
</header>
<Component {...pageProps} />
<Footer />
</>
</SessionProvider>
);
}

View File

@@ -13,9 +13,13 @@ import { apiUrl } from "@/global";
import Markdown from "@/components/Markdown";
import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma";
import { CLIENT_RENEG_LIMIT } from "tls";
import { useSession } from "next-auth/react";
export default function AdminArticlesEditorPage({ article, categories }: { article: ArticleWithIncludes | null; categories: CategoryWithIncludes[] }) {
const router = useRouter();
const { status } = useSession({
required: true,
});
const [title, setTitle] = useState<string>(article?.title ?? "");
const [selectCategoriesOptions, setSelectCategoriesOptions] = useState<{ value: string; label: string }[]>(
@@ -116,132 +120,136 @@ export default function AdminArticlesEditorPage({ article, categories }: { artic
.catch(console.error);
}
return (
<div className={styles.adminArticlesCreate}>
<h1>{article ? "Update article" : "Create new article"}</h1>
<div className={styles.formControl}>
<p className="text-error" ref={errorTextRef}></p>
<button
type="button"
onClick={() => {
if (article) {
updateArticle();
} else {
createArticle();
}
}}
>
{article ? "Update article" : "Create article"}
</button>
</div>
if (status === "authenticated") {
return (
<div className={styles.adminArticlesCreate}>
<h1>{article ? "Update article" : "Create new article"}</h1>
<div className={styles.formControl}>
<p className="text-error" ref={errorTextRef}></p>
<button
type="button"
onClick={() => {
if (article) {
updateArticle();
} else {
createArticle();
}
}}
>
{article ? "Update article" : "Create article"}
</button>
</div>
<div className={styles.form}>
<div className={styles.articleEditor}>
<div className={styles.title}>
<label htmlFor="title">Title</label>
<div className={styles.form}>
<div className={styles.articleEditor}>
<div className={styles.title}>
<label htmlFor="title">Title</label>
<div className={styles.titleInputs}>
<div className={styles.titleInputs}>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
}}
className={!isValidText(title) && title ? "error" : ""}
type="text"
name="title"
placeholder="title"
ref={titleRef}
defaultValue={title}
/>
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
</div>
</div>
<div className={styles.category}>
<label htmlFor="title">Category</label>
<Select
ref={categorySelectRef}
className="react-select-container"
classNamePrefix="react-select"
options={selectCategoriesOptions}
defaultValue={article ? { value: article.category.id, label: article.category.title } : {}}
/>
</div>
<div className={styles.introduction}>
<label htmlFor="title">Introduction</label>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
setIntroduction(e.target.value);
}}
className={!isValidText(title) && title ? "error" : ""}
className={!isValidText(introduction) && introduction ? "error" : ""}
type="text"
name="title"
placeholder="title"
ref={titleRef}
defaultValue={title}
name="introduction"
placeholder="Introduction"
ref={introductionRef}
defaultValue={introduction}
/>
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
</div>
</div>
<div className={styles.category}>
<label htmlFor="title">Category</label>
<Select
ref={categorySelectRef}
className="react-select-container"
classNamePrefix="react-select"
options={selectCategoriesOptions}
defaultValue={article ? { value: article.category.id, label: article.category.title } : {}}
/>
</div>
<div className={styles.introduction}>
<label htmlFor="title">Introduction</label>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setIntroduction(e.target.value);
}}
className={!isValidText(introduction) && introduction ? "error" : ""}
type="text"
name="introduction"
placeholder="Introduction"
ref={introductionRef}
defaultValue={introduction}
/>
</div>
<div className={styles.markdown}>
<label htmlFor="">Markdown Editor</label>
<div className={styles.markdownEditor}>
<textarea
ref={markdownTextAreaRef}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMarkdown(e.target.value);
}}
defaultValue={markdown}
></textarea>
<Markdown value={markdown} />
</div>
</div>
<div className={styles.contentTable}>
<label htmlFor="">Table of contents</label>
<div className={styles.contentTableEditor}>
<div className={styles.entries}>
{contentTable?.map((entry: IContentTableEntry, i: number) => {
return (
<div key={i}>
<input
onChange={(e) => {
changeContentTableEntryAnchor(i, e.target.value);
}}
type="text"
placeholder={"Anchor"}
defaultValue={entry.anchor}
/>
<input
onChange={(e) => {
changeContentTableEntryTitle(i, e.target.value);
}}
type="text"
placeholder={"Title"}
defaultValue={entry.title}
/>{" "}
<button
onClick={() => {
removeEntry(i);
}}
>
Remove
</button>
</div>
);
})}
<button
onClick={() => {
setContentTable([...contentTable, { title: "", anchor: "" }]);
<div className={styles.markdown}>
<label htmlFor="">Markdown Editor</label>
<div className={styles.markdownEditor}>
<textarea
ref={markdownTextAreaRef}
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMarkdown(e.target.value);
}}
>
Add
</button>
defaultValue={markdown}
></textarea>
<Markdown value={markdown} />
</div>
</div>
<div className={styles.contentTable}>
<label htmlFor="">Table of contents</label>
<div className={styles.contentTableEditor}>
<div className={styles.entries}>
{contentTable?.map((entry: IContentTableEntry, i: number) => {
return (
<div key={i}>
<input
onChange={(e) => {
changeContentTableEntryAnchor(i, e.target.value);
}}
type="text"
placeholder={"Anchor"}
defaultValue={entry.anchor}
/>
<input
onChange={(e) => {
changeContentTableEntryTitle(i, e.target.value);
}}
type="text"
placeholder={"Title"}
defaultValue={entry.title}
/>{" "}
<button
onClick={() => {
removeEntry(i);
}}
>
Remove
</button>
</div>
);
})}
<button
onClick={() => {
setContentTable([...contentTable, { title: "", anchor: "" }]);
}}
>
Add
</button>
</div>
<Markdown value={markdown} />
</div>
<Markdown value={markdown} />
</div>
</div>
</div>
</div>
</div>
);
);
} else {
return <></>;
}
}
export async function getServerSideProps(context: any) {

View File

@@ -9,8 +9,13 @@ import { useRouter } from "next/navigation";
import { useEffect } from "react";
import { apiUrl } from "@/global";
import prisma, { CategoryWithIncludes } from "@/lib/prisma";
import { useSession } from "next-auth/react";
export default function AdminCategoriesEditor({ category }: { category: CategoryWithIncludes | null }) {
const { status } = useSession({
required: true,
});
const router = useRouter();
const [title, setTitle] = useState<string>(category?.title ?? "");
const [color, setColor] = useState<string>(category?.color ?? "");
@@ -85,80 +90,84 @@ export default function AdminCategoriesEditor({ category }: { category: Category
.catch(console.error);
}
return (
<div className={styles.categoryEditor}>
<h1>{category ? "Update category" : "Create new category"}</h1>
<div className={styles.formControl}>
<p className="text-error" ref={errorTextRef}></p>
<button
type="button"
onClick={() => {
if (category) {
updateCategory();
} else {
createCategory();
}
}}
>
{category ? "Update category" : "Create category"}
</button>
</div>
<div className={styles.form}>
<div className={styles.title}>
<label htmlFor="title">Title</label>
<div className={styles.titleInputs}>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
}}
className={!isValidText(title) && title ? "error" : ""}
type="text"
name="title"
placeholder="title"
ref={titleRef}
defaultValue={title}
/>
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
</div>
<div className={styles.svg}>
<label>SVG</label>
<div className={styles.svgInputs}>
if (status === "authenticated") {
return (
<div className={styles.categoryEditor}>
<h1>{category ? "Update category" : "Create new category"}</h1>
<div className={styles.formControl}>
<p className="text-error" ref={errorTextRef}></p>
<button
type="button"
onClick={() => {
if (category) {
updateCategory();
} else {
createCategory();
}
}}
>
{category ? "Update category" : "Create category"}
</button>
</div>
<div className={styles.form}>
<div className={styles.title}>
<label htmlFor="title">Title</label>
<div className={styles.titleInputs}>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSvgPath(e.target.value);
setTitle(e.target.value);
}}
className={!isValidText(title) && title ? "error" : ""}
type="text"
placeholder="svg path"
ref={svgPathRef}
defaultValue={svgPath}
name="title"
placeholder="title"
ref={titleRef}
defaultValue={title}
/>
<input readOnly={true} className={""} type="text" name="name" value={title ? formatTextToUrlName(title) : ""} />
</div>
<div className={styles.svg}>
<label>SVG</label>
<div className={styles.svgInputs}>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSvgPath(e.target.value);
}}
type="text"
placeholder="svg path"
ref={svgPathRef}
defaultValue={svgPath}
/>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSvgViewbox(e.target.value);
}}
type="text"
placeholder="0 0 512 512"
ref={svgViewboxRef}
defaultValue={svgViewbox}
/>
</div>
</div>
<div className={styles.color}>
<label>Color</label>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSvgViewbox(e.target.value);
setColor(e.target.value);
}}
type="text"
placeholder="0 0 512 512"
ref={svgViewboxRef}
defaultValue={svgViewbox}
type="color"
ref={colorRef}
defaultValue={color}
/>
</div>
</div>
<div className={styles.color}>
<label>Color</label>
<input
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setColor(e.target.value);
}}
type="color"
ref={colorRef}
defaultValue={color}
/>
</div>
</div>
</div>
</div>
);
);
} else {
return <></>;
}
}
export async function getServerSideProps(context: any) {

View File

@@ -6,66 +6,72 @@ import prisma, { ArticleWithIncludes } from "../../../lib/prisma";
import type { NextApiRequest, NextApiResponse } from 'next'
import { UpdateArticle } from "../../../types/api";
import { isValidText } from "../../../validators";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "")
if (req.method == "PUT") {//* PUT
console.log("PUT")
const session = await getServerSession(req, res, authOptions);
if (session) {
const articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "")
if (req.method == "PUT") {//* PUT
console.log("PUT")
console.log(`API articleId: ${articleId}`)
const articleData: UpdateArticle = req.body;
console.log(`API articleId: ${articleId}`)
const articleData: UpdateArticle = req.body;
if (articleData.title && !isValidText(articleData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
if (articleData.introduction && !isValidText(articleData.introduction)) {
res.json({ target: "introduction", error: "Not a valid introduction" });
return;
}
const newArticle: Prisma.ArticleUncheckedUpdateInput = {
title: articleData.title ?? undefined,
name: articleData.title ? formatTextToUrlName(articleData.title) : undefined,
introduction: articleData.introduction ?? undefined,
categoryId: articleData.categoryId ?? undefined,
contentTable: articleData.contentTable ?? undefined,
markdown: articleData.markdown ?? undefined,
imageUrl: articleData.imageUrl ?? undefined,
}
console.log(newArticle)
await prisma.article.update({ data: newArticle, where: { id: articleId }, include: { category: true } })
.then(
(data) => {
res.json({ success: true, data: data });
},
(errorReason) => {
console.log(errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
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" })
if (articleData.title && !isValidText(articleData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
}, (err) => {
console.log(err)
res.status(500).end(err)
})
if (articleData.introduction && !isValidText(articleData.introduction)) {
res.json({ target: "introduction", error: "Not a valid introduction" });
return;
}
const newArticle: Prisma.ArticleUncheckedUpdateInput = {
title: articleData.title ?? undefined,
name: articleData.title ? formatTextToUrlName(articleData.title) : undefined,
introduction: articleData.introduction ?? undefined,
categoryId: articleData.categoryId ?? undefined,
contentTable: articleData.contentTable ?? undefined,
markdown: articleData.markdown ?? undefined,
imageUrl: articleData.imageUrl ?? undefined,
}
console.log(newArticle)
await prisma.article.update({ data: newArticle, where: { id: articleId }, include: { category: true } })
.then(
(data) => {
res.json({ success: true, data: data });
},
(errorReason) => {
console.log(errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
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)
})
}
} else {
res.status(403).json({ error: true, message: "Authorization Required" });
}
}

View File

@@ -6,56 +6,64 @@ import { isValidText } from "../../../validators";
import type { NextApiRequest, NextApiResponse } from 'next'
import { Prisma } from '@prisma/client';
import { getServerSession } from 'next-auth';
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method == "POST") { //* POST
console.log("API new article")
const articleData: CreateArticle = req.body
console.log(articleData)
const session = await getServerSession(req, res, authOptions);
if (session) {
if (req.method == "POST") { //* POST
console.log("API new article")
const articleData: CreateArticle = req.body
console.log(articleData)
if (!isValidText(articleData.title)) {
res.status(500).json({ target: "title", error: "Not a valid title" });
return;
}
if (!isValidText(articleData.title)) {
res.status(500).json({ target: "title", error: "Not a valid title" });
return;
}
if (!isValidText(articleData.introduction)) {
res.status(500).json({ target: "introduction", error: "Not a valid introduction" });
return;
}
if (!isValidText(articleData.introduction)) {
res.status(500).json({ target: "introduction", error: "Not a valid introduction" });
return;
}
if (!articleData.categoryId) {
res.status(500).json({ target: "category", error: "Category is required" });
return;
}
if (!articleData.categoryId) {
res.status(500).json({ target: "category", error: "Category is required" });
return;
}
const newArticle: Prisma.ArticleUncheckedCreateInput = {
title: articleData.title,
name: formatTextToUrlName(articleData.title),
introduction: articleData.introduction,
categoryId: articleData.categoryId,
markdown: articleData.markdown ?? "",
contentTable: articleData.contentTable ?? {},
imageUrl: articleData.imageUrl ?? ""
}
const newArticle: Prisma.ArticleUncheckedCreateInput = {
title: articleData.title,
name: formatTextToUrlName(articleData.title),
introduction: articleData.introduction,
categoryId: articleData.categoryId,
markdown: articleData.markdown ?? "",
contentTable: articleData.contentTable ?? {},
imageUrl: articleData.imageUrl ?? ""
}
prisma.article
.create({ data: newArticle, include: { category: true } })
.then(
(data: ArticleWithIncludes) => {
res.json({ success: true, data: data });
},
(errorReason) => {
console.log("reason", errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
prisma.article
.create({ data: newArticle, include: { category: true } })
.then(
(data: ArticleWithIncludes) => {
res.json({ success: true, data: data });
},
(errorReason) => {
console.log("reason", errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
}
} else {
res.status(403).json({ error: true, message: "Authorization Required" });
}
}

View File

@@ -0,0 +1,30 @@
import NextAuth, { AuthOptions } from "next-auth"
import GithubProvider from "next-auth/providers/github"
export const authOptions: AuthOptions = {
// Configure one or more authentication providers
providers: [
GithubProvider({
clientId: "1afc604704e6ac0149e3", //! env vars
clientSecret: "b8f76990fc0a9181eaba23359a27b2d140ab67e7", //! env vars
}),
// ...add more providers here
], callbacks: {
async signIn({ user, account, profile, email, credentials }) {
if (user.id.toString() == "76851529") { //! env vars
return true
} else {
// Return false to display a default error message
return false
// Or you can return a URL to redirect to:
// return '/unauthorized'
}
}
},
secret: "@AWeFkHpv!jzVr^a9nRXS8^PcRFnDaLvt65mJb&*C^pcCgpbHFzzKN",
}
export default NextAuth(authOptions)

View File

@@ -7,76 +7,83 @@ import type { NextApiRequest, NextApiResponse } from 'next'
import { formatTextToUrlName } from "../../../utils";
import { isValidText } from "../../../validators";
import { UpdateCategory } from '../../../types/api';
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const categoryId: string = req.query.categoryId?.toString() ?? "";
if (req.method == "PUT") {
const session = await getServerSession(req, res, authOptions);
if (session) {
if (req.method == "PUT") {
const categoryData: UpdateCategory = req.body;
const categoryData: UpdateCategory = req.body;
if (categoryData.title && !isValidText(categoryData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
const newSvg: Prisma.SvgUncheckedUpdateInput = {
viewbox: categoryData.svg?.viewbox ?? undefined,
path: categoryData.svg?.path ?? undefined,
};
const newCategory: Prisma.CategoryUncheckedUpdateInput = {
title: categoryData.title ?? undefined,
name: categoryData.title ? formatTextToUrlName(categoryData.title) : undefined,
color: categoryData.color ?? undefined,
};
await prisma.category
.update({
data: newCategory,
where: { id: categoryId },
include: { svg: true },
})
.then(
async (categoryData) => {
await prisma.svg
.update({ data: newSvg, where: { id: categoryData.svg.id } })
.then(
(svgData) => {
console.log("3");
res.json({ success: true, data: categoryData });
},
(errorReason) => {
res.status(500).end(errorReason);
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
},
(errorReason) => {
console.log(errorReason);
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
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" })
if (categoryData.title && !isValidText(categoryData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
}, (err) => {
console.log(err)
res.status(500).end(err)
})
const newSvg: Prisma.SvgUncheckedUpdateInput = {
viewbox: categoryData.svg?.viewbox ?? undefined,
path: categoryData.svg?.path ?? undefined,
};
const newCategory: Prisma.CategoryUncheckedUpdateInput = {
title: categoryData.title ?? undefined,
name: categoryData.title ? formatTextToUrlName(categoryData.title) : undefined,
color: categoryData.color ?? undefined,
};
await prisma.category
.update({
data: newCategory,
where: { id: categoryId },
include: { svg: true },
})
.then(
async (categoryData) => {
await prisma.svg
.update({ data: newSvg, where: { id: categoryData.svg.id } })
.then(
(svgData) => {
console.log("3");
res.json({ success: true, data: categoryData });
},
(errorReason) => {
res.status(500).end(errorReason);
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
},
(errorReason) => {
console.log(errorReason);
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
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)
})
}
} else {
res.status(403).json({ error: true, message: "Authorization Required" });
}
}

View File

@@ -8,8 +8,12 @@ import { isValidText } from "../../../validators";
import type { NextApiRequest, NextApiResponse } from 'next'
import { CreateCategory } from "@/types/api";
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getServerSession(req, res, authOptions);
if (req.method == "GET") {
console.log("API get categories")
await prisma.category.findMany().then((result: Category[]) => {
@@ -22,65 +26,69 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
})
} else
if (req.method == "POST") {
console.log("API new category")
const categoryData: CreateCategory = req.body;
console.log(categoryData)
if (session) {
console.log("API new category")
const categoryData: CreateCategory = req.body;
console.log(categoryData)
if (!isValidText(categoryData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
if (!isValidText(categoryData.title)) {
res.json({ target: "title", error: "Not a valid title" });
return;
}
categoryData.svg.viewbox = categoryData.svg.viewbox.length > 1 ? categoryData.svg.viewbox : "";
categoryData.svg.viewbox = categoryData.svg.viewbox.length > 1 ? categoryData.svg.viewbox : "";
const newSvg: Prisma.SvgUncheckedCreateInput = {
viewbox: categoryData.svg.viewbox,
path: categoryData.svg.path
}
const newSvg: Prisma.SvgUncheckedCreateInput = {
viewbox: categoryData.svg.viewbox,
path: categoryData.svg.path
}
await prisma.svg
.create({ data: newSvg })
.then(
async (createdSvg: Svg) => {
const newCategory: Prisma.CategoryUncheckedCreateInput = {
title: categoryData.title,
name: formatTextToUrlName(categoryData.title),
color: categoryData.color ?? "teal",
svgId: createdSvg.id,
await prisma.svg
.create({ data: newSvg })
.then(
async (createdSvg: Svg) => {
const newCategory: Prisma.CategoryUncheckedCreateInput = {
title: categoryData.title,
name: formatTextToUrlName(categoryData.title),
color: categoryData.color ?? "teal",
svgId: createdSvg.id,
}
await prisma.category
.create({
data: newCategory,
include: { svg: true, articles: true },
})
.then(
(createdCategory: CategoryWithIncludes | null) => {
if (createdCategory) {
res.json({ success: true, data: createdCategory });
} else {
res.json({ error: true, message: "Could not create category" });
}
},
(errorReason) => {
console.log(errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
},
(errorReason) => {
res.status(500).end(errorReason);
}
await prisma.category
.create({
data: newCategory,
include: { svg: true, articles: true },
})
.then(
(createdCategory: CategoryWithIncludes | null) => {
if (createdCategory) {
res.json({ success: true, data: createdCategory });
} else {
res.json({ error: true, message: "Could not create category" });
}
},
(errorReason) => {
console.log(errorReason)
if (errorReason.code === "P2002") {
res.json({ target: errorReason.meta.target[0], error: "Already exists" });
}
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
},
(errorReason) => {
res.status(500).end(errorReason);
}
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
)
.catch((err) => {
console.error(err);
res.status(500).end();
});
} else {
res.status(403).json({ error: true, message: "Authorization Required" });
}
}
}

View File

@@ -8,13 +8,14 @@ import prisma, { ArticleWithIncludes, CategoryWithIncludes } from "@/lib/prisma"
import ArticleControl from "../../../../components/ArticleControl";
import { IContentTableEntry } from "@/types/contentTable";
import { Prisma } from "@prisma/client";
import { useSession, signIn, signOut } from "next-auth/react";
import { CLIENT_RENEG_LIMIT } from "tls";
//* MAIN
export default function ArticlePage({ article }: { article: ArticleWithIncludes }) {
const dateUpdated: Date = new Date(article?.dateUpdated);
const dateCreated: Date = new Date(article?.dateCreated);
const dateOptions: Intl.DateTimeFormatOptions = { month: "long", day: "numeric", year: "numeric" };
return (
<>
<ArticleControl articleId={article.id} />