order number support
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
Reference in New Issue
Block a user