This commit is contained in:
Janis
2023-02-07 13:45:40 +01:00
parent d5f5b79140
commit d2ff34d3b6
70 changed files with 1388 additions and 8768 deletions

View File

@@ -1,97 +0,0 @@
"use client";
import { usePathname, useRouter } from "next/navigation";
import React, { useEffect, useState } from "react";
import styles from "../styles/modules/AdminControl.module.scss";
import "../styles/buttons.scss";
export default function AdminControl() {
const router = useRouter();
const [isArticle, setIsArticle] = useState(false);
const [isCategory, setIsCategory] = useState(false);
const [articleOrCategoryName, setArticleOrCategoryName] = useState("");
const [articleOrCategoryId, setArticleOrCategoryId] = useState();
const pathname = usePathname();
async function fetchDelete() {
const response = await fetch(
`/api/${isArticle ? "articles" : isCategory ? "categories" : ""}/${articleOrCategoryId}`,
{
method: "DELETE",
cache: "no-cache",
}
);
router.push("/articles/");
}
useEffect(() => {
function checkArticlePage() {
let path = pathname.split("/");
if (path.length == 4) {
if (path[1] == "articles") {
setIsArticle(true);
setArticleOrCategoryName(path[3]);
return;
}
}
setIsArticle(false);
}
function checkCategoryPage() {
let path = pathname.split("/");
if (path.length == 3) {
if (path[1] == "articles") {
setIsCategory(true);
setArticleOrCategoryName(path[2]);
return;
}
}
setIsCategory(false);
}
checkArticlePage();
checkCategoryPage();
}, [pathname]);
useEffect(() => {
async function getArticleOrCategoryId() {
const response = await fetch(
`/api/${isArticle ? "articles" : isCategory ? "categories" : ""}/name/${articleOrCategoryName}`,
{
method: "GET",
cache: "no-cache",
}
);
const json = await response.json();
setArticleOrCategoryId(json.id);
}
getArticleOrCategoryId();
}, [articleOrCategoryName]);
return (
<div className={styles.adminControl}>
{isArticle || isCategory ? (
<>
<button
className="danger"
onClick={() => {
fetchDelete();
}}
>
Delete this {isArticle ? "article" : "category"}
</button>
<button
className="warning"
onClick={() => {
router.push(`/admin/${isArticle ? "articles" : "categories"}/editor/${articleOrCategoryId}`);
}}
>
Edit this {isArticle ? "article" : "category"}
</button>
</>
) : (
""
)}
</div>
);
}

View File

@@ -1,13 +1,13 @@
import React from "react";
import Link from "next/link";
import styles from "../styles/modules/AdminNav.module.scss";
import styles from "@/styles/modules/AdminNav.module.scss";
function AdminNav() {
return (
<div className={styles.adminNav}>
<Link href={"/admin"}>Admin</Link>
<Link href={"/admin/articles/editor/0"}>New article</Link>
<Link href={"/admin/categories/editor/0"}>New category</Link>
<Link href={"/admin/editor/article/0"}>New article</Link>
<Link href={"/admin/editor/category/0"}>New category</Link>
</div>
);
}

View File

@@ -1,22 +1,31 @@
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";
export default function ContentTable({ contentTableData }: { contentTableData: any }) {
return (
<div className={styles.articleContentTable}>
<div className={styles.stickyContainer}>
<div className={styles.list}>
<h2>Contents</h2>
{contentTableData?.map((e, i) => {
return (
<a key={i} href={"#" + e.anchor}>
{e.title}
</a>
);
})}
</div>
{contentTableData?.length < 15 ? <div className={styles.adContainer}>Future advertisement</div> : ""}
</div>
</div>
);
export default function ContentTable({
contentTableData,
}: {
contentTableData: any;
}) {
return (
<div className={styles.articleContentTable}>
<div className={styles.stickyContainer}>
<div className={styles.list}>
<h2>Contents</h2>
{contentTableData?.map((e: IContentTableEntry, i: number) => {
return (
<a key={i} href={"#" + e.anchor}>
{e.title}
</a>
);
})}
</div>
{contentTableData?.length < 15 ? (
<div className={styles.adContainer}>Future advertisement</div>
) : (
""
)}
</div>
</div>
);
}

View File

@@ -1,5 +1,5 @@
import React from "react";
import styles from "../styles/modules/Footer.module.scss";
import styles from "@/styles/modules/Footer.module.scss";
import Image from "next/image";
export default function Footer() {
return (

View File

@@ -1,27 +0,0 @@
"use client";
import React, { useState } from "react";
import { Gallery as ReactGridGallery, Image as ImageType, ThumbnailImageProps } from "react-grid-gallery";
import Image from "next/image";
const ImageComponent = (props: ThumbnailImageProps) => {
const { src, alt, style, title } = props.imageProps;
const { width, height } = props.item;
return (
<Image
alt={alt}
src={src}
title={title || ""}
width={width}
height={height}
onClick={() => {
window.open(src);
}}
style={style}
/>
);
};
export default function Gallery({ images }: { images: ImageType[] }) {
return <ReactGridGallery images={images} enableImageSelection={false} thumbnailImageComponent={ImageComponent} />;
}

View File

@@ -1,32 +0,0 @@
/* eslint-disable @next/next/no-img-element */
"use client";
import React, { useState } from "react";
import { useRef } from "react";
export default function ImageUpload() {
const [selectedImage, setSelectedImage] = useState(null);
const inputRef = useRef<HTMLInputElement>(null);
const handleImageChange = (event) => {
setSelectedImage(URL.createObjectURL(event.target.files[0]));
};
async function uploadImage() {
if (selectedImage) {
const formData = new FormData();
formData.append("image", selectedImage);
const response = await fetch("/api/images/", {
method: "POST",
body: formData,
});
console.log(await response.json());
}
}
return (
<div>
<input onChange={handleImageChange} ref={inputRef} type="file" name="image" accept="image/*" />
{selectedImage && <img src={selectedImage} alt="Selected" />}
<button onClick={uploadImage}>Upload</button>
</div>
);
}

View File

@@ -2,8 +2,8 @@
import PropTypes from "prop-types";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import oneDark from "react-syntax-highlighter/dist/esm/styles/prism/one-dark";
import oneLight from "react-syntax-highlighter/dist/esm/styles/prism/one-light";
import oneDark from "react-syntax-highlighter/dist/cjs/styles/prism/one-dark";
import oneLight from "react-syntax-highlighter/dist/cjs/styles/prism/one-light";
import styles from "../styles/modules/markdown.module.scss";
import remarkGfm from "remark-gfm";
import remarkGemoji from "remark-gemoji";
@@ -13,11 +13,11 @@ import React from "react";
import { formatTextToUrlName } from "../utils";
function flatten(text, child) {
function flatten(text: string, child: any): any {
return typeof child === "string" ? text + child : React.Children.toArray(child.props.children).reduce(flatten, text);
}
function HeadingRenderer({ children, level }) {
function HeadingRenderer({ children, level }: { children: any; level: any }) {
children = React.Children.toArray(children);
const text = children.reduce(flatten, "");
return React.createElement("h" + level, { id: formatTextToUrlName(text) }, children);
@@ -55,7 +55,13 @@ export default function Markdown({ value }: { value: any }) {
</div>
</div>
<SyntaxHighlighter style={oneDark} language={match ? match[1] : ""} PreTag="div" {...props}>
<SyntaxHighlighter
// @ts-ignore
style={oneDark}
language={match ? match[1] : ""}
PreTag="div"
{...props}
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
</>

View File

@@ -1,135 +1,149 @@
"use client";
import styles from "../styles/modules/Nav.module.scss";
import styles from "@/styles/modules/Nav.module.scss";
import Image from "next/image";
import Link from "next/link";
import { useEffect, useState } from "react";
import { Category } from "@prisma/client";
function switchTheme(theme) {
const bodyElement = document.getElementsByTagName("body")[0];
function switchTheme(theme: string) {
const bodyElement = document.getElementsByTagName("body")[0];
if (theme == "dark") {
bodyElement.classList.remove("theme-light");
} else {
bodyElement.classList.add("theme-light");
}
if (theme == "dark") {
bodyElement.classList.remove("theme-light");
} else {
bodyElement.classList.add("theme-light");
}
}
function toggleTheme() {
const svgElement = document.getElementById("themeSwitchSvg");
const svgElement = document.getElementById("themeSwitchSvg");
if (localStorage.getItem("theme") == "light") {
svgElement.style.animationDirection = "normal";
svgElement.style.animationName = styles.spinThemeSwitch;
} else {
svgElement.style.animationDirection = "reverse";
svgElement.style.animationName = styles.spinThemeSwitch;
}
if (svgElement) {
if (localStorage.getItem("theme") == "light") {
svgElement.style.animationDirection = "normal";
svgElement.style.animationName = styles.spinThemeSwitch;
} else {
svgElement.style.animationDirection = "reverse";
svgElement.style.animationName = styles.spinThemeSwitch;
}
setTimeout(() => {
if (localStorage.getItem("theme") == "light") {
localStorage.setItem("theme", "dark");
switchTheme("dark");
} else {
localStorage.setItem("theme", "light");
switchTheme("light");
}
svgElement.style.animationName = "";
}, 150);
setTimeout(() => {
if (localStorage.getItem("theme") == "light") {
localStorage.setItem("theme", "dark");
switchTheme("dark");
} else {
localStorage.setItem("theme", "light");
switchTheme("light");
}
svgElement.style.animationName = "";
}, 150);
}
}
export default function Nav({ categories }: { categories: Category[] }) {
const [searchResults, setSearchResults] = useState([]);
const [searchResults, setSearchResults] = useState<
{ name: string; title: string }[]
>([]);
async function handleSearchInput(event) {
const query = event.target.value;
let result = await fetch(`/api/search?q=${query}`);
let json = await result.json();
async function handleSearchInput(event: React.ChangeEvent<HTMLInputElement>) {
const query = event.target.value;
let result = await fetch(`/api/search?q=${query}`);
let json = await result.json();
if (json.length == 0 && query.length > 0) {
setSearchResults([{ name: "", title: "No article found..." }]);
} else {
setSearchResults(json);
}
}
if (json.length == 0 && query.length > 0) {
setSearchResults([{ name: "", title: "No article found..." }]);
} else {
setSearchResults(json);
}
}
useEffect(() => {
if (localStorage.getItem("theme") == "dark") {
switchTheme("dark");
} else {
switchTheme("light");
}
}, []);
useEffect(() => {
if (localStorage.getItem("theme") == "dark") {
switchTheme("dark");
} else {
switchTheme("light");
}
}, []);
useEffect(() => {
console.log(searchResults);
}, [searchResults]);
useEffect(() => {
console.log(searchResults);
}, [searchResults]);
return (
<nav className={styles.nav}>
<div className={styles.containerLeft}>
<Image
src={"/images/logo.svg"}
height={40}
width={160}
alt="Nav bar logo"
onClick={() => {
window.open("/", "_self");
}}
className={styles.logo}
/>
<div className={styles.links}>
<div className={styles.dropDown}>
<Link href="/articles">Categories</Link>
<div className={styles.dropDownContainer}>
<div className={styles.content}>
<Link href={"/articles"}>All</Link>
return (
<nav className={styles.nav}>
<div className={styles.containerLeft}>
<Image
src={"/images/logo.svg"}
height={40}
width={160}
alt="Nav bar logo"
onClick={() => {
window.open("/", "_self");
}}
className={styles.logo}
/>
<div className={styles.links}>
<div className={styles.dropDown}>
<Link href="/articles">Categories</Link>
<div className={styles.dropDownContainer}>
<div className={styles.content}>
<Link href={"/articles"}>All</Link>
{categories?.map((cat, i) => {
{
return (
<Link key={i} href={`/articles/${cat.name.toLowerCase()}`}>
{cat.title}
</Link>
);
}
})}
</div>
</div>
</div>
</div>
</div>
<div className={styles.containerCenter}>
<div className={styles.searchBar}>
<div className={styles.icon}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z" />
</svg>
</div>
<input onInput={handleSearchInput} type="text" name="" id="" />
</div>
<div className={styles.searchResults}>
<div className={styles.content}>
{searchResults.map((s) => {
{
return <Link href={`/articles/${s.name.toLowerCase()}`}>{s.title}</Link>;
}
})}
</div>
</div>
</div>
<div className={styles.containerRight}>
<div
className={styles.themeSwitch}
onClick={() => {
toggleTheme();
}}
>
<svg 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" />
</svg>
</div>
</div>
</nav>
);
{categories?.map((cat, i) => {
{
return (
<Link
key={i}
href={`/articles/${cat.name.toLowerCase()}`}
>
{cat.title}
</Link>
);
}
})}
</div>
</div>
</div>
</div>
</div>
<div className={styles.containerCenter}>
<div className={styles.searchBar}>
<div className={styles.icon}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z" />
</svg>
</div>
<input onInput={handleSearchInput} type="text" name="" id="" />
</div>
<div className={styles.searchResults}>
<div className={styles.content}>
{searchResults.map((s) => {
{
return (
<Link href={`/articles/${s.name.toLowerCase()}`}>
{s.title}
</Link>
);
}
})}
</div>
</div>
</div>
<div className={styles.containerRight}>
<div
className={styles.themeSwitch}
onClick={() => {
toggleTheme();
}}
>
<svg
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" />
</svg>
</div>
</div>
</nav>
);
}

View File

@@ -1,28 +1,28 @@
import React from "react";
import styles from "../styles/modules/Sidebar.module.scss";
import styles from "@/styles/modules/Sidebar.module.scss";
export default function Sidebar() {
return (
<div className={styles.sidebar}>
<div className={styles.stickyContainer}>
<div className={styles.sidebarContainer}>
<h3>Popular</h3>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
</div>
return (
<div className={styles.sidebar}>
<div className={styles.stickyContainer}>
<div className={styles.sidebarContainer}>
<h3>Popular</h3>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
</div>
<div className={styles.sidebarContainer}>
<h3>Related</h3>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
</div>
<div className={styles.adContainer}>Future advertisement</div>
</div>
</div>
);
<div className={styles.sidebarContainer}>
<h3>Related</h3>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
<a href="#"> Set up Docker</a>
</div>
<div className={styles.adContainer}>Future advertisement</div>
</div>
</div>
);
}