This commit is contained in:
Janis
2023-02-07 15:42:40 +01:00
parent d2ff34d3b6
commit 29f359610f
16 changed files with 421 additions and 264 deletions

View File

@@ -6,12 +6,14 @@ import type { AppProps } from "next/app";
import AdminNav from "@/components/AdminNav";
import Footer from "@/components/Footer";
import Nav from "@/components/Nav";
import { Category } from "@prisma/client";
import prisma from "@/lib/prisma";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<header>
<Nav categories={[]} />
<Nav />
<AdminNav />
</header>
<Component {...pageProps} />

View File

@@ -2,11 +2,10 @@ import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="en">
<Html lang="en" style={{ scrollBehavior: "smooth" }}>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>

View File

@@ -44,7 +44,7 @@ export default function AdminArticlesEditorPage({ article, categories }: { artic
function changeContentTableEntryTitle(index: number, newTitle: string) {
setContentTable((prevArray: any) => {
let newArray = [...prevArray];
newArray[index].anchor = newTitle;
newArray[index].title = newTitle;
return newArray;
});
}

View File

@@ -1,19 +1,19 @@
import { Prisma, Article } from "@prisma/client";
import { ResponseError } from "../../../types/responseErrors";
import { formatTextToUrlName } from "../../../utils";
import prisma from "../../../lib/prisma";
import prisma, { ArticleWithIncludes } from "../../../lib/prisma";
import type { NextApiRequest, NextApiResponse } from 'next'
import { UpdateArticle } from "../../../types/api";
import { isValidText } from "../../../validators";
type ArticleWithIncludes = Prisma.ArticleGetPayload<{ include: { contentTableEntries: true, category: true, image: true } }>
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 articleId: string = formatTextToUrlName(req.query?.articleId?.toString() ?? "")
console.log(`API articleId: ${articleId}`)
const articleData: UpdateArticle = req.body;
@@ -55,6 +55,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
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)
})
}
}

View File

@@ -1,4 +1,4 @@
import prisma from "../../../lib/prisma";
import prisma, { CategoryWithIncludes } from "../../../lib/prisma";
import { Category } from "@prisma/client";
import { ResponseError } from "../../../types/responseErrors";
import { Prisma } from "@prisma/client";
@@ -9,10 +9,10 @@ import { isValidText } from "../../../validators";
import { UpdateCategory } from '../../../types/api';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const categoryId: string = req.query.categoryId?.toString() ?? "";
if (req.method == "PUT") {
const categoryId: string = req.query.categoryId?.toString() ?? "";
const categoryData: UpdateCategory = req.body;
if (categoryData.title && !isValidText(categoryData.title)) {
@@ -66,5 +66,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
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)
})
}
}

View File

@@ -10,66 +10,77 @@ import type { NextApiRequest, NextApiResponse } from 'next'
import { CreateCategory } from "@/types/api";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method == "POST") {
console.log("API new category")
const categoryData: CreateCategory = req.body;
console.log(categoryData)
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") {
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();
});
}
}

30
pages/api/search.ts Normal file
View 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([]);
}
}

View File

@@ -5,7 +5,9 @@ import Image from "next/image";
import Markdown from "@/components/Markdown";
import { formatTextToUrlName } from "@/utils";
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
export default function ArticlePage({ article }: { article: ArticleWithIncludes }) {
@@ -14,27 +16,32 @@ export default function ArticlePage({ article }: { article: ArticleWithIncludes
const dateOptions: Intl.DateTimeFormatOptions = { month: "long", day: "numeric", year: "numeric" };
return (
<div className={styles.article}>
<ContentTable contentTableData={article?.contentTable ? article?.contentTable : []} />
<div className={styles.tutorialContent}>
<div className={styles.header}>
<p className={`${styles.dates} text-muted`}>
{`Published on ${dateCreated.toLocaleDateString("en-US", dateOptions)}`}
<br />
{dateUpdated > dateCreated ? `Updated on ${dateUpdated.toLocaleDateString("en-US", dateOptions)}` : ""}
</p>
<>
<ArticleControl articleId={article.id} />
<div className={styles.article}>
<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.header}>
<p className={`${styles.dates} text-muted`}>
{`Published on ${dateCreated.toLocaleDateString("en-US", dateOptions)}`}
<br />
{dateUpdated > dateCreated ? `Updated on ${dateUpdated.toLocaleDateString("en-US", dateOptions)}` : ""}
</p>
<h1>{article?.title}</h1>
<div className={styles.tags}>
<a href="#">Docker</a> <a href="#">Setup</a> <a href="#">Ubuntu</a>
<h1>{article?.title}</h1>
<div className={styles.tags}>
<a href="#">Docker</a> <a href="#">Setup</a> <a href="#">Ubuntu</a>
</div>
<Image src={""} height={350} width={750} alt={""} quality={100} placeholder="blur" blurDataURL="/images/blur.png" loading="lazy" />
<p>{article?.introduction}</p>
</div>
<Image src={""} height={350} width={750} alt={""} quality={100} placeholder="blur" blurDataURL="/images/blur.png" loading="lazy" />
<p>{article?.introduction}</p>
<Markdown value={article?.markdown ?? ""} />
</div>
<Markdown value={article?.markdown ?? ""} />
<Sidebar />
</div>
<Sidebar />
</div>
</>
);
}

View File

@@ -3,15 +3,18 @@ import Link from "next/link";
import { formatTextToUrlName } from "@/utils";
import { Article, Category } from "@prisma/client";
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 (
<div className={styles.category}>
<h1>{category?.title}</h1>
<div className={styles.content}>
<div className={`${styles.showcase} ${styles.smallShowcase}`}>
<h2>Most popular articles</h2>
{/* {popularArticles?.map((a, i) => {
<>
<CategoryControl categoryId={category.id} />
<div className={styles.category}>
<h1>{category?.title}</h1>
<div className={styles.content}>
<div className={`${styles.showcase} ${styles.smallShowcase}`}>
<h2>Most popular articles</h2>
{/* {popularArticles?.map((a, i) => {
{
return (
<Link key={i} href={`/articles/${category.name}/${a.name}`}>
@@ -20,9 +23,9 @@ export default function CategoryPage({ category }: { category: CategoryWithInclu
);
}
})} */}
</div>
</div>
{/* <div className={`${styles.showcase} ${styles.smallShowcase}`}>
{/* <div className={`${styles.showcase} ${styles.smallShowcase}`}>
<h2>Most recent articles</h2>
{recentArticles?.map((a, i) => {
{
@@ -35,22 +38,23 @@ export default function CategoryPage({ category }: { category: CategoryWithInclu
})}
</div> */}
<div className={styles.showcase}>
<h2>All articles</h2>
{category?.articles
? Array.from(category?.articles).map((a: Article, i: number) => {
{
return (
<Link key={i} href={`/articles/${category.name}/${a.name}`}>
{a.title}
</Link>
);
}
})
: ""}
<div className={styles.showcase}>
<h2>All articles</h2>
{category?.articles
? Array.from(category?.articles).map((a: Article, i: number) => {
{
return (
<Link key={i} href={`/articles/${category.name}/${a.name}`}>
{a.title}
</Link>
);
}
})
: ""}
</div>
</div>
</div>
</div>
</>
);
}
export async function getServerSideProps(context: any) {