Add social links page
This commit is contained in:
+52
-44
@@ -1,69 +1,77 @@
|
|||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client-js"
|
provider = "prisma-client-js"
|
||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "mongodb"
|
provider = "mongodb"
|
||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Post {
|
model Post {
|
||||||
id String @id @map("_id") @default(uuid())
|
id String @id @default(uuid()) @map("_id")
|
||||||
type String
|
type String
|
||||||
name String
|
name String
|
||||||
reviewContents String
|
reviewContents String
|
||||||
image String
|
image String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
createdBy User @relation(fields: [createdById], references: [id])
|
createdBy User @relation(fields: [createdById], references: [id])
|
||||||
createdById String
|
createdById String
|
||||||
|
|
||||||
@@index([name])
|
@@index([name])
|
||||||
|
}
|
||||||
|
|
||||||
|
model SocialLink {
|
||||||
|
id String @id @default(uuid()) @map("_id")
|
||||||
|
name String
|
||||||
|
url String
|
||||||
|
|
||||||
|
@@index([name])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Necessary for Next auth
|
// Necessary for Next auth
|
||||||
model Account {
|
model Account {
|
||||||
id String @id @map("_id") @default(cuid())
|
id String @id @default(cuid()) @map("_id")
|
||||||
userId String
|
userId String
|
||||||
type String
|
type String
|
||||||
provider String
|
provider String
|
||||||
providerAccountId String
|
providerAccountId String
|
||||||
refresh_token String? // @db.Text
|
refresh_token String? // @db.Text
|
||||||
access_token String? // @db.Text
|
access_token String? // @db.Text
|
||||||
expires_at Int?
|
expires_at Int?
|
||||||
token_type String?
|
token_type String?
|
||||||
scope String?
|
scope String?
|
||||||
id_token String? // @db.Text
|
id_token String? // @db.Text
|
||||||
session_state String?
|
session_state String?
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@unique([provider, providerAccountId])
|
@@unique([provider, providerAccountId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
id String @id @map("_id") @default(cuid())
|
id String @id @default(cuid()) @map("_id")
|
||||||
sessionToken String @unique
|
sessionToken String @unique
|
||||||
userId String
|
userId String
|
||||||
expires DateTime
|
expires DateTime
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id String @id @map("_id") @default(cuid())
|
id String @id @default(cuid()) @map("_id")
|
||||||
name String?
|
name String?
|
||||||
email String? @unique
|
email String? @unique
|
||||||
emailVerified DateTime?
|
emailVerified DateTime?
|
||||||
image String?
|
image String?
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
sessions Session[]
|
sessions Session[]
|
||||||
posts Post[]
|
posts Post[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model VerificationToken {
|
model VerificationToken {
|
||||||
identifier String @id @map("_id")
|
identifier String @id @map("_id")
|
||||||
token String @unique
|
token String @unique
|
||||||
expires DateTime
|
expires DateTime
|
||||||
|
|
||||||
@@unique([identifier, token])
|
@@unique([identifier, token])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import Link from "next/link"
|
|||||||
type ProjectCardProps = {
|
type ProjectCardProps = {
|
||||||
name: string,
|
name: string,
|
||||||
subHeading?: string,
|
subHeading?: string,
|
||||||
key: string,
|
|
||||||
description: string,
|
description: string,
|
||||||
urlTo: string
|
urlTo: string
|
||||||
}
|
}
|
||||||
@@ -11,8 +10,8 @@ type ProjectCardProps = {
|
|||||||
export default function ProjectCard(props: ProjectCardProps) {
|
export default function ProjectCard(props: ProjectCardProps) {
|
||||||
return (
|
return (
|
||||||
<Link href={props.urlTo} target="_blank" rel="noreferrer">
|
<Link href={props.urlTo} target="_blank" rel="noreferrer">
|
||||||
<div className="mx-auto text-center justify-center flex">
|
<div className="mx-auto justify-center flex">
|
||||||
<div key={props.key} className="w-1/2 rounded-lf relative">
|
<div className="w-1/2 rounded-lf relative">
|
||||||
<div className="top-0 left-0 p-2 bg-gray-800">
|
<div className="top-0 left-0 p-2 bg-gray-800">
|
||||||
<p className="font-bold">{props.name}</p>
|
<p className="font-bold">{props.name}</p>
|
||||||
<p className="pb-2">{props.subHeading}</p>
|
<p className="pb-2">{props.subHeading}</p>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
type SocialCardProps = {
|
||||||
|
name: string,
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SocialMediaCard(props: SocialCardProps) {
|
||||||
|
return (
|
||||||
|
<div className="bg-gray-500 rounded-lg text-center max-w-sm p-4 mx-auto">
|
||||||
|
<Link href={props.url}>
|
||||||
|
{props.name}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import Header from "@/components/header";
|
||||||
|
import Footer from "@/components/footer";
|
||||||
|
import SocialCard from '@/components/SocialCard'
|
||||||
|
import Dropdown from "@/components/Dropdown";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
import React, {useEffect, useState} from 'react';
|
||||||
|
|
||||||
|
export default function Links() {
|
||||||
|
const {data: linkData} = api.links.getAll.useQuery()
|
||||||
|
const [formatted, setFormatted] = useState<React.JSX.Element[] | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (linkData) {
|
||||||
|
setFormatted(
|
||||||
|
linkData.map((v) => {
|
||||||
|
return <SocialCard url={v.url} name={v.name} key={v.name} />
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [linkData])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="min-h-screen bg-black">
|
||||||
|
<div className="w-1/2 mx-auto text-white">
|
||||||
|
<Header />
|
||||||
|
|
||||||
|
{formatted}
|
||||||
|
|
||||||
|
{/* {
|
||||||
|
formatted ?
|
||||||
|
<Dropdown content={formatted} name="Social Dropdown" />
|
||||||
|
:
|
||||||
|
<h1>There's nothing here :(</h1>
|
||||||
|
} */}
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,7 +8,8 @@ import { api } from "@/utils/api";
|
|||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const { data: session, status } = useSession()
|
const { data: session, status } = useSession()
|
||||||
const mutation = api.post.create.useMutation()
|
const postMutation = api.post.create.useMutation()
|
||||||
|
const linksMutation = api.links.create.useMutation()
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -26,7 +27,7 @@ export default function Page() {
|
|||||||
return title.replaceAll(" ", "-").toLowerCase()
|
return title.replaceAll(" ", "-").toLowerCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit(event: FormEvent<HTMLFormElement>) {
|
function onPostSubmit(event: FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const form = event.target as HTMLFormElement
|
const form = event.target as HTMLFormElement
|
||||||
|
|
||||||
@@ -42,7 +43,22 @@ export default function Page() {
|
|||||||
type: typeElement.value
|
type: typeElement.value
|
||||||
}
|
}
|
||||||
|
|
||||||
mutation.mutate(object)
|
postMutation.mutate(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLinkSubmit(event: FormEvent<HTMLFormElement>) {
|
||||||
|
event.preventDefault()
|
||||||
|
const form = event.target as HTMLFormElement
|
||||||
|
|
||||||
|
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
|
||||||
|
const urlElement = form.elements.namedItem('url') as HTMLInputElement;
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
name: sanitizeTitleForLink(titleElement.value),
|
||||||
|
url: urlElement.value
|
||||||
|
}
|
||||||
|
|
||||||
|
linksMutation.mutate(object)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
@@ -50,7 +66,7 @@ export default function Page() {
|
|||||||
<main className="min-h-screen bg-black color-white">
|
<main className="min-h-screen bg-black color-white">
|
||||||
<div className="w-1/2 mx-auto text-white">
|
<div className="w-1/2 mx-auto text-white">
|
||||||
<Header />
|
<Header />
|
||||||
<form onSubmit={onSubmit} className="text-black">
|
<form onSubmit={onPostSubmit} className="text-black">
|
||||||
<div className="bg-gray-500 text-center">
|
<div className="bg-gray-500 text-center">
|
||||||
<label className="font-bold">Title</label><br />
|
<label className="font-bold">Title</label><br />
|
||||||
<input name="title" type="text" /><br />
|
<input name="title" type="text" /><br />
|
||||||
@@ -67,6 +83,18 @@ export default function Page() {
|
|||||||
<button type="submit">submit</button>
|
<button type="submit">submit</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form onSubmit={onLinkSubmit} className="text-black">
|
||||||
|
<div className="bg-gray-500 text-center">
|
||||||
|
<label className="font-bold">Title</label><br />
|
||||||
|
<input name="title" type="text" /><br />
|
||||||
|
|
||||||
|
<label className="font-bold">URL</label><br />
|
||||||
|
<input name="url" type="text" /><br />
|
||||||
|
|
||||||
|
<button type="submit">submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+22
-12
@@ -43,17 +43,21 @@ export default function Reviews() {
|
|||||||
if (itemData) {
|
if (itemData) {
|
||||||
const filteredArray = itemData.filter(v => v.type == type.toLowerCase())
|
const filteredArray = itemData.filter(v => v.type == type.toLowerCase())
|
||||||
|
|
||||||
setList(
|
if (filteredArray.length <= 0) {
|
||||||
filteredArray.map((v: GenericItemType, k) => {
|
setList(null)
|
||||||
return <ReviewCard
|
} else {
|
||||||
key={k}
|
setList(
|
||||||
title={v.name}
|
filteredArray.map((v: GenericItemType, k) => {
|
||||||
url={`/reviews/${sanitizeTitleForLink(v.name)}`}
|
return <ReviewCard
|
||||||
image={v.image}
|
key={k}
|
||||||
showText={false}
|
title={v.name}
|
||||||
/>
|
url={`/reviews/${sanitizeTitleForLink(v.name)}`}
|
||||||
})
|
image={v.image}
|
||||||
)
|
showText={false}
|
||||||
|
/>
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [itemData, type])
|
}, [itemData, type])
|
||||||
|
|
||||||
@@ -77,7 +81,13 @@ export default function Reviews() {
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-center">
|
<div className="flex flex-wrap justify-center">
|
||||||
{list}
|
{
|
||||||
|
list ? list
|
||||||
|
: <div className="text-center">
|
||||||
|
<h1>Nothing here!</h1>
|
||||||
|
<p>Try again later :(</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { postRouter } from "@/server/api/routers/post";
|
import { postRouter } from "@/server/api/routers/post";
|
||||||
|
import { linksRouter } from "./routers/links";
|
||||||
import { createTRPCRouter } from "@/server/api/trpc";
|
import { createTRPCRouter } from "@/server/api/trpc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8,6 +9,7 @@ import { createTRPCRouter } from "@/server/api/trpc";
|
|||||||
*/
|
*/
|
||||||
export const appRouter = createTRPCRouter({
|
export const appRouter = createTRPCRouter({
|
||||||
post: postRouter,
|
post: postRouter,
|
||||||
|
links: linksRouter
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {z} from "zod";
|
||||||
|
import {
|
||||||
|
createTRPCRouter,
|
||||||
|
publicProcedure,
|
||||||
|
protectedProcedure
|
||||||
|
} from "@/server/api/trpc";
|
||||||
|
|
||||||
|
export const linksRouter = createTRPCRouter({
|
||||||
|
create: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
name: z.string().min(1),
|
||||||
|
url: z.string().min(1),
|
||||||
|
type: z.string().min(1)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ctx, input}) => {
|
||||||
|
return ctx.db.socialLink.create({
|
||||||
|
data: {
|
||||||
|
name: input.name,
|
||||||
|
url: input.url,
|
||||||
|
type: input.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
delete: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.string().min(1)
|
||||||
|
)
|
||||||
|
.query(async ({ctx, input}) => {
|
||||||
|
return ctx.db.socialLink.delete({
|
||||||
|
where: {
|
||||||
|
id: input
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
getAll: publicProcedure
|
||||||
|
.query(async ({ctx}) => {
|
||||||
|
return ctx.db.socialLink.findMany()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -28,6 +28,18 @@ export const postRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
delete: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.string().min(1)
|
||||||
|
)
|
||||||
|
.query(async ({ctx, input}) => {
|
||||||
|
return ctx.db.post.delete({
|
||||||
|
where: {
|
||||||
|
id: input
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
|
||||||
getOne: publicProcedure
|
getOne: publicProcedure
|
||||||
.input(String)
|
.input(String)
|
||||||
.query(({ ctx, input }) => {
|
.query(({ ctx, input }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user