order number support

This commit is contained in:
Alex Frantz
2024-03-21 01:40:07 -04:00
parent 381081c051
commit 94b7b9a83c
5 changed files with 122 additions and 48 deletions
+5 -4
View File
@@ -8,7 +8,7 @@ datasource db {
} }
model BlogPost { model BlogPost {
id String @id @default(uuid()) @map("_id") id Int @id @default(autoincrement())
name String name String
image String image String
contents String contents String
@@ -23,7 +23,7 @@ model BlogPost {
} }
model Post { model Post {
id String @id @default(uuid()) @map("_id") id Int @id @default(autoincrement())
type String type String
name String name String
reviewContents String reviewContents String
@@ -38,9 +38,10 @@ model Post {
} }
model SocialLink { model SocialLink {
id String @id @default(uuid()) @map("_id") id Int @id @default(autoincrement())
name String name String
url String url String
orderNumber Int @default(0)
@@index([name]) @@index([name])
} }
@@ -85,7 +86,7 @@ model User {
} }
model VerificationToken { model VerificationToken {
identifier String @id @map("_id") identifier String
token String @unique token String @unique
expires DateTime expires DateTime
+11 -8
View File
@@ -3,22 +3,25 @@ import { Trash } from "grommet-icons"
type SocialCardProps = { type SocialCardProps = {
name: string, name: string,
id: number,
url: string, url: string,
showDelete: boolean, isAuthenticated: boolean,
deleteCallback: () => void deleteCallback: () => void
} }
export default function SocialMediaCard(props: SocialCardProps) { export default function SocialMediaCard(props: SocialCardProps) {
return ( return (
<div> <div className="bg-white p-5 w-full text-left">
{
props.showDelete && <Trash onClick={props.deleteCallback} color="white" className="pl-2 hover:cursor-pointer" />
}
<Link href={props.url} target="_blank"> <Link href={props.url} target="_blank">
<div className="bg-white py-5 w-full text-left"> <h1 className="inline font-bold">{props.name}</h1>
<h2 className="px-4 text-lg font-bold">{props.name}</h2>
</div>
</Link> </Link>
{ props.isAuthenticated && (
<>
<Trash onClick={props.deleteCallback} color="black" className="float-right inline hover:cursor-pointer" />
<p className="float-right opacity-50 font-bold inline mr-2">ID: {props.id}</p>
</>
)}
</div> </div>
) )
} }
+57 -5
View File
@@ -16,15 +16,16 @@ export default function CreatePage() {
// trpc hooks // trpc hooks
const linksMutation = api.links.create.useMutation() const linksMutation = api.links.create.useMutation()
function onLinkSubmit(event: FormEvent<HTMLFormElement>) { function onLinkCreateSubmit(event: FormEvent<HTMLFormElement>) {
const form = event.target as HTMLFormElement const form = event.target as HTMLFormElement
const urlRegex = new RegExp("^((https):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$") const urlRegex = new RegExp("^((https):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$")
const titleElement = form.elements.namedItem('title') as HTMLInputElement; const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const urlElement = form.elements.namedItem('url') as HTMLInputElement; const urlElement = form.elements.namedItem('url') as HTMLInputElement;
const orderElement = form.elements.namedItem("orderNumber") as HTMLInputElement;
const urlMatches = urlElement.value.match(urlRegex) const urlMatches = urlElement.value.match(urlRegex)
if (!titleElement.value || !urlElement.value) { if (!titleElement.value || !urlElement.value || (orderElement.value && isNaN(Number(orderElement.value)))) {
event.preventDefault(); event.preventDefault();
setMessage("One or more values were missing!") setMessage("One or more values were missing!")
@@ -37,7 +38,39 @@ export default function CreatePage() {
} else { } else {
const object = { const object = {
name: titleElement.value, name: titleElement.value,
url: urlElement.value url: urlElement.value,
orderNumber: Number(orderElement.value) || 0
}
linksMutation.mutate(object)
}
}
function onLinkUpdateSubmit(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 idElement = form.elements.namedItem("id") as HTMLInputElement;
const titleElement = form.elements.namedItem('title') as HTMLInputElement;
const urlElement = form.elements.namedItem('url') as HTMLInputElement;
const orderElement = form.elements.namedItem("orderNumber") as HTMLInputElement;
const urlMatches = urlElement.value.match(urlRegex)
if (!idElement.value || isNaN(Number(idElement.value)) || (orderElement.value && isNaN(Number(orderElement.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 = {
id: Number(idElement.value),
name: titleElement.value,
url: urlElement.value,
orderNumber: Number(orderElement.value) || 0
} }
linksMutation.mutate(object) linksMutation.mutate(object)
@@ -47,15 +80,34 @@ export default function CreatePage() {
return ( return (
<RootLayout protectedPage={true}> <RootLayout protectedPage={true}>
<div className="space-y-4"> <div className="space-y-4">
<div className="w-96 p-2 mx-auto bg-gray-500 text-center"> <div className="w-96 p-2 mx-auto bg-gray-500 text-center text-white">
<h1 className="font-bold text-2xl">Create Link</h1> <h1 className="font-bold text-2xl">Create Link</h1>
<div className="pt-2 pb-2"> <div className="pt-2 pb-2">
<hr /> <hr />
</div> </div>
<form onSubmit={onLinkSubmit}> <form onSubmit={onLinkCreateSubmit}>
<InputOption inputName="title" label="Title" /> <InputOption inputName="title" label="Title" />
<InputOption inputName="orderNumber" label="Order Number" />
<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 className="w-96 p-2 mx-auto bg-gray-500 text-center text-white">
<h1 className="font-bold text-2xl">Update Link</h1>
<div className="pt-2 pb-2">
<hr />
</div>
<form onSubmit={onLinkUpdateSubmit}>
<InputOption inputName="id" label="Link ID" />
<InputOption inputName="title" label="Title" />
<InputOption inputName="orderNumber" label="Order Number" />
<InputOption inputName="url" label="URL" /> <InputOption inputName="url" label="URL" />
<button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button> <button className="bg-gray-800 text-white rounded-lg p-2" type="submit">Submit</button>
+23 -28
View File
@@ -2,43 +2,16 @@ import SocialCard from '@/components/SocialCard'
import Head from '@/components/Head'; import Head from '@/components/Head';
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { useSession } from 'next-auth/react'; import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import React, {useState, useEffect} from 'react';
export default function Links() { export default function Links() {
const router = useRouter();
const {status} = useSession(); const {status} = useSession();
const [links, setLinks] = useState<React.JSX.Element[] | null>(null);
// trpc hooks // trpc hooks
const { data: linkData } = api.links.getAll.useQuery() const { data: linkData } = api.links.getAll.useQuery()
const deleteMutation = api.links.delete.useMutation() const deleteMutation = api.links.delete.useMutation()
useEffect(() => {
if (!links && linkData) {
const allLinks = [];
for (const link of linkData) {
allLinks.push(
<SocialCard
name={link.name}
key={link.id}
url={link.url}
showDelete={status == "authenticated"}
deleteCallback={() => {
deleteMutation.mutate(link.id);
router.reload()
}}
/>
)
}
setLinks(allLinks)
}
})
return ( return (
<main className="bg-black flex flex-col justify-center items-center min-h-screen"> <main className="bg-black flex flex-col justify-center items-center min-h-screen">
<Head /> <Head />
@@ -57,7 +30,29 @@ export default function Links() {
<div className="space-y-2"> <div className="space-y-2">
{ {
links && links.length > 0 ? links : <h1 className="text-center text-white font-bold">Uh oh, there are no links here!</h1> linkData ?
linkData.sort((a,b) => {
if (a.orderNumber < b.orderNumber) {
return -1
} else if (a.orderNumber > b.orderNumber) {
return 1
}
return 0
}).map((v,k) => {
return <SocialCard
name={v.name}
key={k}
id={v.id}
url={v.url}
deleteCallback={() => {
deleteMutation.mutate(v.id);
}}
isAuthenticated={ status === "authenticated" }
/>
})
:
<h1 className="text-center text-white font-bold">Uh oh, there are no links here!</h1>
} }
</div> </div>
</div> </div>
+26 -3
View File
@@ -10,20 +10,43 @@ export const linksRouter = createTRPCRouter({
.input( .input(
z.object({ z.object({
name: z.string().min(1), name: z.string().min(1),
url: z.string().min(1) url: z.string().min(1),
orderNumber: z.number().optional()
}) })
) )
.mutation(async ({ctx, input}) => { .mutation(async ({ctx, input}) => {
return ctx.db.socialLink.create({ return ctx.db.socialLink.create({
data: { data: {
name: input.name, name: input.name,
url: input.url url: input.url,
orderNumber: input.orderNumber
}
})
}),
update: protectedProcedure
.input(
z.object({
id: z.number(),
name: z.string().min(1).optional(),
url: z.string().min(1).optional(),
orderNumber: z.number().optional(),
})
)
.mutation(async ({ctx, input}) => {
return ctx.db.socialLink.update({
data: {
name: input.name,
url: input.url,
orderNumber: input.orderNumber
},
where: {
id: input.id
} }
}) })
}), }),
delete: protectedProcedure delete: protectedProcedure
.input( .input(
z.string().min(1) z.number().min(1)
) )
.mutation(async ({ctx, input}) => { .mutation(async ({ctx, input}) => {
return ctx.db.socialLink.delete({ return ctx.db.socialLink.delete({