More progress
This commit is contained in:
parent
7e2e58f7e6
commit
409bc2c3cc
26 changed files with 1459 additions and 146 deletions
|
@ -1,4 +1,11 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
const nextConfig = {
|
||||
images: {
|
||||
domains:
|
||||
["lh3.googleusercontent.com"]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
// 3:21:40
|
||||
|
|
930
package-lock.json
generated
930
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -9,15 +9,17 @@
|
|||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/prisma-adapter": "^2.4.1",
|
||||
"@auth/prisma-adapter": "^2.4.2",
|
||||
"@prisma/client": "^5.17.0",
|
||||
"eslint": "8.48.0",
|
||||
"eslint-config-next": "^14.2.5",
|
||||
"firebase": "^10.12.5",
|
||||
"next": "^14.2.5",
|
||||
"next-auth": "^4.24.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-quill": "^2.0.0"
|
||||
"react-quill": "^2.0.0",
|
||||
"swr": "^2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prisma": "^5.17.0"
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
@ -8,25 +6,10 @@ datasource db {
|
|||
provider = "mongodb"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(auto) @map("_id") @db.ObjectId
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
// Optional for WebAuthn support
|
||||
Authenticator Authenticator[]
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
|
||||
model Account {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
userId String
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
|
@ -37,31 +20,69 @@ model Account {
|
|||
scope String?
|
||||
id_token String?
|
||||
session_state String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
name String?
|
||||
email String @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
Post Post[]
|
||||
Comment Comment[]
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
id String @id @map("_id")
|
||||
identifier String
|
||||
token String
|
||||
identifier String @id @map("_id")
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
|
||||
model Category {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
slug String @unique
|
||||
title String
|
||||
img String?
|
||||
Posts Post[]
|
||||
}
|
||||
|
||||
model Post {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
createdAt DateTime @default(now())
|
||||
slug String @unique
|
||||
title String
|
||||
desc String
|
||||
img String?
|
||||
views Int @default(0)
|
||||
catSlug String
|
||||
cat Category @relation(fields: [catSlug], references: [slug])
|
||||
userEmail String
|
||||
user User @relation(fields: [userEmail], references: [email])
|
||||
comments Comment[]
|
||||
}
|
||||
|
||||
model Comment {
|
||||
id String @id @default(cuid()) @map("_id")
|
||||
createdAt DateTime @default(now())
|
||||
desc String
|
||||
userEmail String
|
||||
user User @relation(fields: [userEmail], references: [email])
|
||||
postSlug String
|
||||
post Post @relation(fields: [postSlug], references: [slug])
|
||||
}
|
|
@ -8,7 +8,7 @@ const SinglePage = () => {
|
|||
<div className={styles.container}>
|
||||
<div className={styles.infoContainer}>
|
||||
<div className={styles.textContainer}>
|
||||
<h1>Lorem ipsum dolor sit amet consectetur adipiscing elit.</h1>
|
||||
<h1 className={styles.title}>Lorem ipsum dolor sit amet consectetur adipiscing elit.</h1>
|
||||
<div className={styles.user}>
|
||||
<div className={styles.userImageContainer}>
|
||||
<Image src="/p1.jpeg" fill className={styles.image}/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { authOptions } from "@/utils/auth"
|
||||
import NextAuth from "next-auth"
|
||||
import { authOptions } from "@/utils/auth";
|
||||
import NextAuth from "next-auth";
|
||||
|
||||
|
||||
const handler = NextAuth(authOptions);
|
||||
|
|
15
src/app/api/categories/route.js
Normal file
15
src/app/api/categories/route.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import prisma from "@/utils/connect";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export const GET = async ()=>{
|
||||
try{
|
||||
|
||||
const categories = await prisma.category.findMany();
|
||||
return new NextResponse(JSON.stringify(categories, {status:200}));
|
||||
|
||||
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
return new NextResponse(JSON.stringify({message: "Something went wrong!"}, {status:500}));
|
||||
}
|
||||
};
|
51
src/app/api/comments/route.js
Normal file
51
src/app/api/comments/route.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { getAuthSession } from "@/utils/auth";
|
||||
import prisma from "@/utils/connect";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
// GET ALL COMMENTS OF A POST
|
||||
export const GET = async (req) => {
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
const postSlug = searchParams.get("postSlug");
|
||||
|
||||
try {
|
||||
const comments = await prisma.comment.findMany({
|
||||
where: {
|
||||
...(postSlug && { postSlug }),
|
||||
},
|
||||
include: { user: true },
|
||||
});
|
||||
|
||||
return new NextResponse(JSON.stringify(comments, { status: 200 }));
|
||||
} catch (err) {
|
||||
// console.log(err);
|
||||
return new NextResponse(
|
||||
JSON.stringify({ message: "Something went wrong!" }, { status: 500 })
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// CREATE A COMMENT
|
||||
export const POST = async (req) => {
|
||||
const session = await getAuthSession();
|
||||
|
||||
if (!session) {
|
||||
return new NextResponse(
|
||||
JSON.stringify({ message: "Not Authenticated!" }, { status: 401 })
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await req.json();
|
||||
const comment = await prisma.comment.create({
|
||||
data: { ...body, userEmail: session.user.email },
|
||||
});
|
||||
|
||||
return new NextResponse(JSON.stringify(comment, { status: 200 }));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return new NextResponse(
|
||||
JSON.stringify({ message: "Something went wrong!" }, { status: 500 })
|
||||
);
|
||||
}
|
||||
};
|
22
src/app/api/posts/[slug]/route.js
Normal file
22
src/app/api/posts/[slug]/route.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import prisma from "@/utils/connect";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
|
||||
// GET SINGLE POST
|
||||
export const GET = async (req, {params})=>{
|
||||
|
||||
const {slug} = params
|
||||
|
||||
try{
|
||||
|
||||
const post = await prisma.post.findUnique({
|
||||
where: {slug},
|
||||
include: {user: true},
|
||||
});
|
||||
return new NextResponse(JSON.stringify(post, {status: 200}));
|
||||
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
return new NextResponse(JSON.stringify({message: "Something went wrong!"}, {status:500}));
|
||||
}
|
||||
};
|
34
src/app/api/posts/route.js
Normal file
34
src/app/api/posts/route.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import prisma from "@/utils/connect";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export const GET = async (req)=>{
|
||||
|
||||
const {searchParams} = new URL(req.url);
|
||||
const page = searchParams.get("page");
|
||||
const cat = searchParams.get("cat");
|
||||
const POST_PER_PAGE = 2;
|
||||
|
||||
const query = {
|
||||
take: POST_PER_PAGE,
|
||||
skip: POST_PER_PAGE * (page - 1),
|
||||
where: {
|
||||
...(cat && {catSlug: cat}),
|
||||
},
|
||||
};
|
||||
|
||||
try{
|
||||
|
||||
const [posts,count] = await prisma.$transaction(
|
||||
[
|
||||
prisma.post.findMany(query),
|
||||
prisma.post.count({where:query.where}),
|
||||
]
|
||||
);
|
||||
return new NextResponse(JSON.stringify({posts, count}, {status:200}));
|
||||
|
||||
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
return new NextResponse(JSON.stringify({message: "Something went wrong!"}, {status:500}));
|
||||
}
|
||||
};
|
|
@ -1,12 +1,9 @@
|
|||
.container {
|
||||
|
||||
}
|
||||
|
||||
.title{
|
||||
background-color: coral;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.content{
|
||||
|
|
|
@ -3,12 +3,15 @@ import styles from "./blogPage.module.css";
|
|||
import CardList from '@/components/cardList/CardList';
|
||||
import Menu from '@/components/Menu/Menu';
|
||||
|
||||
const BlogPage = () => {
|
||||
const BlogPage = ({searchParams}) => {
|
||||
|
||||
const page = parseInt(searchParams.page) || 1;
|
||||
const { cat } = searchParams;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Style Blog</h1>
|
||||
<h1 className={styles.title}>{cat} Blog</h1>
|
||||
<div className={styles.content}>
|
||||
<CardList/>
|
||||
<CardList page={page} cat={cat}/>
|
||||
<Menu/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,12 +8,13 @@ const LoginPage = () => {
|
|||
const {data, status} = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
console.log(data,status);
|
||||
|
||||
if (status === "loading") {
|
||||
return <div className={styles.loading}>Loading...</div>
|
||||
}
|
||||
if (status === "authenticated") {
|
||||
router.push("/");
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
|
|
|
@ -5,13 +5,16 @@ import CardList from "@/components/cardList/CardList";
|
|||
import CategoryList from "@/components/categoryList/CategoryList";
|
||||
import Menu from "@/components/Menu/Menu";
|
||||
|
||||
export default function Home() {
|
||||
export default function Home({searchParams}) {
|
||||
|
||||
const page = parseInt(searchParams.page) || 1;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Featured/>
|
||||
<CategoryList/>
|
||||
<div className={styles.content}>
|
||||
<CardList/>
|
||||
<CardList page={page}/>
|
||||
<Menu/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
61
src/app/posts/[slug]/page.jsx
Normal file
61
src/app/posts/[slug]/page.jsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import styles from "./singlePage.module.css";
|
||||
import Menu from "@/components/Menu/Menu";
|
||||
import Image from "next/image";
|
||||
import Comments from "@/components/comments/Comments";
|
||||
|
||||
const getData = async (slug) => {
|
||||
const res = await fetch(
|
||||
`http://localhost:3000/api/posts/${slug}`,
|
||||
{
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Failed");
|
||||
}
|
||||
|
||||
return res.json();
|
||||
};
|
||||
|
||||
const SinglePage = async ({params}) => {
|
||||
|
||||
const {slug} = params;
|
||||
|
||||
const data = await getData(slug);
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.infoContainer}>
|
||||
<div className={styles.textContainer}>
|
||||
<h1 className={styles.title}>
|
||||
{data?.title}
|
||||
</h1>
|
||||
<div className={styles.user}>
|
||||
{data?.user?.image && <div className={styles.userImageContainer}>
|
||||
<Image src={data.user.image} fill className={styles.image}/>
|
||||
</div>}
|
||||
<div className={styles.userTextContainer}>
|
||||
<span className={styles.username}>{data?.user.name}</span>
|
||||
<span className={styles.date}>2024.07.19</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{ data?.img && <div className={styles.imageContainer}>
|
||||
<Image src={data.img} fill className={styles.image}/>
|
||||
</div>}
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.post}>
|
||||
<div className={styles.description}
|
||||
dangerouslySetInnerHTML={{ __html:data?.desc }}/>
|
||||
<div className={styles.comment}>
|
||||
<Comments postSlug={slug}/>
|
||||
</div>
|
||||
</div>
|
||||
<Menu/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SinglePage;
|
101
src/app/posts/[slug]/singlePage.module.css
Normal file
101
src/app/posts/[slug]/singlePage.module.css
Normal file
|
@ -0,0 +1,101 @@
|
|||
.container{
|
||||
|
||||
}
|
||||
|
||||
.infoContainer{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 50px;
|
||||
}
|
||||
|
||||
.textContainer{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title{
|
||||
font-size: 64px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.user{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.userImageContainer{
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar{
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.userTextContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
color: var(--softTextColor);
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.imageContainer{
|
||||
flex: 1;
|
||||
height: 350px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image{
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
gap: 50px;
|
||||
}
|
||||
|
||||
.post {
|
||||
flex: 5;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.description p{
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1536px) {
|
||||
.title {
|
||||
font-size: 54px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1280px) {
|
||||
.title {
|
||||
font-size: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1024px) {
|
||||
.imageContainer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.title {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.description p{
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
|
@ -5,12 +5,24 @@ import Image from "next/image";
|
|||
import { useState } from "react";
|
||||
import ReactQuill from "react-quill";
|
||||
import "react-quill/dist/quill.bubble.css";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
const WritePage = () => {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const {status} = useSession();
|
||||
const router = useRouter();
|
||||
const [file, setFile] = useState(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
if (status === "loading") {
|
||||
return <div className={styles.loading}>Loading...</div>
|
||||
}
|
||||
if (status === "authenticated") {
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<input type="text" placeholder="Title" className={styles.input}/>
|
||||
|
@ -20,9 +32,19 @@ const WritePage = () => {
|
|||
</button>
|
||||
{open && (
|
||||
<div className={styles.add}>
|
||||
<input
|
||||
type="file"
|
||||
id="image"
|
||||
onChange={e=>setFile(e.target.files[0])}
|
||||
style={ { display: "none" }}
|
||||
/>
|
||||
|
||||
<button className={styles.addButton}>
|
||||
<Image src="/image.png" alt="" width={16} height={16} />
|
||||
<label htmlFor="image">
|
||||
<Image src="/image.png" alt="" width={16} height={16} />
|
||||
</label>
|
||||
</button>
|
||||
|
||||
|
||||
<button className={styles.addButton}>
|
||||
<Image src="/external.png" alt="" width={16} height={16} />
|
||||
|
|
|
@ -28,7 +28,7 @@ const AuthLinks = () => {
|
|||
<Link href="/">Homepage</Link>
|
||||
<Link href="/">About</Link>
|
||||
<Link href="/">Contact</Link>
|
||||
{status==="notauthenticated" ? (
|
||||
{status==="unauthenticated" ? (
|
||||
<Link href="/login">Login</Link>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
@ -2,28 +2,29 @@ import styles from "./card.module.css";
|
|||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
const Card = () => {
|
||||
const Card = ({key, item}) => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.container} key={key}>
|
||||
{item.img &&
|
||||
<div className={styles.imageContainer}>
|
||||
<Image src="/p1.jpeg" alt="" fill className={styles.image}/>
|
||||
</div>
|
||||
<Image src={item.img} alt="" fill className={styles.image}/>
|
||||
</div>}
|
||||
<div className={styles.textContainer}>
|
||||
<div className={styles.detail}>
|
||||
<span className={styles.date}>
|
||||
2023.02.11 -
|
||||
{item.createdAt.substring(0,10)} -{" "}
|
||||
</span>
|
||||
<span className={styles.category}>
|
||||
CULTURE
|
||||
{item.catSlug}
|
||||
</span>
|
||||
</div>
|
||||
<Link href="/">
|
||||
<h1>Lorem ipsum dolor sit amet consectetur adipiscing elit.</h1>
|
||||
<Link href={`/post/${item.slug}`}>
|
||||
<h1>{item.title}</h1>
|
||||
</Link>
|
||||
<p className={styles.desc}>
|
||||
Some more Lorem Ipsum xD.
|
||||
{item.desc.substring(0,60)}
|
||||
</p>
|
||||
<Link href="/" className={styles.link}>
|
||||
<Link href={`/post/${item.slug}`} className={styles.link}>
|
||||
Read More
|
||||
</Link>
|
||||
</div>
|
||||
|
|
|
@ -1,22 +1,43 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
import styles from "./cardlist.module.css";
|
||||
import Pagination from '../pagination/Pagination';
|
||||
import Pagination from "../pagination/Pagination";
|
||||
import Image from "next/image";
|
||||
import Card from '../card/Card';
|
||||
import Card from "../card/Card";
|
||||
|
||||
const CardList = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1>Recent Posts</h1>
|
||||
<div className={styles.posts}>
|
||||
<Card />
|
||||
<Card />
|
||||
<Card />
|
||||
<Card />
|
||||
</div>
|
||||
<Pagination/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const getData = async (page, cat) => {
|
||||
const res = await fetch(
|
||||
`http://localhost:3000/api/posts?page=${page}&cat=${cat || ""}`,
|
||||
{
|
||||
cache: "no-store",
|
||||
}
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Failed");
|
||||
}
|
||||
|
||||
return res.json();
|
||||
};
|
||||
|
||||
const CardList = async ({ page, cat }) => {
|
||||
const { posts, count } = await getData(page, cat);
|
||||
|
||||
const POST_PER_PAGE = 2;
|
||||
|
||||
const hasPrev = POST_PER_PAGE * (page - 1) > 0;
|
||||
const hasNext = POST_PER_PAGE * (page - 1) + POST_PER_PAGE < count;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Recent Posts</h1>
|
||||
<div className={styles.posts}>
|
||||
{posts?.map((item) => (
|
||||
<Card item={item} key={item._id} />
|
||||
))}
|
||||
</div>
|
||||
<Pagination page={page} hasPrev={hasPrev} hasNext={hasNext} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardList;
|
|
@ -3,40 +3,32 @@ import styles from "./categoryList.module.css";
|
|||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
const CategoryList = () => {
|
||||
const getData = async ()=> {
|
||||
const res = await fetch("http://localhost:3000/api/categories", {cache: "no-store"})
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Failed")
|
||||
}
|
||||
|
||||
return res.json()
|
||||
}
|
||||
|
||||
const CategoryList = async () => {
|
||||
|
||||
const data = await getData();
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Categories</h1>
|
||||
<div className={styles.categories}>
|
||||
<Link href="/blog?cat=style" className={`${styles.category} ${styles.style}`}>
|
||||
<Image src="/style.png" alt="" width={32} height={32} className={styles.image} />
|
||||
style
|
||||
</Link>
|
||||
{data?.map(item=>(
|
||||
|
||||
<Link href={`/blog`} className={`${styles.category} ${styles.fashion}`}>
|
||||
<Image src="/fashion.png" alt="" width={32} height={32} className={styles.image} />
|
||||
philosophy
|
||||
<Link href="/blog?cat=style" className={`${styles.category} ${styles[item.slug]}`} key={item._id}>
|
||||
{item.img && (<Image src={item.img} alt="" width={32} height={32} className={styles.image} />)}
|
||||
{item.title}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
<Link href={`/blog`} className={`${styles.category} ${styles.food}`}>
|
||||
<Image src="/food.png" alt="" width={32} height={32} className={styles.image} />
|
||||
food
|
||||
</Link>
|
||||
|
||||
<Link href={`/blog`} className={`${styles.category} ${styles.travel}`}>
|
||||
<Image src="/travel.png" alt="" width={32} height={32} className={styles.image} />
|
||||
travel
|
||||
</Link>
|
||||
|
||||
<Link href={`/blog`} className={`${styles.category} ${styles.culture}`}>
|
||||
<Image src="/culture.png" alt="" width={32} height={32} className={styles.image} />
|
||||
culture
|
||||
</Link>
|
||||
|
||||
<Link href={`/blog`} className={`${styles.category} ${styles.coding}`}>
|
||||
<Image src="/coding.png" alt="" width={32} height={32} className={styles.image} />
|
||||
coding
|
||||
</Link>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,34 +1,64 @@
|
|||
"use client"
|
||||
import styles from "./comments.module.css"
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import useSWR from "swr";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useState } from "react";
|
||||
|
||||
const Comments = () => {
|
||||
const status = "authenticated";
|
||||
const fetcher = async (url) => {
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
const error = new Error(data.message);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const Comments = ({postSlug}) => {
|
||||
const {status} = useSession();
|
||||
const {data, mutate, isLoading} = useSWR(`http://localhost:3000/api/comments?postSlug=${postSlug}`, fetcher);
|
||||
|
||||
const [desc, setDesc] = useState("");
|
||||
const handleSubmit = async () => {
|
||||
await fetch("/api/comments", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({desc, postSlug}),
|
||||
})
|
||||
mutate();
|
||||
}
|
||||
|
||||
console.log("Fetched data:", data);
|
||||
console.log("Session status:", status);
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Comments</h1>
|
||||
{status === "authenticated" ? (
|
||||
<div className={styles.write}>
|
||||
<textarea placeholder="write a comment..." className={styles.input}/>
|
||||
<button className={styles.button}>Send</button>
|
||||
<textarea placeholder="write a comment..." className={styles.input} onChange={e=>setDesc(e.target.value)}/>
|
||||
<button className={styles.button} onClick={handleSubmit}>Send</button>
|
||||
</div>
|
||||
) : (
|
||||
<Link href="/login">Login to write a comment</Link>)}
|
||||
<div className={styles.comments}>
|
||||
<div className={styles.comment}>
|
||||
<div className={styles.user}>
|
||||
<Image src="/p1.jpeg" alt="" width={50} height={50} className={styles.image}/>
|
||||
<div className={styles.userInfo}>
|
||||
<span className={styles.username}>Max Mustermann</span>
|
||||
<span className={styles.date}>2024.02.01</span>
|
||||
{isLoading ? "loading" : Array.isArray(data) ? data.map((item)=> (
|
||||
<div className={styles.comment} key={item._id}>
|
||||
<div className={styles.user}>
|
||||
{item?.user?.image && (<Image src={item.user.image} alt="" width={50} height={50} className={styles.image}/>)}
|
||||
<div className={styles.userInfo}>
|
||||
<span className={styles.username}>{item.user.name}</span>
|
||||
<span className={styles.date}>{item.createdAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className={styles.desc}>{item.desc}</p>
|
||||
</div>
|
||||
<p className={styles.desc}>Lorem ipsum oder etwas in die Richtung</p>
|
||||
</div>
|
||||
)) : null}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Comments;
|
|
@ -1,11 +1,23 @@
|
|||
"use client";
|
||||
import React from 'react';
|
||||
import styles from "./pagination.module.css";
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const Pagination = ({page, hasPrev, hasNext}) => {
|
||||
const router = useRouter();
|
||||
|
||||
const Pagination = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<button className={styles.button}>Malantaŭen</button>
|
||||
<button className={styles.button}>Antaŭen</button>
|
||||
<button className={styles.button}
|
||||
disabled={!hasPrev}
|
||||
onClick={()=>router.push(`?page=${page-1}`)}>
|
||||
Malantaŭen
|
||||
</button>
|
||||
<button className={styles.button}
|
||||
disabled={!hasNext}
|
||||
onClick={()=>router.push(`?page=${page+1}`)}>
|
||||
Antaŭen
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,4 +10,9 @@
|
|||
background-color: crimson;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:disabled{
|
||||
background-color: rgba(220, 20, 60, 0.473);
|
||||
cursor: not-allowed;
|
||||
}
|
|
@ -6,7 +6,7 @@ import { useContext } from "react";
|
|||
import { ThemeContext } from "@/context/ThemeContext";
|
||||
|
||||
const ThemeToggle = () => {
|
||||
const {toggle, theme} = useContext(ThemeContext)
|
||||
const {toggle, theme} = useContext(ThemeContext);
|
||||
|
||||
return <div className={styles.container} onClick={toggle} style={
|
||||
theme === "dark"
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||
import NextAuth from "next-auth/next"
|
||||
import GithubProvider from "next-auth/providers/github"
|
||||
import GoogleProvider from "next-auth/providers/google"
|
||||
import prisma from "./connect"
|
||||
import { PrismaAdapter } from "@auth/prisma-adapter";
|
||||
import GithubProvider from "next-auth/providers/github";
|
||||
import GoogleProvider from "next-auth/providers/google";
|
||||
import prisma from "./connect";
|
||||
import { getServerSession } from "next-auth";
|
||||
|
||||
export const authOptions = {
|
||||
adapters: PrismaAdapter(prisma),
|
||||
// Configure one or more authentication providers
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_ID,
|
||||
clientSecret: process.env.GOOGLE_SECRET,
|
||||
}),
|
||||
// ...add more providers here
|
||||
GithubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
export const getAuthSession = () => getServerSession(authOptions);
|
Loading…
Reference in a new issue