Updated links logic, new admin dash

This commit is contained in:
alex 3
2023-11-07 01:13:54 -05:00
parent 48fb8f644f
commit 56af375cc0
15 changed files with 300 additions and 209 deletions
+118
View File
@@ -0,0 +1,118 @@
// react modules
import {useState} from 'react'
import type {FormEvent} from 'react';
// trpc api
import { api } from "@/utils/api";
// components
import RootLayout from '@/components/RootLayout';
import InputOption from '@/components/InputOption';
export default function CreatePage() {
// ui states
const [message, setMessage] = useState<string | null>(null)
// trpc hooks
const postMutation = api.post.create.useMutation()
const linksMutation = api.links.create.useMutation()
function sanitizeTitleForLink(title: string) {
return title.replaceAll(" ", "-").toLowerCase()
}
function onPostSubmit(event: FormEvent<HTMLFormElement>) {
const form = event.target as HTMLFormElement
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const reviewElement = form.elements.namedItem('review') as HTMLInputElement;
const imageElement = form.elements.namedItem('image') as HTMLInputElement;
const typeElement = form.elements.namedItem('type') as HTMLInputElement;
if (!titleElement.value || !reviewElement.value || !imageElement.value || !typeElement.value) {
event.preventDefault();
setMessage("One or more values were missing!")
setTimeout(() => setMessage(null), 5000)
} else {
const object = {
name: sanitizeTitleForLink(titleElement.value),
reviewContents: reviewElement.value,
image: imageElement.value,
type: typeElement.value
}
postMutation.mutate(object)
}
}
function onLinkSubmit(event: FormEvent<HTMLFormElement>) {
const form = event.target as HTMLFormElement
const urlRegex = new RegExp("^((https):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$")
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const urlElement = form.elements.namedItem('url') as HTMLInputElement;
const urlMatches = urlElement.value.match(urlRegex)
if (!titleElement.value || !urlElement.value) {
event.preventDefault();
setMessage("One or more values were missing!")
setTimeout(() => setMessage(null), 5000)
} else if (urlMatches != null && !urlMatches[1]) {
event.preventDefault();
setMessage("An invalid URL was provided!")
setTimeout(() => setMessage(null), 5000)
} else {
const object = {
name: titleElement.value,
url: urlElement.value
}
linksMutation.mutate(object)
}
}
return (
<RootLayout protectedPage={true}>
<div className="space-y-4">
<div className="w-96 p-2 mx-auto bg-gray-500 text-center">
<h1 className="font-bold text-2xl">Create Review Post</h1>
<div className="pt-2 pb-2">
<hr />
</div>
<form onSubmit={onPostSubmit}>
<InputOption inputName="title" label="Title" />
<InputOption inputName="image" label="Image URL" />
<InputOption inputName="type" label="Content Type (book, movie, tv, game)" />
<InputOption inputName="review" label="Review" isTextArea={true} />
<button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button>
</form>
<p>{message}</p>
</div>
<div className="w-96 p-2 mx-auto bg-gray-500 text-center">
<h1 className="font-bold text-2xl">Create Link</h1>
<div className="pt-2 pb-2">
<hr />
</div>
<form onSubmit={onLinkSubmit}>
<InputOption inputName="title" label="Title" />
<InputOption inputName="url" label="URL" />
<button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button>
</form>
<p>{message}</p>
</div>
</div>
</RootLayout>
)
}
+3 -8
View File
@@ -1,22 +1,17 @@
import Header from "@/components/header";
import Footer from "@/components/footer";
import RootLayout from "@/components/RootLayout";
import Dropdown from "@/components/Dropdown";
import ProjectCard from "@/components/ProjectsCard";
import Image from 'next/image';
export default function Home() {
return (
<main className="min-h-screen bg-black">
<RootLayout>
<div className="w-1/2 mx-auto text-white">
<Header />
<Dropdown content={Music()} name="Music" />
<Dropdown content={Games()} name="Games" />
<Dropdown content={Projects()} name="Projects" />
<Footer />
</div>
</main>
</RootLayout>
)
}
+28 -31
View File
@@ -1,40 +1,37 @@
import Header from "@/components/header";
import Footer from "@/components/footer";
import SocialCard from '@/components/SocialCard'
import Dropdown from "@/components/Dropdown";
import RootLayout from '@/components/RootLayout';
import { api } from "@/utils/api";
import React, {useEffect, useState} from 'react';
import { useSession } from 'next-auth/react';
import React 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])
const {status} = useSession();
// trpc hooks
const { data: linkData } = api.links.getAll.useQuery()
const deleteMutation = api.links.delete.useMutation()
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&apos;s nothing here :(</h1>
} */}
<Footer />
<RootLayout>
<div className="space-y-2">
{
linkData && linkData.length > 0 ? linkData.map((v) => {
return <SocialCard
url={v.url}
name={v.name}
key={v.name}
showDelete={status === "authenticated"}
deleteCallback={() => {
deleteMutation.mutate(v.id)
}}
/>
}) :
<div className="text-center text-white">
<h1>Uh oh!</h1>
<p>There&apos;s nothing here :(</p>
</div>
}
</div>
</main>
</RootLayout>
)
}
+3 -8
View File
@@ -1,13 +1,10 @@
import Header from "@/components/header"
import Footer from "@/components/footer"
import RootLayout from "@/components/RootLayout"
import Image from "next/image"
import ProjectCard from "@/components/ProjectsCard"
export default function Resume() {
return (
<main className="min-h-screen bg-black">
<Header />
<RootLayout>
<div className="text-center mx-auto text-white">
<div className="flex flex-wrap justify-center pb-2">
<Image src="https://storage.thatalex.dev/content/pfp.png" width="130" height="130" alt="PFP" className="rounded-3xl" />
@@ -64,8 +61,6 @@ export default function Resume() {
</div>
</div>
</div>
<Footer />
</main>
</RootLayout>
)
}
+37 -17
View File
@@ -1,35 +1,56 @@
import {useRouter} from 'next/router';
import Header from '@/components/header';
import Footer from "@/components/footer";
import Image from 'next/image';
import {api} from '@/utils/api'
import RootLayout from '@/components/RootLayout';
// next modules
import Router, {useRouter} from 'next/router';
import Link from 'next/link';
import {useSession} from 'next-auth/react';
import Image from 'next/image';
// api
import {api} from '@/utils/api'
// icons
import { Trash } from 'grommet-icons';
export default function ReviewPage() {
const router = useRouter()
const name = router.query.name as string
const { status } = useSession()
// trpc stuff
const {data: bookData} = api.post.getOne.useQuery(name)
const deletionMutation = api.post.delete.useMutation()
function deleteItem() {
if (bookData) {
deletionMutation.mutate(bookData.id)
Router.replace("/").catch(() => {
console.error("Failed to reroute user!")
})
}
}
if (bookData) {
return (
<main className="min-h-screen bg-black text-white">
<Header />
<div className="mx-auto w-1/2 text-center flex justify-center text-white">
<RootLayout>
<div className="mx-auto w-1/2 text-center flex justify-center">
<Image src={bookData.image} width="150" height="150" alt={`${bookData.name} ${bookData.type} cover`} />
</div>
<div className="mx-auto w-1/2 text-center">
<h1 className="text-2xl font-bold">{bookData.name}</h1>
<hr />
<div className="mx-auto w-1/2 text-center text-white">
<h1 className="text-2xl font-bold">{bookData.name} { status == "authenticated" ? <Trash onClick={deleteItem} className="hover:cursor-pointer" /> : ""}</h1>
<div className="pt-2 pb-2">
<hr />
</div>
{bookData.reviewContents}
</div>
<Footer />
</main>
</RootLayout>
)
} else {
return (
<main className="min-h-screen bg-black text-white flex flex-col justify-center items-centers">
<Header />
<RootLayout>
<div className="text-center text-2xl font-bold">
<p>Well, that&apos;s not good..</p>
</div>
@@ -40,8 +61,7 @@ export default function ReviewPage() {
Head Home
</Link>
</div>
<Footer />
</main>
</RootLayout>
)
}
}
-111
View File
@@ -1,111 +0,0 @@
import {useSession} from 'next-auth/react';
import {useState, useEffect} from 'react';
import type {FormEvent} from 'react';
import Header from '@/components/header';
import Footer from "@/components/footer";
import Router from 'next/router';
import { api } from "@/utils/api";
export default function Page() {
const { data: session, status } = useSession()
const postMutation = api.post.create.useMutation()
const linksMutation = api.links.create.useMutation()
const [loading, setLoading] = useState(true)
useEffect(() => {
if (status == "unauthenticated") {
console.log('user is unauthenticated!')
Router.replace('/').catch(() => {
console.error("Failed to reroute user!")
})
} else {
setLoading(false)
}
}, [status])
function sanitizeTitleForLink(title: string) {
return title.replaceAll(" ", "-").toLowerCase()
}
function onPostSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault()
const form = event.target as HTMLFormElement
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const reviewElement = form.elements.namedItem('review') as HTMLInputElement;
const imageElement = form.elements.namedItem('image') as HTMLInputElement;
const typeElement = form.elements.namedItem('type') as HTMLInputElement;
const object = {
name: sanitizeTitleForLink(titleElement.value),
reviewContents: reviewElement.value,
image: imageElement.value,
type: typeElement.value
}
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) {
return (
<main className="min-h-screen bg-black color-white">
<div className="w-1/2 mx-auto text-white">
<Header />
<form onSubmit={onPostSubmit} 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">Image URL</label><br />
<input name="image" type="text" /><br />
<label className="font-bold">Content Type (book, movie, tv, game)</label><br />
<input name="type" type="text" /><br />
<label className="font-bold">Review</label><br />
<textarea name="review" /><br />
<button type="submit">submit</button>
</div>
</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 />
</div>
</main>
)
} else {
return (
<main>
<p>
Loading..
</p>
</main>
)
}
}
+4 -21
View File
@@ -1,9 +1,6 @@
import Header from "@/components/header"
import Footer from "@/components/footer";
import RootLayout from "@/components/RootLayout"
import ReviewCard from "@/components/ReviewCard"
import {useSession} from 'next-auth/react';
import {useState, useEffect} from 'react';
import Link from 'next/link';
import React, {useState, useEffect} from 'react';
import { api } from "@/utils/api";
type GenericItemType = {
@@ -12,7 +9,6 @@ type GenericItemType = {
}
export default function Reviews() {
const { data: sessionData, status } = useSession();
const {data: itemData} = api.post.getAll.useQuery()
const [list, setList] = useState<null | JSX.Element[]>(null)
const [type, setType] = useState("Book")
@@ -62,20 +58,8 @@ export default function Reviews() {
}, [itemData, type])
return (
<main className="min-h-screen bg-black color-white">
<Header />
<RootLayout>
<div className="w-1/2 mx-auto text-white">
{
status == "authenticated" ?
<p className="text-center font-bold">
<Link href="/reviews/create">
Create
</Link>
</p>
:
""
}
<p className="text-center font-bold text-2xl hover:cursor-pointer hover:underline pb-2" onClick={onCatClick}>
{type}
</p>
@@ -90,7 +74,6 @@ export default function Reviews() {
}
</div>
</div>
<Footer />
</main>
</RootLayout>
)
}