Compare commits
	
		
			140 Commits
		
	
	
		
			009433f483
			...
			3b6652c617
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						3b6652c617 | |
| 
							
							
								
								 | 
						89e338a0ac | |
| 
							
							
								
								 | 
						17578d50d6 | |
| 
							
							
								
								 | 
						c8879b04c9 | |
| 
							
							
								
								 | 
						b7bca4cacc | |
| 
							
							
								
								 | 
						71f5a44c8e | |
| 
							
							
								
								 | 
						cd90c92c6d | |
| 
							
							
								
								 | 
						055b3c254d | |
| 
							
							
								
								 | 
						79e3403902 | |
| 
							
							
								
								 | 
						41951a2ac6 | |
| 
							
							
								
								 | 
						0fa28a46eb | |
| 
							
							
								
								 | 
						6ee4128c85 | |
| 
							
							
								
								 | 
						aec413ba7a | |
| 
							
							
								
								 | 
						57cc55f414 | |
| 
							
							
								
								 | 
						bc244497cd | |
| 
							
							
								
								 | 
						29ab837aca | |
| 
							
							
								
								 | 
						1bad3ba5f8 | |
| 
							
							
								
								 | 
						2294d0c0b0 | |
| 
							
							
								
								 | 
						2e1409cf46 | |
| 
							
							
								
								 | 
						f3dbd2cb9e | |
| 
							
							
								
								 | 
						5206e415ed | |
| 
							
							
								
								 | 
						fe9878cb7a | |
| 
							
							
								
								 | 
						b6a56fca2b | |
| 
							
							
								
								 | 
						8c4b9d27f2 | |
| 
							
							
								
								 | 
						5ea7a61915 | |
| 
							
							
								
								 | 
						7c6f06e194 | |
| 
							
							
								
								 | 
						540413173c | |
| 
							
							
								
								 | 
						330226ecd6 | |
| 
							
							
								
								 | 
						23584a0a50 | |
| 
							
							
								
								 | 
						8ad3583c4e | |
| 
							
							
								
								 | 
						c6496130e3 | |
| 
							
							
								
								 | 
						97a537f5a2 | |
| 
							
							
								
								 | 
						1fca1a2b81 | |
| 
							
							
								
								 | 
						4aa7194427 | |
| 
							
							
								
								 | 
						f163de99c8 | |
| 
							
							
								
								 | 
						10408f604a | |
| 
							
							
								
								 | 
						ee2a7c4cbf | |
| 
							
							
								
								 | 
						06fb2831ef | |
| 
							
							
								
								 | 
						5b919db59b | |
| 
							
							
								
								 | 
						06b69b5ce7 | |
| 
							
							
								
								 | 
						54a001183a | |
| 
							
							
								
								 | 
						d210b13bde | |
| 
							
							
								
								 | 
						45af32d091 | |
| 
							
							
								
								 | 
						0c39838f6a | |
| 
							
							
								
								 | 
						788051fa10 | |
| 
							
							
								
								 | 
						191457d6c1 | |
| 
							
							
								
								 | 
						b5745a3c05 | |
| 
							
							
								
								 | 
						878daf35bb | |
| 
							
							
								
								 | 
						494521d51d | |
| 
							
							
								
								 | 
						aeb7bc1f6f | |
| 
							
							
								
								 | 
						bea291aa92 | |
| 
							
							
								
								 | 
						a691250637 | |
| 
							
							
								
								 | 
						7dd912d6f6 | |
| 
							
							
								
								 | 
						be83489ea6 | |
| 
							
							
								
								 | 
						4145d84d65 | |
| 
							
							
								
								 | 
						ef70bd9d92 | |
| 
							
							
								
								 | 
						e1e4ce23f1 | |
| 
							
							
								
								 | 
						5563de438a | |
| 
							
							
								
								 | 
						c2bfee6b87 | |
| 
							
							
								
								 | 
						a4a2ba35cd | |
| 
							
							
								
								 | 
						21bee8cc8b | |
| 
							
							
								
								 | 
						447b4a7edd | |
| 
							
							
								
								 | 
						26eb4cd9eb | |
| 
							
							
								
								 | 
						e91caeb51c | |
| 
							
							
								
								 | 
						10b512bb5c | |
| 
							
							
								
								 | 
						c2107b14a3 | |
| 
							
							
								
								 | 
						27b368e0cb | |
| 
							
							
								
								 | 
						c4b61069fd | |
| 
							
							
								
								 | 
						be32c7e0a6 | |
| 
							
							
								
								 | 
						8d2bf53a1c | |
| 
							
							
								
								 | 
						be765fda2a | |
| 
							
							
								
								 | 
						b32aabcd08 | |
| 
							
							
								
								 | 
						f245b8d72d | |
| 
							
							
								
								 | 
						04688feb28 | |
| 
							
							
								
								 | 
						febaec3220 | |
| 
							
							
								
								 | 
						40f2360ebd | |
| 
							
							
								
								 | 
						1546a2ff31 | |
| 
							
							
								
								 | 
						c8f374f754 | |
| 
							
							
								
								 | 
						c8b25c36f5 | |
| 
							
							
								
								 | 
						1db71fb21b | |
| 
							
							
								
								 | 
						a9257c2825 | |
| 
							
							
								
								 | 
						4f41415a80 | |
| 
							
							
								
								 | 
						98b2b1e3cc | |
| 
							
							
								
								 | 
						c3ee490ce5 | |
| 
							
							
								
								 | 
						e1391bec62 | |
| 
							
							
								
								 | 
						5aaa45cade | |
| 
							
							
								
								 | 
						96db18580e | |
| 
							
							
								
								 | 
						782ccb76f5 | |
| 
							
							
								
								 | 
						c63175a0f8 | |
| 
							
							
								
								 | 
						d87eb3b342 | |
| 
							
							
								
								 | 
						c7149fc8af | |
| 
							
							
								
								 | 
						4a8b6f72df | |
| 
							
							
								
								 | 
						8c62f7addf | |
| 
							
							
								
								 | 
						6ceb035b19 | |
| 
							
							
								
								 | 
						fdf05f2b4c | |
| 
							
							
								
								 | 
						ec96a1e988 | |
| 
							
							
								
								 | 
						6dc05d2610 | |
| 
							
							
								
								 | 
						d8a2f3df7e | |
| 
							
							
								
								 | 
						b891780881 | |
| 
							
							
								
								 | 
						525f716f16 | |
| 
							
							
								
								 | 
						7b68a7451e | |
| 
							
							
								
								 | 
						f454f6739e | |
| 
							
							
								
								 | 
						99b2fb9628 | |
| 
							
							
								
								 | 
						7b994ec06e | |
| 
							
							
								
								 | 
						52a30ec141 | |
| 
							
							
								
								 | 
						3a91fd7cb4 | |
| 
							
							
								
								 | 
						6839c1c369 | |
| 
							
							
								
								 | 
						285cef524c | |
| 
							
							
								
								 | 
						9583d0da16 | |
| 
							
							
								
								 | 
						b5b8d8ad09 | |
| 
							
							
								
								 | 
						13a9407caa | |
| 
							
							
								
								 | 
						0eb09073ca | |
| 
							
							
								
								 | 
						de2c8991c6 | |
| 
							
							
								
								 | 
						fff436f87c | |
| 
							
							
								
								 | 
						e7f0cf3fb6 | |
| 
							
							
								
								 | 
						1f3655f14c | |
| 
							
							
								
								 | 
						6080037d83 | |
| 
							
							
								
								 | 
						688c260f4b | |
| 
							
							
								
								 | 
						3a56b1f31e | |
| 
							
							
								
								 | 
						2d340983e6 | |
| 
							
							
								
								 | 
						3151236ca0 | |
| 
							
							
								
								 | 
						d4d73750b3 | |
| 
							
							
								
								 | 
						5ad03054a9 | |
| 
							
							
								
								 | 
						7539b8a577 | |
| 
							
							
								
								 | 
						02d111098d | |
| 
							
							
								
								 | 
						b604ed48da | |
| 
							
							
								
								 | 
						f503647469 | |
| 
							
							
								
								 | 
						f3e5233171 | |
| 
							
							
								
								 | 
						47756280d9 | |
| 
							
							
								
								 | 
						58d1fd1ed4 | |
| 
							
							
								
								 | 
						ce8b52cb87 | |
| 
							
							
								
								 | 
						c3ae4721d4 | |
| 
							
							
								
								 | 
						438599a530 | |
| 
							
							
								
								 | 
						50409895c0 | |
| 
							
							
								
								 | 
						ed8e71694f | |
| 
							
							
								
								 | 
						d54b8180ce | |
| 
							
							
								
								 | 
						b36f0edfb1 | |
| 
							
							
								
								 | 
						25f8f728c9 | |
| 
							
							
								
								 | 
						d1c69c9c15 | |
| 
							
							
								
								 | 
						dd07c259ac | 
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					# Environment variables declared in this file are automatically made available to Prisma.
 | 
				
			||||||
 | 
					# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
 | 
				
			||||||
 | 
					# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DATABASE_URL="file:./dev.db"
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -12,7 +12,6 @@
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "@hookform/resolvers": "^3.6.0",
 | 
					    "@hookform/resolvers": "^3.6.0",
 | 
				
			||||||
    "@mapbox/node-pre-gyp": "^1.0.11",
 | 
					 | 
				
			||||||
    "@prisma/client": "^5.15.0",
 | 
					    "@prisma/client": "^5.15.0",
 | 
				
			||||||
    "@radix-ui/react-checkbox": "^1.0.4",
 | 
					    "@radix-ui/react-checkbox": "^1.0.4",
 | 
				
			||||||
    "@radix-ui/react-context-menu": "^2.2.1",
 | 
					    "@radix-ui/react-context-menu": "^2.2.1",
 | 
				
			||||||
| 
						 | 
					@ -25,15 +24,11 @@
 | 
				
			||||||
    "@radix-ui/react-slot": "^1.0.2",
 | 
					    "@radix-ui/react-slot": "^1.0.2",
 | 
				
			||||||
    "@radix-ui/react-toast": "^1.1.5",
 | 
					    "@radix-ui/react-toast": "^1.1.5",
 | 
				
			||||||
    "@tanstack/react-table": "^8.17.3",
 | 
					    "@tanstack/react-table": "^8.17.3",
 | 
				
			||||||
    "@types/bcrypt": "^5.0.2",
 | 
					 | 
				
			||||||
    "bcrypt": "^5.1.1",
 | 
					 | 
				
			||||||
    "class-variance-authority": "^0.7.0",
 | 
					    "class-variance-authority": "^0.7.0",
 | 
				
			||||||
    "clsx": "^2.1.1",
 | 
					    "clsx": "^2.1.1",
 | 
				
			||||||
    "date-fns": "^3.6.0",
 | 
					    "date-fns": "^3.6.0",
 | 
				
			||||||
    "jose": "^5.8.0",
 | 
					 | 
				
			||||||
    "lucide": "^0.445.0",
 | 
					 | 
				
			||||||
    "lucide-react": "^0.394.0",
 | 
					    "lucide-react": "^0.394.0",
 | 
				
			||||||
    "next": "^14.2.13",
 | 
					    "next": "14.2.3",
 | 
				
			||||||
    "next-themes": "^0.3.0",
 | 
					    "next-themes": "^0.3.0",
 | 
				
			||||||
    "react": "^18",
 | 
					    "react": "^18",
 | 
				
			||||||
    "react-day-picker": "^8.10.1",
 | 
					    "react-day-picker": "^8.10.1",
 | 
				
			||||||
| 
						 | 
					@ -42,7 +37,6 @@
 | 
				
			||||||
    "recharts": "^2.12.7",
 | 
					    "recharts": "^2.12.7",
 | 
				
			||||||
    "tailwind-merge": "^2.3.0",
 | 
					    "tailwind-merge": "^2.3.0",
 | 
				
			||||||
    "tailwindcss-animate": "^1.0.7",
 | 
					    "tailwindcss-animate": "^1.0.7",
 | 
				
			||||||
    "text-encoding": "^0.7.0",
 | 
					 | 
				
			||||||
    "zod": "^3.23.8"
 | 
					    "zod": "^3.23.8"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
 | 
				
			||||||
  <CheckboxPrimitive.Root
 | 
					  <CheckboxPrimitive.Root
 | 
				
			||||||
    ref={ref}
 | 
					    ref={ref}
 | 
				
			||||||
    className={cn(
 | 
					    className={cn(
 | 
				
			||||||
      "peer h-3 w-3 sm:h-6 sm:w-6 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
 | 
					      "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
 | 
				
			||||||
      className
 | 
					      className
 | 
				
			||||||
    )}
 | 
					    )}
 | 
				
			||||||
    {...props}
 | 
					    {...props}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ const TableHead = React.forwardRef<
 | 
				
			||||||
  <th
 | 
					  <th
 | 
				
			||||||
    ref={ref}
 | 
					    ref={ref}
 | 
				
			||||||
    className={cn(
 | 
					    className={cn(
 | 
				
			||||||
      "h-12 md:px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
 | 
					      "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
 | 
				
			||||||
      className
 | 
					      className
 | 
				
			||||||
    )}
 | 
					    )}
 | 
				
			||||||
    {...props}
 | 
					    {...props}
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,7 @@ const TableCell = React.forwardRef<
 | 
				
			||||||
>(({ className, ...props }, ref) => (
 | 
					>(({ className, ...props }, ref) => (
 | 
				
			||||||
  <td
 | 
					  <td
 | 
				
			||||||
    ref={ref}
 | 
					    ref={ref}
 | 
				
			||||||
    className={cn("md:p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
 | 
					    className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
 | 
				
			||||||
    {...props}
 | 
					    {...props}
 | 
				
			||||||
  />
 | 
					  />
 | 
				
			||||||
))
 | 
					))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,6 @@ import "./globals.css";
 | 
				
			||||||
import Navlinks from "./ui/navLinks";
 | 
					import Navlinks from "./ui/navLinks";
 | 
				
			||||||
import { ModeToggle } from "./ui/modeToggle";
 | 
					import { ModeToggle } from "./ui/modeToggle";
 | 
				
			||||||
import { inter } from "./ui/fonts";
 | 
					import { inter } from "./ui/fonts";
 | 
				
			||||||
import LogoutButton from "./ui/logoutButton";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +15,7 @@ export const metadata: Metadata = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function RootLayout({
 | 
					export default function RootLayout({
 | 
				
			||||||
  children,
 | 
					  children,
 | 
				
			||||||
}: Readonly<{
 | 
					}: Readonly<{
 | 
				
			||||||
| 
						 | 
					@ -31,16 +30,14 @@ export default function RootLayout({
 | 
				
			||||||
          enableSystem
 | 
					          enableSystem
 | 
				
			||||||
          disableTransitionOnChange
 | 
					          disableTransitionOnChange
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <div id="layout-container" className="md:p-4 w-screen h-screen mt-2 md:mt-6 flex justify-center">
 | 
					          <div id="layout-container" className="p-4 w-screen h-screen mt-6 flex justify-center">
 | 
				
			||||||
            <div className="w-full md:w-5/6 flex flex-col md:flex-row">
 | 
					            <div className="w-5/6 flex">
 | 
				
			||||||
              <div id="sidebar" className=" flex flex-row md:flex-col  justify-between items-center"> <header className="">
 | 
					              <div id="sidebar" className="h-5/6 flex flex-col"> <header className="">
 | 
				
			||||||
                <h1 className="font-black text-primary-foreground bg-primary antialiased w-full p-2 rounded-tl-3xl pl-6 pr-4 text-sm sm:text-4xl
 | 
					                <h1 className="font-black text-4xl text-primary-foreground bg-primary antialiased w-full p-2 rounded-tl-3xl pl-6 pr-4">SubMan</h1>
 | 
				
			||||||
                ">SubMan</h1>
 | 
					                <p className="mt-2 mx-1 text-sm antialiased w-40">The self-hosted literary submission tracker.</p>
 | 
				
			||||||
                <p className="mt-2 mx-1 text-sm antialiased w-40 hidden md:block">The self-hosted literary submission tracker.</p>
 | 
					 | 
				
			||||||
              </header>
 | 
					              </header>
 | 
				
			||||||
                <Navlinks className="md:mt-6" />
 | 
					                <Navlinks className="mt-6" />
 | 
				
			||||||
                <footer className="my-auto md:mt-auto flex justify-center"><ModeToggle /><LogoutButton />
 | 
					                <footer className="mt-auto"><ModeToggle /></footer>
 | 
				
			||||||
                </footer>
 | 
					 | 
				
			||||||
              </div>
 | 
					              </div>
 | 
				
			||||||
              <div className="flex justify-center w-full">
 | 
					              <div className="flex justify-center w-full">
 | 
				
			||||||
                {children}
 | 
					                {children}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,80 +1,59 @@
 | 
				
			||||||
"use server"
 | 
					"use server"
 | 
				
			||||||
import { Pub, Story, Sub } from "@prisma/client"
 | 
					import { Genre, Story } from "@prisma/client"
 | 
				
			||||||
import prisma from "./db"
 | 
					import prisma from "./db"
 | 
				
			||||||
import { revalidatePath } from "next/cache"
 | 
					import { revalidatePath } from "next/cache"
 | 
				
			||||||
import { z } from "zod"
 | 
					import { redirect } from "next/navigation"
 | 
				
			||||||
import { pubSchema } from "app/ui/forms/schemas"
 | 
					 | 
				
			||||||
import { subSchema } from "app/ui/forms/schemas"
 | 
					 | 
				
			||||||
import { prepGenreData, prepStoryData } from "./validate"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function createStory(data: Story & { genres: number[] }) {
 | 
				
			||||||
export async function createStory({ story, genres }: { story: Story, genres: number[] }): Promise<{ success: string }> {
 | 
					 | 
				
			||||||
	// will return undefined if middleware authorization fails
 | 
					 | 
				
			||||||
	"use server"
 | 
						"use server"
 | 
				
			||||||
	try {
 | 
						const genresArray = data.genres.map((e) => { return { id: e } })
 | 
				
			||||||
		const storyData = await prepStoryData(story)
 | 
						const res = await prisma.story.create({
 | 
				
			||||||
		const genresArray = await prepGenreData(genres)
 | 
							data: {
 | 
				
			||||||
 | 
								title: data.title,
 | 
				
			||||||
		//submit
 | 
								word_count: data.word_count,
 | 
				
			||||||
		const res = await prisma.story.create({ data: storyData })
 | 
							}
 | 
				
			||||||
		await prisma.story.update({
 | 
						})
 | 
				
			||||||
			where: { id: res.id },
 | 
						console.log(res)
 | 
				
			||||||
			data: {
 | 
						const genresRes = await prisma.story.update({
 | 
				
			||||||
				genres: { set: genresArray }
 | 
							where: { id: res.id },
 | 
				
			||||||
			}
 | 
							data: {
 | 
				
			||||||
		})
 | 
								genres: { set: genresArray }
 | 
				
			||||||
		revalidatePath("/story")
 | 
							}
 | 
				
			||||||
		return { success: `Created the story '${story.title}'.` }
 | 
						})
 | 
				
			||||||
	} catch (error) {
 | 
						console.log(genresRes)
 | 
				
			||||||
		console.error(error)
 | 
						revalidatePath("/story")
 | 
				
			||||||
	}
 | 
						redirect("/story")
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function createPub({ pub, genres }: { pub: Pub, genres: number[] }): Promise<{ success: string }> {
 | 
					export async function createPub(data) {
 | 
				
			||||||
	"use server"
 | 
						"use server"
 | 
				
			||||||
	const genresArray = genres.map(e => { return { id: e } })
 | 
						const genresArray = data.genres.map(e => { return { id: e } })
 | 
				
			||||||
 | 
						const res = await prisma.pub.create({
 | 
				
			||||||
	//prepare schemas
 | 
							data: {
 | 
				
			||||||
	const schema = pubSchema.omit({ genres: true })
 | 
								title: data.title,
 | 
				
			||||||
	const genreSchema = z.object({ id: z.number() })
 | 
								link: data.link,
 | 
				
			||||||
	const genresSchema = z.array(genreSchema)
 | 
								query_after_days: data.query_after_days
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	try {
 | 
						})
 | 
				
			||||||
 | 
						console.log(res)
 | 
				
			||||||
		//validate
 | 
						const genresRes = await prisma.pub.update({
 | 
				
			||||||
		schema.parse(pub)
 | 
							where: { id: res.id },
 | 
				
			||||||
		genresSchema.safeParse(genresArray)
 | 
							data:
 | 
				
			||||||
 | 
								{ genres: { set: genresArray } }
 | 
				
			||||||
		//submit
 | 
						})
 | 
				
			||||||
		const res = await prisma.pub.create({
 | 
						console.log(genresRes)
 | 
				
			||||||
			data: pub
 | 
						revalidatePath("/publication")
 | 
				
			||||||
		})
 | 
						redirect("/publication")
 | 
				
			||||||
		const genresRes = await prisma.pub.update({
 | 
					 | 
				
			||||||
			where: { id: res.id },
 | 
					 | 
				
			||||||
			data:
 | 
					 | 
				
			||||||
				{ genres: { set: genresArray } }
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		revalidatePath("/publication")
 | 
					 | 
				
			||||||
		return { success: `Created the publication '${pub.title}'.` }
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function createSub(data: Sub): Promise<Sub | boolean> {
 | 
					export async function createSub(data) {
 | 
				
			||||||
	"use server"
 | 
						"use server"
 | 
				
			||||||
	try {
 | 
						const res = await prisma.sub.create({ data })
 | 
				
			||||||
		subSchema.parse(data)
 | 
						console.log(res)
 | 
				
			||||||
		const res = await prisma.sub.create({ data })
 | 
						revalidatePath("/submission")
 | 
				
			||||||
		revalidatePath("/submission")
 | 
						redirect("/submission")
 | 
				
			||||||
		return res
 | 
					
 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,34 +10,24 @@ const tableMap = {
 | 
				
			||||||
	"/submission": "sub"
 | 
						"/submission": "sub"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function deleteRecord(id: number, pathname: string): Promise<undefined | boolean> {
 | 
					export async function deleteRecord(id: number, pathname: Pathname) {
 | 
				
			||||||
	const table = tableMap[pathname]
 | 
						const table = tableMap[pathname]
 | 
				
			||||||
	try {
 | 
						const res = await prisma[table].delete({ where: { id } })
 | 
				
			||||||
		//@ts-ignore
 | 
						console.log(`deleted from ${table}: ${res.id}`)
 | 
				
			||||||
		const res = await prisma[table].delete({ where: { id } })
 | 
						console.log("revalidating: " + pathname)
 | 
				
			||||||
		console.log(`deleted from ${table}: ${res.id}`)
 | 
						revalidatePath(pathname)
 | 
				
			||||||
		revalidatePath(pathname)
 | 
						redirect(pathname)
 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return undefined
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function deleteRecords(ids: number[], pathname: "/story" | "/publication" | "/submission"): Promise<boolean | undefined> {
 | 
					export async function deleteRecords(ids: number[], pathname: "/story" | "/publication" | "/submission") {
 | 
				
			||||||
	try {
 | 
						const table = tableMap[pathname]
 | 
				
			||||||
		const table = tableMap[pathname]
 | 
						ids.forEach(async (id) => {
 | 
				
			||||||
		ids.forEach(async (id) => {
 | 
							const res = await prisma[table].delete({
 | 
				
			||||||
			const res = await prisma[table].delete({
 | 
								where: { id }
 | 
				
			||||||
				where: { id }
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
			console.log(`deleted from ${table}: ${res.id}`)
 | 
					 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		revalidatePath(pathname)
 | 
							console.log(`deleted from ${table}: ${res.id}`)
 | 
				
			||||||
		return true
 | 
						})
 | 
				
			||||||
	} catch (error) {
 | 
						revalidatePath(pathname)
 | 
				
			||||||
		console.error(error)
 | 
						redirect(pathname)
 | 
				
			||||||
		return undefined
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					"use server"
 | 
				
			||||||
 | 
					import prisma from "./db"
 | 
				
			||||||
 | 
					import { revalidatePath } from "next/cache"
 | 
				
			||||||
 | 
					import { redirect } from "next/navigation"
 | 
				
			||||||
 | 
					import { SubForm } from "app/ui/forms/sub"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function editSubmission(data: SubForm) {
 | 
				
			||||||
 | 
						const res = await prisma.sub.update({
 | 
				
			||||||
 | 
							where: { id: data.id },
 | 
				
			||||||
 | 
							data
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						console.log(`updated ${data} to ${res}`)
 | 
				
			||||||
 | 
						revalidatePath("/submission")
 | 
				
			||||||
 | 
						redirect("/submission")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
export default function pluralize(word: string): string {
 | 
					export default function pluralize(word: "story" | "publication" | "submission"): string {
 | 
				
			||||||
	const map = {
 | 
						const map = {
 | 
				
			||||||
		story: "stories",
 | 
							story: "stories",
 | 
				
			||||||
		publication: "publications",
 | 
							publication: "publications",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,113 +1,30 @@
 | 
				
			||||||
"use server"
 | 
					"use server"
 | 
				
			||||||
import { prepGenreData, prepPubData, prepStoryData } from "./validate"
 | 
					import { Genre } from "@prisma/client"
 | 
				
			||||||
import { Genre, Pub, Story, Sub } from "@prisma/client"
 | 
					 | 
				
			||||||
import prisma from "./db"
 | 
					import prisma from "./db"
 | 
				
			||||||
import { revalidatePath } from "next/cache"
 | 
					import { revalidatePath } from "next/cache"
 | 
				
			||||||
import { subSchema } from "app/ui/forms/schemas"
 | 
					import { redirect } from "next/navigation"
 | 
				
			||||||
import { SubForm } from "app/ui/forms/sub"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateField({ datum, table, column, id, pathname }: { datum?: string | number | Genre[], table: string, column: string, id: number, pathname: string }) {
 | 
					export async function updateField({ datum, table, column, id, pathname }: { datum?: string | number | Genre[], table: string, column: string, id: number, pathname: string }) {
 | 
				
			||||||
	"use server"
 | 
						const res = await prisma[table].update({
 | 
				
			||||||
	try {
 | 
							where: { id },
 | 
				
			||||||
		const res = await prisma[table].update({
 | 
							data: {
 | 
				
			||||||
			where: { id },
 | 
								[column]: datum
 | 
				
			||||||
			data: {
 | 
							}
 | 
				
			||||||
				[column]: datum
 | 
						})
 | 
				
			||||||
			}
 | 
						console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
 | 
				
			||||||
		})
 | 
						revalidatePath(pathname)
 | 
				
			||||||
		console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
 | 
						redirect(pathname)
 | 
				
			||||||
		revalidatePath(pathname)
 | 
					 | 
				
			||||||
		return res
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function updateGenres({ genres, table, id, pathname }: { genres: { id: number }[], table: string, id: number, pathname: string }) {
 | 
					export async function updateGenres({ genres, table, id, pathname }: { genres: { id: number }[], table: string, id: number, pathname: string }) {
 | 
				
			||||||
	"use server"
 | 
						const res = await prisma[table].update({
 | 
				
			||||||
	try {
 | 
							where: { id },
 | 
				
			||||||
		const res = await prisma[table].update({
 | 
							data: {
 | 
				
			||||||
			where: { id },
 | 
								genres: { set: genres }
 | 
				
			||||||
			data: {
 | 
							}
 | 
				
			||||||
				genres: { set: genres }
 | 
						})
 | 
				
			||||||
			}
 | 
						console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
 | 
				
			||||||
		})
 | 
						revalidatePath(pathname)
 | 
				
			||||||
		console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
 | 
						redirect(pathname)
 | 
				
			||||||
		revalidatePath(pathname)
 | 
					 | 
				
			||||||
		return res
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function updateSub(data: SubForm): Promise<Sub> {
 | 
					 | 
				
			||||||
	"use server"
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		subSchema.parse(data)
 | 
					 | 
				
			||||||
		const res = await prisma.sub.update({ where: { id: data.id }, data })
 | 
					 | 
				
			||||||
		revalidatePath("submission")
 | 
					 | 
				
			||||||
		return res
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function updateStory(data: Story & { genres: number[] }): Promise<{ success: string }> {
 | 
					 | 
				
			||||||
	"use server"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		//prep and validate
 | 
					 | 
				
			||||||
		const storyData = await prepStoryData(data)
 | 
					 | 
				
			||||||
		const genresArray = await prepGenreData(data.genres)
 | 
					 | 
				
			||||||
		//submit
 | 
					 | 
				
			||||||
		const res = await prisma.story.update({
 | 
					 | 
				
			||||||
			where: { id: data.id },
 | 
					 | 
				
			||||||
			data: storyData
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		const genreRes = await prisma.story.update({
 | 
					 | 
				
			||||||
			where: { id: data.id },
 | 
					 | 
				
			||||||
			data: {
 | 
					 | 
				
			||||||
				genres: { set: genresArray }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		return { success: "Updated the story '" + res.title + "'." }
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export async function updatePub(data: Pub & { genres: number[] }): Promise<{ success: string }> {
 | 
					 | 
				
			||||||
	"use server"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		//prep and validate
 | 
					 | 
				
			||||||
		const pubData = await prepPubData
 | 
					 | 
				
			||||||
			(data)
 | 
					 | 
				
			||||||
		const genresArray = await prepGenreData(data.genres)
 | 
					 | 
				
			||||||
		//submit
 | 
					 | 
				
			||||||
		const res = await prisma.pub.update({
 | 
					 | 
				
			||||||
			where: { id: data.id },
 | 
					 | 
				
			||||||
			data: pubData
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		await prisma.pub.update({
 | 
					 | 
				
			||||||
			where: { id: data.id },
 | 
					 | 
				
			||||||
			data: {
 | 
					 | 
				
			||||||
				genres: { set: genresArray }
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
		return { success: "Updated the publication '" + res.title + "'" }
 | 
					 | 
				
			||||||
	} catch (error) {
 | 
					 | 
				
			||||||
		console.error(error)
 | 
					 | 
				
			||||||
		return null
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ export default async function Page({ params }: { params: { id: string } }) {
 | 
				
			||||||
      <PageHeader>{pub.title}</PageHeader>
 | 
					      <PageHeader>{pub.title}</PageHeader>
 | 
				
			||||||
      <GenreBadges genres={pub.genres} className="my-6" />
 | 
					      <GenreBadges genres={pub.genres} className="my-6" />
 | 
				
			||||||
      <PageSubHeader>Submissions:</PageSubHeader>
 | 
					      <PageSubHeader>Submissions:</PageSubHeader>
 | 
				
			||||||
      <DataTable columns={columns} data={pubSubs} tableName="sub" />
 | 
					      <DataTable columns={columns} data={pubSubs} type="submission" />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,19 @@
 | 
				
			||||||
"use client"
 | 
					"use client"
 | 
				
			||||||
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
					import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
				
			||||||
import { ArrowUpDown, BookType, Clock, Drama, SquareArrowOutUpRight } from "lucide-react"
 | 
					import { ArrowUpDown } from "lucide-react"
 | 
				
			||||||
import { Button } from "@/components/ui/button"
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
import { PubWithGenres } from "./page"
 | 
					import { Badge } from "@/components/ui/badge"
 | 
				
			||||||
 | 
					import { PubsWithGenres } from "./page"
 | 
				
			||||||
import { TextInputCell } from "app/ui/tables/inputs/textInput"
 | 
					import { TextInputCell } from "app/ui/tables/inputs/textInput"
 | 
				
			||||||
import { selectCol } from "app/ui/tables/selectColumn"
 | 
					import { selectCol } from "app/ui/tables/selectColumn"
 | 
				
			||||||
import NumberInputCell from "app/ui/tables/inputs/numberInput"
 | 
					import NumberInputCell from "app/ui/tables/inputs/numberInput"
 | 
				
			||||||
import { pubSchema } from "app/ui/forms/schemas"
 | 
					import { formSchema } from "app/ui/forms/pub"
 | 
				
			||||||
import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput"
 | 
					import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput"
 | 
				
			||||||
import { genrePickerFilterFn } from "app/lib/filterFns"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const columnHelper = createColumnHelper<PubWithGenres>()
 | 
					const columnHelper = createColumnHelper<PubsWithGenres>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const columns: ColumnDef<PubWithGenres>[] = [
 | 
					export const columns: ColumnDef<PubsWithGenres>[] = [
 | 
				
			||||||
  selectCol,
 | 
					  selectCol,
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorKey: "title",
 | 
					    accessorKey: "title",
 | 
				
			||||||
| 
						 | 
					@ -23,73 +23,35 @@ export const columns: ColumnDef<PubWithGenres>[] = [
 | 
				
			||||||
          variant="ghost"
 | 
					          variant="ghost"
 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span className="hidden sm:block">
 | 
					          Title
 | 
				
			||||||
            Title
 | 
					          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
          <span className="block sm:hidden"><BookType /></span>
 | 
					 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    cell: cell => (
 | 
					    cell: TextInputCell,
 | 
				
			||||||
      <>
 | 
					    meta: { formSchema }
 | 
				
			||||||
        {/* @ts-ignore */}
 | 
					 | 
				
			||||||
        <p className="block text-xs max-w-24 break-words md:hidden">{cell.getValue()}</p>
 | 
					 | 
				
			||||||
        <TextInputCell cellContext={cell} className="hidden md:block" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    meta: { formSchema: pubSchema }
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorKey: "link",
 | 
					    accessorKey: "link",
 | 
				
			||||||
    header: () => (
 | 
					    header: "Link",
 | 
				
			||||||
      <div className="mx-auto w-fit">
 | 
					    cell: TextInputCell,
 | 
				
			||||||
        <span className="hidden sm:block">Link</span>
 | 
					    meta: { formSchema }
 | 
				
			||||||
        <span className="block sm:hidden"><SquareArrowOutUpRight /></span>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    cell: cell => (
 | 
					 | 
				
			||||||
      <>
 | 
					 | 
				
			||||||
        {/* @ts-ignore */}
 | 
					 | 
				
			||||||
        <p className="block text-xs max-w-16 truncate md:hidden">{cell.getValue()}</p>
 | 
					 | 
				
			||||||
        <TextInputCell cellContext={cell} className="hidden md:block" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    meta: { formSchema: pubSchema }
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  columnHelper.accessor("genres", {
 | 
					  columnHelper.accessor("genres", {
 | 
				
			||||||
    header: () => (
 | 
					 | 
				
			||||||
      <div className="w-fit mx-auto">
 | 
					 | 
				
			||||||
        <span className="hidden sm:block">Genres</span>
 | 
					 | 
				
			||||||
        <span className="sm:hidden"><Drama /></span>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    cell: GenrePickerInputCell,
 | 
					    cell: GenrePickerInputCell,
 | 
				
			||||||
    filterFn: genrePickerFilterFn
 | 
					    filterFn: "arrIncludes"
 | 
				
			||||||
 | 
					    //TODO - write custom filter function, to account for an array of objects
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorKey: "query_after_days",
 | 
					    accessorKey: "query_after_days",
 | 
				
			||||||
    header: () => (
 | 
					    header: "Query After (days)",
 | 
				
			||||||
      <div>
 | 
					    cell: NumberInputCell,
 | 
				
			||||||
        <span className="hidden sm:block">Query After (days)</span>
 | 
					 | 
				
			||||||
        <span className="sm:hidden"><Clock /></span>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    enableColumnFilter: false,
 | 
					 | 
				
			||||||
    cell: cell => (
 | 
					 | 
				
			||||||
      <>
 | 
					 | 
				
			||||||
        {/* @ts-ignore */}
 | 
					 | 
				
			||||||
        <p className="block md:hidden text-center">{cell.getValue()}</p>
 | 
					 | 
				
			||||||
        <NumberInputCell cellContext={cell} className="hidden md:block" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    meta: {
 | 
					    meta: {
 | 
				
			||||||
      step: 10,
 | 
					      step: 10,
 | 
				
			||||||
      formSchema: pubSchema
 | 
					      formSchema
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,31 +5,21 @@ import { ComponentProps } from "react";
 | 
				
			||||||
import { Genre } from "@prisma/client";
 | 
					import { Genre } from "@prisma/client";
 | 
				
			||||||
import { createPub } from "app/lib/create";
 | 
					import { createPub } from "app/lib/create";
 | 
				
			||||||
import PubForm from "app/ui/forms/pub";
 | 
					import PubForm from "app/ui/forms/pub";
 | 
				
			||||||
import { Plus } from "lucide-react";
 | 
					 | 
				
			||||||
import { useState } from "react";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function CreatePubDialog({ genres }: ComponentProps<"div"> & { genres: Genre[] }) {
 | 
					export default function CreatePubDialog({ genres }: ComponentProps<"div"> & { genres: Genre[] }) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [isOpen, setIsOpen] = useState(false)
 | 
					 | 
				
			||||||
  function closeDialog() {
 | 
					 | 
				
			||||||
    setIsOpen(false)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
 | 
					    <Dialog>
 | 
				
			||||||
      <DialogTrigger asChild>
 | 
					      <DialogTrigger asChild>
 | 
				
			||||||
        <Button>
 | 
					        <Button>Create new publication</Button>
 | 
				
			||||||
          <span className="hidden md:block">Create new publication</span>
 | 
					 | 
				
			||||||
          <Plus className="block md:hidden" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					 | 
				
			||||||
      </DialogTrigger>
 | 
					      </DialogTrigger>
 | 
				
			||||||
      <DialogContent>
 | 
					      <DialogContent>
 | 
				
			||||||
        <DialogHeader>
 | 
					        <DialogHeader>
 | 
				
			||||||
          <DialogTitle>New publication</DialogTitle>
 | 
					          <DialogTitle>New publication</DialogTitle>
 | 
				
			||||||
          <DialogDescription>Create an entry for a new publication i.e. a place you intend to submit stories to.</DialogDescription>
 | 
					          <DialogDescription>Create an entry for a new publication i.e. a place you intend to submit stories to.</DialogDescription>
 | 
				
			||||||
        </DialogHeader>
 | 
					        </DialogHeader>
 | 
				
			||||||
        <PubForm dbAction={createPub} genres={genres} closeDialog={closeDialog} />
 | 
					        <PubForm createPub={createPub} genres={genres} />
 | 
				
			||||||
        <DialogFooter>
 | 
					        <DialogFooter>
 | 
				
			||||||
          <Button form="pubform">Submit</Button>
 | 
					          <Button form="pubform">Submit</Button>
 | 
				
			||||||
        </DialogFooter>
 | 
					        </DialogFooter>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import { columns } from "./columns";
 | 
				
			||||||
import { DataTable } from "app/ui/tables/data-table";
 | 
					import { DataTable } from "app/ui/tables/data-table";
 | 
				
			||||||
import CreatePubDialog from "./create";
 | 
					import CreatePubDialog from "./create";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type PubWithGenres = Pub & { genres: Array<Genre> }
 | 
					export type PubsWithGenres = Pub & { genres: Array<Genre> }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ export default async function Page() {
 | 
				
			||||||
  const genres = await getGenres()
 | 
					  const genres = await getGenres()
 | 
				
			||||||
  const pubs = await getPubsWithGenres()
 | 
					  const pubs = await getPubsWithGenres()
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="container px-0 md:px-4 mx-auto">
 | 
					    <div className="container mx-auto">
 | 
				
			||||||
      <DataTable data={pubs} columns={columns} tableName="pub" genres={genres}>
 | 
					      <DataTable data={pubs} columns={columns} tableName="pub" genres={genres}>
 | 
				
			||||||
        <CreatePubDialog genres={genres} />
 | 
					        <CreatePubDialog genres={genres} />
 | 
				
			||||||
      </DataTable>
 | 
					      </DataTable>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,14 @@
 | 
				
			||||||
"use client"
 | 
					"use client"
 | 
				
			||||||
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
					import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
				
			||||||
import { StoryWithGenres } from "./page"
 | 
					import { StoryWithGenres } from "./page"
 | 
				
			||||||
import { ArrowUpDown, BookType, Drama, Tally5 } from "lucide-react"
 | 
					import { ArrowUpDown } from "lucide-react"
 | 
				
			||||||
import { Button } from "@/components/ui/button"
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
 | 
					import GenreBadges from "app/ui/genreBadges"
 | 
				
			||||||
import { selectCol } from "app/ui/tables/selectColumn"
 | 
					import { selectCol } from "app/ui/tables/selectColumn"
 | 
				
			||||||
import NumberInputCell from "app/ui/tables/inputs/numberInput"
 | 
					import NumberInputCell from "app/ui/tables/inputs/numberInput"
 | 
				
			||||||
import { storySchema } from "app/ui/forms/schemas"
 | 
					import { formSchema } from "app/ui/forms/story"
 | 
				
			||||||
import { TextInputCell } from "app/ui/tables/inputs/textInput"
 | 
					import { TextInputCell } from "app/ui/tables/inputs/textInput"
 | 
				
			||||||
import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput"
 | 
					import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput"
 | 
				
			||||||
import { genrePickerFilterFn } from "app/lib/filterFns"
 | 
					 | 
				
			||||||
const columnHelper = createColumnHelper<StoryWithGenres>()
 | 
					const columnHelper = createColumnHelper<StoryWithGenres>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const columns: ColumnDef<StoryWithGenres>[] = [
 | 
					export const columns: ColumnDef<StoryWithGenres>[] = [
 | 
				
			||||||
| 
						 | 
					@ -19,25 +19,16 @@ export const columns: ColumnDef<StoryWithGenres>[] = [
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          variant="ghost"
 | 
					          variant="ghost"
 | 
				
			||||||
          className="px-1"
 | 
					 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span className="hidden sm:block">
 | 
					          Title
 | 
				
			||||||
            Title
 | 
					          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
          <span className="block sm:hidden"><BookType /></span>
 | 
					 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    cell: cell => (
 | 
					    cell: TextInputCell,
 | 
				
			||||||
      <>
 | 
					    meta: { formSchema }
 | 
				
			||||||
        {/* @ts-ignore */}
 | 
					
 | 
				
			||||||
        <p className="block break-words max-w-28 md:hidden text-xs">{cell.getValue()}</p>
 | 
					 | 
				
			||||||
        <TextInputCell cellContext={cell} className="hidden md:block" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    meta: { formSchema: storySchema }
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorKey: "word_count",
 | 
					    accessorKey: "word_count",
 | 
				
			||||||
| 
						 | 
					@ -45,41 +36,23 @@ export const columns: ColumnDef<StoryWithGenres>[] = [
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          variant="ghost"
 | 
					          variant="ghost"
 | 
				
			||||||
          className="px-1"
 | 
					 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span className="hidden sm:block">
 | 
					          Word Count
 | 
				
			||||||
            Word Count
 | 
					          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
          <span className="sm:hidden">
 | 
					 | 
				
			||||||
            <Tally5 />
 | 
					 | 
				
			||||||
          </span>
 | 
					 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    enableColumnFilter: false,
 | 
					    enableColumnFilter: false,
 | 
				
			||||||
    cell: cell => (
 | 
					    cell: NumberInputCell,
 | 
				
			||||||
      <>
 | 
					 | 
				
			||||||
        {/* @ts-ignore */}
 | 
					 | 
				
			||||||
        <p className="block md:hidden text-center text-xs">{cell.getValue()}</p>
 | 
					 | 
				
			||||||
        <NumberInputCell cellContext={cell} className="hidden md:block" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    meta: {
 | 
					    meta: {
 | 
				
			||||||
      step: 50,
 | 
					      step: 50,
 | 
				
			||||||
      formSchema: storySchema
 | 
					      formSchema
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  columnHelper.accessor("genres", {
 | 
					  columnHelper.accessor("genres", {
 | 
				
			||||||
    header: () => (
 | 
					 | 
				
			||||||
      <div className="w-fit mx-auto">
 | 
					 | 
				
			||||||
        <span className="hidden sm:block">Genres</span>
 | 
					 | 
				
			||||||
        <span className="sm:hidden"><Drama /></span>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    cell: GenrePickerInputCell,
 | 
					    cell: GenrePickerInputCell,
 | 
				
			||||||
    filterFn: genrePickerFilterFn,
 | 
					    filterFn: "arrIncludes",
 | 
				
			||||||
    meta: {}
 | 
					    meta: {}
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,33 +2,24 @@
 | 
				
			||||||
import { createStory } from "app/lib/create"
 | 
					import { createStory } from "app/lib/create"
 | 
				
			||||||
import { Dialog, DialogHeader, DialogTrigger, DialogContent, DialogClose, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
 | 
					import { Dialog, DialogHeader, DialogTrigger, DialogContent, DialogClose, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
 | 
				
			||||||
import { Button } from "@/components/ui/button";
 | 
					import { Button } from "@/components/ui/button";
 | 
				
			||||||
import { ComponentProps, useState } from "react";
 | 
					import { ComponentProps } from "react";
 | 
				
			||||||
import { Genre } from "@prisma/client";
 | 
					import { Genre } from "@prisma/client";
 | 
				
			||||||
import StoryForm from "app/ui/forms/story";
 | 
					import StoryForm from "app/ui/forms/story";
 | 
				
			||||||
import { Plus } from "lucide-react";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function CreateStoryDialog({ genres }: ComponentProps<"div"> & { genres: Genre[] }) {
 | 
					export default function CreateStoryDialog({ genres }: ComponentProps<"div"> & { genres: Genre[] }) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [isOpen, setIsOpen] = useState(false)
 | 
					 | 
				
			||||||
  function closeDialog() {
 | 
					 | 
				
			||||||
    setIsOpen(false)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
 | 
					    <Dialog>
 | 
				
			||||||
      <DialogTrigger asChild>
 | 
					      <DialogTrigger asChild>
 | 
				
			||||||
        <div>
 | 
					        <Button>Create new story</Button>
 | 
				
			||||||
          <Button className="hidden md:block">Create new story</Button>
 | 
					 | 
				
			||||||
          <Button className="block md:hidden"><Plus /> </Button>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </DialogTrigger>
 | 
					      </DialogTrigger>
 | 
				
			||||||
      <DialogContent>
 | 
					      <DialogContent>
 | 
				
			||||||
        <DialogHeader>
 | 
					        <DialogHeader>
 | 
				
			||||||
          <DialogTitle>New story</DialogTitle>
 | 
					          <DialogTitle>New story</DialogTitle>
 | 
				
			||||||
          <DialogDescription>Create an entry for a new story i.e. a thing you intend to submit for publication.</DialogDescription>
 | 
					          <DialogDescription>Create an entry for a new story i.e. a thing you intend to submit for publication.</DialogDescription>
 | 
				
			||||||
        </DialogHeader>
 | 
					        </DialogHeader>
 | 
				
			||||||
        <StoryForm dbAction={createStory} genres={genres} className="" closeDialog={closeDialog} />
 | 
					        <StoryForm createStory={createStory} genres={genres} existingData={null} />
 | 
				
			||||||
        <DialogFooter>
 | 
					        <DialogFooter>
 | 
				
			||||||
          <Button form="storyform">Submit</Button>
 | 
					          <Button form="storyform">Submit</Button>
 | 
				
			||||||
        </DialogFooter>
 | 
					        </DialogFooter>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,10 +4,9 @@ import prisma from "app/lib/db";
 | 
				
			||||||
import { revalidatePath } from "next/cache";
 | 
					import { revalidatePath } from "next/cache";
 | 
				
			||||||
import { redirect } from "next/navigation";
 | 
					import { redirect } from "next/navigation";
 | 
				
			||||||
import { CreateContainerContent, CreateContainerHeader, CreateContainer, CreateContainerDescription } from "app/ui/createContainer";
 | 
					import { CreateContainerContent, CreateContainerHeader, CreateContainer, CreateContainerDescription } from "app/ui/createContainer";
 | 
				
			||||||
import { Story } from "@prisma/client";
 | 
					 | 
				
			||||||
export default async function Page() {
 | 
					export default async function Page() {
 | 
				
			||||||
	const genres = await getGenres()
 | 
						const genres = await getGenres()
 | 
				
			||||||
	async function createStory(data: Story & { genres: number[] }): Promise<{ success: string }> {
 | 
						async function createStory(data) {
 | 
				
			||||||
		"use server"
 | 
							"use server"
 | 
				
			||||||
		const genresArray = data.genres.map(e => { return { id: e } })
 | 
							const genresArray = data.genres.map(e => { return { id: e } })
 | 
				
			||||||
		const res = await prisma.story.create({
 | 
							const res = await prisma.story.create({
 | 
				
			||||||
| 
						 | 
					@ -27,14 +26,12 @@ export default async function Page() {
 | 
				
			||||||
		revalidatePath("/story")
 | 
							revalidatePath("/story")
 | 
				
			||||||
		redirect("/story")
 | 
							redirect("/story")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<CreateContainer>
 | 
							<CreateContainer>
 | 
				
			||||||
			<CreateContainerHeader>New story</CreateContainerHeader>
 | 
								<CreateContainerHeader>New story</CreateContainerHeader>
 | 
				
			||||||
			<CreateContainerContent>
 | 
								<CreateContainerContent>
 | 
				
			||||||
				<CreateContainerDescription>Make an entry for a new work of fiction i.e. a thing you intend to submit for publication.</CreateContainerDescription>
 | 
									<CreateContainerDescription>Make an entry for a new work of fiction i.e. a thing you intend to submit for publication.</CreateContainerDescription>
 | 
				
			||||||
				<StoryForm genres={genres} dbAction={createStory} className="mt-6" />
 | 
									<StoryForm genres={genres} createStory={createStory} className="mt-6" />
 | 
				
			||||||
			</CreateContainerContent>
 | 
								</CreateContainerContent>
 | 
				
			||||||
		</CreateContainer>
 | 
							</CreateContainer>
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ export default async function Page() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="container px-1 md:px-4 mx-auto">
 | 
					    <div className="container mx-auto">
 | 
				
			||||||
      <DataTable columns={columns} data={storiesWithGenres} tableName="story"
 | 
					      <DataTable columns={columns} data={storiesWithGenres} tableName="story"
 | 
				
			||||||
        genres={genres}
 | 
					        genres={genres}
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					import { LineChart, Line, CartesianGrid, XAxis, YAxis, PieChart, Pie } from "recharts"
 | 
				
			||||||
 | 
					import { SubComplete } from "./page"
 | 
				
			||||||
 | 
					export function SubsChart({ data }: { data: Array<SubComplete> }) {
 | 
				
			||||||
 | 
					  const pieData: Array<{ story: string, occurrences: number }> = []
 | 
				
			||||||
 | 
					  data.forEach(dataRow => {
 | 
				
			||||||
 | 
					    const story = dataRow.story.title
 | 
				
			||||||
 | 
					    const exists = pieData.findIndex(pieRow => story === pieRow.story)
 | 
				
			||||||
 | 
					    if (exists === -1) {
 | 
				
			||||||
 | 
					      //add the story to pieData if it doesn't already exist
 | 
				
			||||||
 | 
					      pieData.push({ story: story, occurrences: 0 })
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pieData[exists].occurrences++
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  console.log(pieData)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <PieChart width={400} height={400}>
 | 
				
			||||||
 | 
					        <Pie data={pieData} dataKey="story" outerRadius={50} fill="teal" />
 | 
				
			||||||
 | 
					      </PieChart>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <LineChart width={400} height={400} data={data}>
 | 
				
			||||||
 | 
					        <Line type="monotone" dataKey="id" stroke="#8884d8" />
 | 
				
			||||||
 | 
					        <CartesianGrid />
 | 
				
			||||||
 | 
					        <XAxis dataKey="submitted" />
 | 
				
			||||||
 | 
					        <YAxis />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      </LineChart>
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
"use client"
 | 
					"use client"
 | 
				
			||||||
import { CellContext, ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
					import { CellContext, ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
				
			||||||
import { ArrowUpDown, BookText, CalendarMinus, CalendarPlus, MessageCircleReply, NotepadText } from "lucide-react"
 | 
					import { ArrowUpDown } from "lucide-react"
 | 
				
			||||||
import { Button } from "@/components/ui/button"
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
import { SubComplete } from "./page"
 | 
					import { SubComplete } from "./page"
 | 
				
			||||||
import { selectCol } from "app/ui/tables/selectColumn"
 | 
					import { selectCol } from "app/ui/tables/selectColumn"
 | 
				
			||||||
import TitleContainer from "app/ui/titleContainer"
 | 
					import TitleContainer from "app/ui/titleContainer"
 | 
				
			||||||
import { CalendarArrowUp } from "lucide"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,12 +18,7 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
				
			||||||
      return "RECORD DELETED"
 | 
					      return "RECORD DELETED"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    id: "story",
 | 
					    id: "story",
 | 
				
			||||||
    header: () => (
 | 
					    header: "Story",
 | 
				
			||||||
      <>
 | 
					 | 
				
			||||||
        <span className="hidden md:block">Story</span>
 | 
					 | 
				
			||||||
        <NotepadText className="block md:hidden" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    cell: (props: CellContext<any, any>) => (<TitleContainer>{props.getValue()}</TitleContainer>)
 | 
					    cell: (props: CellContext<any, any>) => (<TitleContainer>{props.getValue()}</TitleContainer>)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
| 
						 | 
					@ -35,12 +29,7 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
				
			||||||
      return "RECORD DELETED"
 | 
					      return "RECORD DELETED"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    id: "pub",
 | 
					    id: "pub",
 | 
				
			||||||
    header: () => (
 | 
					    header: "Publication",
 | 
				
			||||||
      <>
 | 
					 | 
				
			||||||
        <span className="hidden md:block">Publication</span>
 | 
					 | 
				
			||||||
        <BookText className="block md:hidden" />
 | 
					 | 
				
			||||||
      </>
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    cell: (props: CellContext<any, any>) => (<TitleContainer>{props.getValue()}</TitleContainer>)
 | 
					    cell: (props: CellContext<any, any>) => (<TitleContainer>{props.getValue()}</TitleContainer>)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
| 
						 | 
					@ -50,24 +39,16 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          variant="ghost"
 | 
					          variant="ghost"
 | 
				
			||||||
          className="p-0 flex justify-center w-full"
 | 
					 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span className="hidden md:block"> Date Submitted </span>
 | 
					          Date Submitted
 | 
				
			||||||
          <CalendarPlus className="block md:hidden" />
 | 
					          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    enableColumnFilter: false,
 | 
					    enableColumnFilter: false,
 | 
				
			||||||
    sortingFn: "datetime",
 | 
					    sortingFn: "datetime",
 | 
				
			||||||
    cell: (props: CellContext<any, any>) => (
 | 
					    cell: (props: CellContext<any, any>) => (<p className="w-full text-center">{props.getValue().toLocaleDateString()}</p>)
 | 
				
			||||||
      <p className="w-full text-center text-xs md:text-sm">{props.getValue().toLocaleDateString('ES', {
 | 
					 | 
				
			||||||
        day: 'numeric',
 | 
					 | 
				
			||||||
        month: 'numeric',
 | 
					 | 
				
			||||||
        year: '2-digit'
 | 
					 | 
				
			||||||
      })}</p>
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorFn: row => row.responded ? new Date(row.responded) : null,
 | 
					    accessorFn: row => row.responded ? new Date(row.responded) : null,
 | 
				
			||||||
| 
						 | 
					@ -76,22 +57,16 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
				
			||||||
      return (
 | 
					      return (
 | 
				
			||||||
        <Button
 | 
					        <Button
 | 
				
			||||||
          variant="ghost"
 | 
					          variant="ghost"
 | 
				
			||||||
          className="p-0 flex justify-center w-full"
 | 
					 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <span className="hidden md:block"> Date Responded </span>
 | 
					          Date Responded
 | 
				
			||||||
          <CalendarMinus className="block md:hidden" />
 | 
					          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					        </Button>
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    enableColumnFilter: false,
 | 
					    enableColumnFilter: false,
 | 
				
			||||||
    sortingFn: "datetime",
 | 
					    sortingFn: "datetime",
 | 
				
			||||||
    cell: (props: CellContext<any, any>) => (<p className="w-full text-center text-xs md:text-sm">{props.getValue()?.toLocaleDateString('ES', {
 | 
					    cell: (props: CellContext<any, any>) => (<p className="w-full text-center">{props.getValue()?.toLocaleDateString()}</p>)
 | 
				
			||||||
      day: 'numeric',
 | 
					 | 
				
			||||||
      month: 'numeric',
 | 
					 | 
				
			||||||
      year: '2-digit'
 | 
					 | 
				
			||||||
    })}</p>)
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    accessorFn: row => {
 | 
					    accessorFn: row => {
 | 
				
			||||||
| 
						 | 
					@ -101,20 +76,8 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
				
			||||||
      return "RECORD DELETED"
 | 
					      return "RECORD DELETED"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    id: "response",
 | 
					    id: "response",
 | 
				
			||||||
    header: ({ column }) => {
 | 
					    header: "Response",
 | 
				
			||||||
      return (
 | 
					    cell: (props: CellContext<any, any>) => (<p className="w-full text-center">{props.getValue()}</p>)
 | 
				
			||||||
        <Button
 | 
					 | 
				
			||||||
          variant="ghost"
 | 
					 | 
				
			||||||
          className="p-0 flex justify-center w-full"
 | 
					 | 
				
			||||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
					 | 
				
			||||||
        >
 | 
					 | 
				
			||||||
          <span className="hidden md:block"> Response </span>
 | 
					 | 
				
			||||||
          <MessageCircleReply className="block md:hidden" />
 | 
					 | 
				
			||||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					 | 
				
			||||||
      )
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    cell: (props: CellContext<any, any>) => (<p className="w-full text-center text-xs md:text-sm">{props.getValue()}</p>)
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,33 +6,33 @@ import { Button } from "@/components/ui/button";
 | 
				
			||||||
import { ComponentProps } from "react";
 | 
					import { ComponentProps } from "react";
 | 
				
			||||||
import { Pub, Response, Story } from "@prisma/client";
 | 
					import { Pub, Response, Story } from "@prisma/client";
 | 
				
			||||||
import SubmissionForm from "app/ui/forms/sub";
 | 
					import SubmissionForm from "app/ui/forms/sub";
 | 
				
			||||||
import { Plus } from "lucide-react";
 | 
					 | 
				
			||||||
import { useState } from "react";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type CreateSubDefaults = {
 | 
				
			||||||
 | 
					  subId?: number,
 | 
				
			||||||
 | 
					  storyId: number,
 | 
				
			||||||
 | 
					  pubId: number,
 | 
				
			||||||
 | 
					  submitted: Date,
 | 
				
			||||||
 | 
					  responded: Date,
 | 
				
			||||||
 | 
					  respoonseId: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function CreateSubmissionDialog({ stories, pubs, responses }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[] }) {
 | 
					export default function CreateSubmissionDialog({ stories, pubs, responses, defaults }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[], defaults?: CreateSubDefaults }) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [isOpen, setIsOpen] = useState(false)
 | 
					 | 
				
			||||||
  function closeDialog() {
 | 
					 | 
				
			||||||
    setIsOpen(false)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
 | 
					    <Dialog>
 | 
				
			||||||
      <DialogTrigger asChild>
 | 
					      <DialogTrigger asChild>
 | 
				
			||||||
        <Button>
 | 
					        <Button>Create new submission</Button>
 | 
				
			||||||
          <span className="hidden md:block">Create new submission</span>
 | 
					 | 
				
			||||||
          <Plus className="block md:hidden" />
 | 
					 | 
				
			||||||
        </Button>
 | 
					 | 
				
			||||||
      </DialogTrigger>
 | 
					      </DialogTrigger>
 | 
				
			||||||
      <DialogContent className="text-xs md:text-sm">
 | 
					      <DialogContent>
 | 
				
			||||||
        <DialogHeader>
 | 
					        <DialogHeader>
 | 
				
			||||||
          <DialogTitle>New submission</DialogTitle>
 | 
					          <DialogTitle>New submission</DialogTitle>
 | 
				
			||||||
          <DialogDescription>Create an entry for a new story i.e. a thing you intend to submit for publication.</DialogDescription>
 | 
					          <DialogDescription>Create an entry for a new story i.e. a thing you intend to submit for publication.</DialogDescription>
 | 
				
			||||||
        </DialogHeader>
 | 
					        </DialogHeader>
 | 
				
			||||||
        <SubmissionForm pubs={pubs} responses={responses} stories={stories} closeDialog={closeDialog} />
 | 
					        <SubmissionForm createSub={createSub} pubs={pubs} responses={responses} stories={stories} defaults={defaults} />
 | 
				
			||||||
        <DialogFooter>
 | 
					        <DialogFooter>
 | 
				
			||||||
 | 
					          <DialogClose asChild>
 | 
				
			||||||
 | 
					          </DialogClose>
 | 
				
			||||||
          <Button form="subform">Submit</Button>
 | 
					          <Button form="subform">Submit</Button>
 | 
				
			||||||
        </DialogFooter>
 | 
					        </DialogFooter>
 | 
				
			||||||
      </DialogContent>
 | 
					      </DialogContent>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,12 +10,19 @@ export default async function Page() {
 | 
				
			||||||
	const stories = await getStories()
 | 
						const stories = await getStories()
 | 
				
			||||||
	const pubs = await getPubs()
 | 
						const pubs = await getPubs()
 | 
				
			||||||
	const responses = await getResponses()
 | 
						const responses = await getResponses()
 | 
				
			||||||
 | 
						async function createSub(data) {
 | 
				
			||||||
 | 
							"use server"
 | 
				
			||||||
 | 
							const res = await prisma.sub.create({ data })
 | 
				
			||||||
 | 
							console.log(res)
 | 
				
			||||||
 | 
							revalidatePath("/submission")
 | 
				
			||||||
 | 
							redirect("/submission")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<CreateContainer>
 | 
							<CreateContainer>
 | 
				
			||||||
			<CreateContainerHeader>New submission</CreateContainerHeader>
 | 
								<CreateContainerHeader>New submission</CreateContainerHeader>
 | 
				
			||||||
			<CreateContainerContent>
 | 
								<CreateContainerContent>
 | 
				
			||||||
				<SubmissionForm stories={stories} pubs={pubs} responses={responses} />
 | 
									<SubmissionForm stories={stories} pubs={pubs} responses={responses} createSub={createSub} />
 | 
				
			||||||
			</CreateContainerContent>
 | 
								</CreateContainerContent>
 | 
				
			||||||
		</CreateContainer>
 | 
							</CreateContainer>
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,14 @@
 | 
				
			||||||
"use client"
 | 
					"use client"
 | 
				
			||||||
import { DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
 | 
					import { createSub } from "app/lib/create"
 | 
				
			||||||
 | 
					import { Dialog, DialogHeader, DialogTrigger, DialogContent, DialogClose, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
 | 
				
			||||||
import { Button } from "@/components/ui/button";
 | 
					import { Button } from "@/components/ui/button";
 | 
				
			||||||
import { ComponentProps } from "react";
 | 
					import { ComponentProps } from "react";
 | 
				
			||||||
import { Pub, Response, Story } from "@prisma/client";
 | 
					import { Pub, Response, Story } from "@prisma/client";
 | 
				
			||||||
import { SubForm } from "app/ui/forms/sub";
 | 
					import SubmissionForm, { SubForm } from "app/ui/forms/sub";
 | 
				
			||||||
import EditSubmissionForm from "app/ui/forms/editSub";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function EditSubmissionDialog({ stories, pubs, responses, defaults, children, closeDialog }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[], defaults: SubForm, closeDialog: () => void }) {
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function EditSubmissionDialog({ stories, pubs, responses, defaults, children }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[], defaults: SubForm }) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
| 
						 | 
					@ -16,8 +16,10 @@ export default function EditSubmissionDialog({ stories, pubs, responses, default
 | 
				
			||||||
        <DialogTitle>Edit Submission</DialogTitle>
 | 
					        <DialogTitle>Edit Submission</DialogTitle>
 | 
				
			||||||
        <DialogDescription>Change response status, edit dates etc</DialogDescription>
 | 
					        <DialogDescription>Change response status, edit dates etc</DialogDescription>
 | 
				
			||||||
      </DialogHeader>
 | 
					      </DialogHeader>
 | 
				
			||||||
      <EditSubmissionForm pubs={pubs} responses={responses} stories={stories} defaults={defaults} closeDialog={closeDialog} />
 | 
					      <SubmissionForm pubs={pubs} responses={responses} stories={stories} defaults={defaults} />
 | 
				
			||||||
      <DialogFooter>
 | 
					      <DialogFooter>
 | 
				
			||||||
 | 
					        <DialogClose asChild>
 | 
				
			||||||
 | 
					        </DialogClose>
 | 
				
			||||||
        <Button form="subform">Submit</Button>
 | 
					        <Button form="subform">Submit</Button>
 | 
				
			||||||
      </DialogFooter>
 | 
					      </DialogFooter>
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ import { DataTable } from "app/ui/tables/data-table"
 | 
				
			||||||
import { columns } from "./columns"
 | 
					import { columns } from "./columns"
 | 
				
			||||||
import { Pub, Response, Story, Sub } from "@prisma/client"
 | 
					import { Pub, Response, Story, Sub } from "@prisma/client"
 | 
				
			||||||
import CreateSubmissionDialog from "./create"
 | 
					import CreateSubmissionDialog from "./create"
 | 
				
			||||||
 | 
					import { Trash2 } from "lucide-react"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SubComplete = Sub & {
 | 
					export type SubComplete = Sub & {
 | 
				
			||||||
  pub: Pub,
 | 
					  pub: Pub,
 | 
				
			||||||
| 
						 | 
					@ -10,15 +11,13 @@ export type SubComplete = Sub & {
 | 
				
			||||||
  response: Response
 | 
					  response: Response
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export default async function Page() {
 | 
					export default async function Page() {
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const subs: Array<SubComplete> = await getSubsComplete()
 | 
					  const subs: Array<SubComplete> = await getSubsComplete()
 | 
				
			||||||
  const stories = await getStories()
 | 
					  const stories = await getStories()
 | 
				
			||||||
  const pubs = await getPubs()
 | 
					  const pubs = await getPubs()
 | 
				
			||||||
  const responses = await getResponses()
 | 
					  const responses = await getResponses()
 | 
				
			||||||
  const genres = await getGenres()
 | 
					  const genres = await getGenres()
 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className="container px-1 md:px-4 mx-auto">
 | 
					    <div className="container">
 | 
				
			||||||
      <DataTable data={subs} columns={columns} tableName="sub"
 | 
					      <DataTable data={subs} columns={columns} tableName="sub"
 | 
				
			||||||
        stories={stories}
 | 
					        stories={stories}
 | 
				
			||||||
        pubs={pubs}
 | 
					        pubs={pubs}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -740,9 +740,8 @@ body {
 | 
				
			||||||
  margin-bottom: 1.5rem;
 | 
					  margin-bottom: 1.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.my-auto {
 | 
					.mb-4 {
 | 
				
			||||||
  margin-top: auto;
 | 
					  margin-bottom: 1rem;
 | 
				
			||||||
  margin-bottom: auto;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.ml-2 {
 | 
					.ml-2 {
 | 
				
			||||||
| 
						 | 
					@ -753,22 +752,10 @@ body {
 | 
				
			||||||
  margin-left: auto;
 | 
					  margin-left: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.mr-2 {
 | 
					 | 
				
			||||||
  margin-right: 0.5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mr-4 {
 | 
					 | 
				
			||||||
  margin-right: 1rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mt-2 {
 | 
					.mt-2 {
 | 
				
			||||||
  margin-top: 0.5rem;
 | 
					  margin-top: 0.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.mt-20 {
 | 
					 | 
				
			||||||
  margin-top: 5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mt-3 {
 | 
					.mt-3 {
 | 
				
			||||||
  margin-top: 0.75rem;
 | 
					  margin-top: 0.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -781,8 +768,8 @@ body {
 | 
				
			||||||
  margin-top: 1.5rem;
 | 
					  margin-top: 1.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.block {
 | 
					.mt-auto {
 | 
				
			||||||
  display: block;
 | 
					  margin-top: auto;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.flex {
 | 
					.flex {
 | 
				
			||||||
| 
						 | 
					@ -805,10 +792,6 @@ body {
 | 
				
			||||||
  display: grid;
 | 
					  display: grid;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.hidden {
 | 
					 | 
				
			||||||
  display: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.size-full {
 | 
					.size-full {
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  height: 100%;
 | 
					  height: 100%;
 | 
				
			||||||
| 
						 | 
					@ -850,6 +833,10 @@ body {
 | 
				
			||||||
  height: 1.25rem;
 | 
					  height: 1.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.h-5\/6 {
 | 
				
			||||||
 | 
					  height: 83.333333%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h-7 {
 | 
					.h-7 {
 | 
				
			||||||
  height: 1.75rem;
 | 
					  height: 1.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -927,6 +914,10 @@ body {
 | 
				
			||||||
  width: 10rem;
 | 
					  width: 10rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.w-5\/6 {
 | 
				
			||||||
 | 
					  width: 83.333333%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.w-7 {
 | 
					.w-7 {
 | 
				
			||||||
  width: 1.75rem;
 | 
					  width: 1.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -981,26 +972,6 @@ body {
 | 
				
			||||||
  min-width: fit-content;
 | 
					  min-width: fit-content;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.max-w-16 {
 | 
					 | 
				
			||||||
  max-width: 4rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.max-w-24 {
 | 
					 | 
				
			||||||
  max-width: 6rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.max-w-28 {
 | 
					 | 
				
			||||||
  max-width: 7rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.max-w-32 {
 | 
					 | 
				
			||||||
  max-width: 8rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.max-w-60 {
 | 
					 | 
				
			||||||
  max-width: 15rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.max-w-full {
 | 
					.max-w-full {
 | 
				
			||||||
  max-width: 100%;
 | 
					  max-width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1013,6 +984,10 @@ body {
 | 
				
			||||||
  max-width: 24rem;
 | 
					  max-width: 24rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.max-w-xs {
 | 
				
			||||||
 | 
					  max-width: 20rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.shrink-0 {
 | 
					.shrink-0 {
 | 
				
			||||||
  flex-shrink: 0;
 | 
					  flex-shrink: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1121,10 +1096,6 @@ body {
 | 
				
			||||||
  justify-content: space-between;
 | 
					  justify-content: space-between;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.justify-around {
 | 
					 | 
				
			||||||
  justify-content: space-around;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.gap-1 {
 | 
					.gap-1 {
 | 
				
			||||||
  gap: 0.25rem;
 | 
					  gap: 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1204,12 +1175,6 @@ body {
 | 
				
			||||||
  margin-bottom: calc(1rem * var(--tw-space-y-reverse));
 | 
					  margin-bottom: calc(1rem * var(--tw-space-y-reverse));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
 | 
					 | 
				
			||||||
  --tw-space-y-reverse: 0;
 | 
					 | 
				
			||||||
  margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
 | 
					 | 
				
			||||||
  margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.space-y-8 > :not([hidden]) ~ :not([hidden]) {
 | 
					.space-y-8 > :not([hidden]) ~ :not([hidden]) {
 | 
				
			||||||
  --tw-space-y-reverse: 0;
 | 
					  --tw-space-y-reverse: 0;
 | 
				
			||||||
  margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
 | 
					  margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
 | 
				
			||||||
| 
						 | 
					@ -1228,24 +1193,10 @@ body {
 | 
				
			||||||
  overflow: hidden;
 | 
					  overflow: hidden;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.truncate {
 | 
					 | 
				
			||||||
  overflow: hidden;
 | 
					 | 
				
			||||||
  text-overflow: ellipsis;
 | 
					 | 
				
			||||||
  white-space: nowrap;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.whitespace-nowrap {
 | 
					.whitespace-nowrap {
 | 
				
			||||||
  white-space: nowrap;
 | 
					  white-space: nowrap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.break-words {
 | 
					 | 
				
			||||||
  overflow-wrap: break-word;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.rounded-3xl {
 | 
					 | 
				
			||||||
  border-radius: 1.5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.rounded-full {
 | 
					.rounded-full {
 | 
				
			||||||
  border-radius: 9999px;
 | 
					  border-radius: 9999px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1258,6 +1209,11 @@ body {
 | 
				
			||||||
  border-radius: calc(var(--radius) - 4px);
 | 
					  border-radius: calc(var(--radius) - 4px);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.rounded-l-3xl {
 | 
				
			||||||
 | 
					  border-top-left-radius: 1.5rem;
 | 
				
			||||||
 | 
					  border-bottom-left-radius: 1.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.rounded-t-3xl {
 | 
					.rounded-t-3xl {
 | 
				
			||||||
  border-top-left-radius: 1.5rem;
 | 
					  border-top-left-radius: 1.5rem;
 | 
				
			||||||
  border-top-right-radius: 1.5rem;
 | 
					  border-top-right-radius: 1.5rem;
 | 
				
			||||||
| 
						 | 
					@ -1388,16 +1344,6 @@ body {
 | 
				
			||||||
  padding: 1.5rem;
 | 
					  padding: 1.5rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.px-0 {
 | 
					 | 
				
			||||||
  padding-left: 0px;
 | 
					 | 
				
			||||||
  padding-right: 0px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.px-1 {
 | 
					 | 
				
			||||||
  padding-left: 0.25rem;
 | 
					 | 
				
			||||||
  padding-right: 0.25rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.px-2 {
 | 
					.px-2 {
 | 
				
			||||||
  padding-left: 0.5rem;
 | 
					  padding-left: 0.5rem;
 | 
				
			||||||
  padding-right: 0.5rem;
 | 
					  padding-right: 0.5rem;
 | 
				
			||||||
| 
						 | 
					@ -1498,6 +1444,11 @@ body {
 | 
				
			||||||
  line-height: 2.25rem;
 | 
					  line-height: 2.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.text-4xl {
 | 
				
			||||||
 | 
					  font-size: 2.25rem;
 | 
				
			||||||
 | 
					  line-height: 2.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.text-\[0\.8rem\] {
 | 
					.text-\[0\.8rem\] {
 | 
				
			||||||
  font-size: 0.8rem;
 | 
					  font-size: 0.8rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2352,22 +2303,6 @@ body {
 | 
				
			||||||
    top: auto;
 | 
					    top: auto;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .sm\:block {
 | 
					 | 
				
			||||||
    display: block;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .sm\:hidden {
 | 
					 | 
				
			||||||
    display: none;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .sm\:h-6 {
 | 
					 | 
				
			||||||
    height: 1.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .sm\:w-6 {
 | 
					 | 
				
			||||||
    width: 1.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .sm\:flex-row {
 | 
					  .sm\:flex-row {
 | 
				
			||||||
    flex-direction: row;
 | 
					    flex-direction: row;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -2406,87 +2341,15 @@ body {
 | 
				
			||||||
    text-align: left;
 | 
					    text-align: left;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .sm\:text-4xl {
 | 
					 | 
				
			||||||
    font-size: 2.25rem;
 | 
					 | 
				
			||||||
    line-height: 2.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] {
 | 
					  .data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open] {
 | 
				
			||||||
    --tw-enter-translate-y: 100%;
 | 
					    --tw-enter-translate-y: 100%;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media (min-width: 768px) {
 | 
					@media (min-width: 768px) {
 | 
				
			||||||
  .md\:mt-6 {
 | 
					 | 
				
			||||||
    margin-top: 1.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:mt-auto {
 | 
					 | 
				
			||||||
    margin-top: auto;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:block {
 | 
					 | 
				
			||||||
    display: block;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:hidden {
 | 
					 | 
				
			||||||
    display: none;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:w-24 {
 | 
					 | 
				
			||||||
    width: 6rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:w-5\/6 {
 | 
					 | 
				
			||||||
    width: 83.333333%;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:max-w-\[420px\] {
 | 
					  .md\:max-w-\[420px\] {
 | 
				
			||||||
    max-width: 420px;
 | 
					    max-width: 420px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:flex-row {
 | 
					 | 
				
			||||||
    flex-direction: row;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:flex-col {
 | 
					 | 
				
			||||||
    flex-direction: column;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:space-y-8 > :not([hidden]) ~ :not([hidden]) {
 | 
					 | 
				
			||||||
    --tw-space-y-reverse: 0;
 | 
					 | 
				
			||||||
    margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse)));
 | 
					 | 
				
			||||||
    margin-bottom: calc(2rem * var(--tw-space-y-reverse));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:rounded-l-3xl {
 | 
					 | 
				
			||||||
    border-top-left-radius: 1.5rem;
 | 
					 | 
				
			||||||
    border-bottom-left-radius: 1.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:p-4 {
 | 
					 | 
				
			||||||
    padding: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:px-4 {
 | 
					 | 
				
			||||||
    padding-left: 1rem;
 | 
					 | 
				
			||||||
    padding-right: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:py-4 {
 | 
					 | 
				
			||||||
    padding-top: 1rem;
 | 
					 | 
				
			||||||
    padding-bottom: 1rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:text-base {
 | 
					 | 
				
			||||||
    font-size: 1rem;
 | 
					 | 
				
			||||||
    line-height: 1.5rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  .md\:text-sm {
 | 
					 | 
				
			||||||
    font-size: 0.875rem;
 | 
					 | 
				
			||||||
    line-height: 1.25rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]) {
 | 
					.\[\&\:has\(\[aria-selected\]\)\]\:bg-accent:has([aria-selected]) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					import { CheckboxReactHookFormMultiple } from "app/ui/forms/Checkboxdemo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function Page() {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <CheckboxReactHookFormMultiple />
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,121 @@
 | 
				
			||||||
 | 
					"use client"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { z } from "zod"
 | 
				
			||||||
 | 
					import { zodResolver } from "@hookform/resolvers/zod"
 | 
				
			||||||
 | 
					import { useForm } from "react-hook-form"
 | 
				
			||||||
 | 
					import { Genre } from "@prisma/client"
 | 
				
			||||||
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
						Form,
 | 
				
			||||||
 | 
						FormControl,
 | 
				
			||||||
 | 
						FormDescription,
 | 
				
			||||||
 | 
						FormField,
 | 
				
			||||||
 | 
						FormItem,
 | 
				
			||||||
 | 
						FormLabel,
 | 
				
			||||||
 | 
						FormMessage,
 | 
				
			||||||
 | 
					} from "@/components/ui/form"
 | 
				
			||||||
 | 
					import { Input } from "@/components/ui/input"
 | 
				
			||||||
 | 
					import { Checkbox } from "@/components/ui/checkbox"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const formSchema = z.object({
 | 
				
			||||||
 | 
						title: z.string().min(2).max(50),
 | 
				
			||||||
 | 
						word_count: z.number(),
 | 
				
			||||||
 | 
						genres: z.object({ id: z.number(), name: z.string() }).array()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function FancyForm({ genres }) {
 | 
				
			||||||
 | 
						// 1. Define your form.
 | 
				
			||||||
 | 
						const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
 | 
							resolver: zodResolver(formSchema),
 | 
				
			||||||
 | 
							defaultValues: {
 | 
				
			||||||
 | 
								title: "",
 | 
				
			||||||
 | 
								word_count: 0,
 | 
				
			||||||
 | 
								genres: genres
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						// 2. Define a submit handler.
 | 
				
			||||||
 | 
						function onSubmit(values: z.infer<typeof formSchema>) {
 | 
				
			||||||
 | 
							// Do something with the form values.
 | 
				
			||||||
 | 
							// ✅ This will be type-safe and validated.
 | 
				
			||||||
 | 
							console.log(values)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return (
 | 
				
			||||||
 | 
							<Form {...form}>
 | 
				
			||||||
 | 
								<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
 | 
				
			||||||
 | 
									<FormField
 | 
				
			||||||
 | 
										control={form.control}
 | 
				
			||||||
 | 
										name="title"
 | 
				
			||||||
 | 
										render={({ field }) => (
 | 
				
			||||||
 | 
											<FormItem>
 | 
				
			||||||
 | 
												<FormLabel>Title</FormLabel>
 | 
				
			||||||
 | 
												<FormControl>
 | 
				
			||||||
 | 
													<Input placeholder="title goes here..." {...field} />
 | 
				
			||||||
 | 
												</FormControl>
 | 
				
			||||||
 | 
												<FormMessage />
 | 
				
			||||||
 | 
											</FormItem>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<FormField
 | 
				
			||||||
 | 
										control={form.control}
 | 
				
			||||||
 | 
										name="word_count"
 | 
				
			||||||
 | 
										render={({ field }) => (
 | 
				
			||||||
 | 
											<FormItem>
 | 
				
			||||||
 | 
												<FormLabel>Word count</FormLabel>
 | 
				
			||||||
 | 
												<FormControl>
 | 
				
			||||||
 | 
													<Input type="number" step={500} min={0} {...field}></Input>
 | 
				
			||||||
 | 
												</FormControl>
 | 
				
			||||||
 | 
											</FormItem>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<FormField
 | 
				
			||||||
 | 
										control={form.control}
 | 
				
			||||||
 | 
										name="genres"
 | 
				
			||||||
 | 
										render={({ field }) => (
 | 
				
			||||||
 | 
											<FormItem>
 | 
				
			||||||
 | 
												<div className="mb-4">
 | 
				
			||||||
 | 
													<FormLabel>Genres</FormLabel>
 | 
				
			||||||
 | 
													<FormDescription>genres baby</FormDescription>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
												{genres.map((item) => (
 | 
				
			||||||
 | 
													<FormField
 | 
				
			||||||
 | 
														key={item.id}
 | 
				
			||||||
 | 
														control={form.control}
 | 
				
			||||||
 | 
														name="genres"
 | 
				
			||||||
 | 
														render={({ field }) => {
 | 
				
			||||||
 | 
															return (
 | 
				
			||||||
 | 
																<FormItem
 | 
				
			||||||
 | 
																	key={item.id}
 | 
				
			||||||
 | 
																	className="flex flex-row items-start space-x-3 space-y-0"
 | 
				
			||||||
 | 
																>
 | 
				
			||||||
 | 
																	<FormControl>
 | 
				
			||||||
 | 
																		<Checkbox
 | 
				
			||||||
 | 
																			checked={field.value?.includes(item.id)}
 | 
				
			||||||
 | 
																			onCheckedChange={(checked) => {
 | 
				
			||||||
 | 
																				return checked
 | 
				
			||||||
 | 
																					? field.onChange([...field.value, item.id])
 | 
				
			||||||
 | 
																					: field.onChange(
 | 
				
			||||||
 | 
																						field.value?.filter(
 | 
				
			||||||
 | 
																							(value) => value !== item.id
 | 
				
			||||||
 | 
																						)
 | 
				
			||||||
 | 
																					)
 | 
				
			||||||
 | 
																			}}
 | 
				
			||||||
 | 
																		/>
 | 
				
			||||||
 | 
																	</FormControl>
 | 
				
			||||||
 | 
																	<FormLabel className="text-sm font-normal">
 | 
				
			||||||
 | 
																		{item.name}
 | 
				
			||||||
 | 
																	</FormLabel>
 | 
				
			||||||
 | 
																</FormItem>
 | 
				
			||||||
 | 
															)
 | 
				
			||||||
 | 
														}}
 | 
				
			||||||
 | 
													/>
 | 
				
			||||||
 | 
												))}
 | 
				
			||||||
 | 
											</FormItem>
 | 
				
			||||||
 | 
										)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/>
 | 
				
			||||||
 | 
									<Button type="submit">Submit</Button>
 | 
				
			||||||
 | 
								</form>
 | 
				
			||||||
 | 
							</Form>
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ export default function GenrePicker({ genres, form }: ComponentProps<"div"> & {
 | 
				
			||||||
							<Button
 | 
												<Button
 | 
				
			||||||
								variant={"outline"}
 | 
													variant={"outline"}
 | 
				
			||||||
								className={cn(
 | 
													className={cn(
 | 
				
			||||||
									"min-w-fit max-w-60 pl-3 text-left font-normal flex-wrap gap-y-1 h-fit min-h-10",
 | 
														"min-w-fit max-w-full w-fit pl-3 text-left font-normal flex-wrap gap-y-1 h-fit min-h-10",
 | 
				
			||||||
									!field.value && "text-muted-foreground"
 | 
														!field.value && "text-muted-foreground"
 | 
				
			||||||
								)}
 | 
													)}
 | 
				
			||||||
							>
 | 
												>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,53 +17,49 @@ import { toast } from "@/components/ui/use-toast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { randomPublicationTitle } from "app/lib/shortStoryTitleGenerator"
 | 
					import { randomPublicationTitle } from "app/lib/shortStoryTitleGenerator"
 | 
				
			||||||
import { ComponentProps } from "react"
 | 
					import { ComponentProps } from "react"
 | 
				
			||||||
import { Genre, Pub } from "@prisma/client"
 | 
					import { Genre } from "@prisma/client"
 | 
				
			||||||
import GenrePicker from "./genrePicker"
 | 
					import GenrePicker from "./genrePicker"
 | 
				
			||||||
import { pubSchema } from "./schemas"
 | 
					 | 
				
			||||||
import { useRouter } from "next/navigation"
 | 
					 | 
				
			||||||
import { Ban } from "lucide-react"
 | 
					 | 
				
			||||||
import { PubWithGenres } from "app/publication/page"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function PubForm({ genres, dbAction, className, closeDialog, defaults }: ComponentProps<"div"> & { genres: Array<Genre>, dbAction: (data: any) => Promise<{ success: string }>, closeDialog: () => void, defaults?: PubWithGenres }) {
 | 
					export const formSchema = z.object({
 | 
				
			||||||
	const form = useForm<z.infer<typeof pubSchema>>({
 | 
						title: z.string().min(2).max(50),
 | 
				
			||||||
		resolver: zodResolver(pubSchema),
 | 
						link: z.string(),
 | 
				
			||||||
 | 
						query_after_days: z.coerce.number().min(30),
 | 
				
			||||||
 | 
						genres: z.array(z.number()),
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function PubForm({ genres, createPub, className }: ComponentProps<"div"> & { genres: Array<Genre>, createPub: (data: any) => void }) {
 | 
				
			||||||
 | 
						const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
 | 
							resolver: zodResolver(formSchema),
 | 
				
			||||||
		defaultValues: {
 | 
							defaultValues: {
 | 
				
			||||||
			id: defaults?.id,
 | 
								title: "",
 | 
				
			||||||
			title: defaults?.title ?? "",
 | 
								link: "",
 | 
				
			||||||
			link: defaults?.link ?? "",
 | 
								query_after_days: 30,
 | 
				
			||||||
			query_after_days: defaults?.query_after_days ?? 30,
 | 
								genres: []
 | 
				
			||||||
			genres: defaults?.genres.map(e => e.id) ?? []
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const router = useRouter()
 | 
						function onSubmit(values: z.infer<typeof formSchema>) {
 | 
				
			||||||
 | 
							// Do something with the form values.
 | 
				
			||||||
	async function onSubmit(values: z.infer<typeof pubSchema>) {
 | 
							// ✅ This will be type-safe and validated.
 | 
				
			||||||
		try {
 | 
							toast({
 | 
				
			||||||
			const res = await dbAction({
 | 
								title: "You submitted the following values:",
 | 
				
			||||||
				pub: {
 | 
								description: (
 | 
				
			||||||
					id: values?.id,
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
					title: values.title,
 | 
										<code className="text-white">{JSON.stringify(values, null, 2)}</code>
 | 
				
			||||||
					link: values.link,
 | 
									</pre>
 | 
				
			||||||
					query_after_days: values.query_after_days
 | 
								),
 | 
				
			||||||
				}, genres: values.genres
 | 
							})
 | 
				
			||||||
			})
 | 
							createPub(values)
 | 
				
			||||||
			if (!res?.success) throw new Error("something went wrong")
 | 
							console.log(values)
 | 
				
			||||||
			toast({ title: "Success!", description: res.success })
 | 
					 | 
				
			||||||
			router.refresh()
 | 
					 | 
				
			||||||
			closeDialog()
 | 
					 | 
				
			||||||
		} catch (error) {
 | 
					 | 
				
			||||||
			toast({
 | 
					 | 
				
			||||||
				title: "Oh dear... ",
 | 
					 | 
				
			||||||
				description: error.message
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function onErrors(errors) {
 | 
						function onErrors(errors) {
 | 
				
			||||||
		toast({
 | 
							toast({
 | 
				
			||||||
 | 
								title: "You have errors",
 | 
				
			||||||
			description: (
 | 
								description: (
 | 
				
			||||||
				<Ban />
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(errors, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		console.log(JSON.stringify(errors))
 | 
							console.log(JSON.stringify(errors))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,13 +15,10 @@ import {
 | 
				
			||||||
import { Input } from "@/components/ui/input"
 | 
					import { Input } from "@/components/ui/input"
 | 
				
			||||||
import { toast } from "@/components/ui/use-toast"
 | 
					import { toast } from "@/components/ui/use-toast"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ComponentProps, SetStateAction } from "react"
 | 
					import { ComponentProps } from "react"
 | 
				
			||||||
import { Genre, Story } from "@prisma/client"
 | 
					import { Genre, Story } from "@prisma/client"
 | 
				
			||||||
import { randomStoryTitle } from "app/lib/shortStoryTitleGenerator"
 | 
					import { randomStoryTitle } from "app/lib/shortStoryTitleGenerator"
 | 
				
			||||||
import GenrePicker from "./genrePicker"
 | 
					import GenrePicker from "./genrePicker"
 | 
				
			||||||
import { useRouter } from "next/navigation"
 | 
					 | 
				
			||||||
import { Ban, Cross } from "lucide-react"
 | 
					 | 
				
			||||||
import { StoryWithGenres } from "app/story/page"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const formSchema = z.object({
 | 
					export const formSchema = z.object({
 | 
				
			||||||
	id: z.number().optional(),
 | 
						id: z.number().optional(),
 | 
				
			||||||
| 
						 | 
					@ -30,47 +27,42 @@ export const formSchema = z.object({
 | 
				
			||||||
	genres: z.array(z.number())
 | 
						genres: z.array(z.number())
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function StoryForm({ genres, dbAction, className, closeDialog, defaults }: ComponentProps<"div"> & { genres: Array<Genre>, dbAction: (data: any) => Promise<{ success: string }>, className: string, closeDialog?: () => void, defaults?: StoryWithGenres }) {
 | 
					export default function StoryForm({ genres, createStory, className, existingData }: ComponentProps<"div"> & { genres: Array<Genre>, createStory: (data: any) => void, existingData: Story & { genres: number[] } | null }) {
 | 
				
			||||||
	const form = useForm<z.infer<typeof formSchema>>({
 | 
						const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
		resolver: zodResolver(formSchema),
 | 
							resolver: zodResolver(formSchema),
 | 
				
			||||||
		defaultValues: {
 | 
							defaultValues: {
 | 
				
			||||||
			id: defaults?.id,
 | 
								id: existingData?.id,
 | 
				
			||||||
			title: defaults?.title ?? "",
 | 
								title: existingData?.title ?? "",
 | 
				
			||||||
			word_count: defaults?.word_count ?? 500,
 | 
								word_count: existingData?.word_count ?? 500,
 | 
				
			||||||
			genres: defaults?.genres.map(e => e.id) ?? []
 | 
								genres: existingData?.genres ?? []
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const router = useRouter()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function onSubmit(values: z.infer<typeof formSchema>) {
 | 
					
 | 
				
			||||||
		try {
 | 
						function onSubmit(values: z.infer<typeof formSchema>) {
 | 
				
			||||||
			const res = await dbAction({
 | 
							toast({
 | 
				
			||||||
				story: {
 | 
								title: "You submitted the following values:",
 | 
				
			||||||
					id: values?.id,
 | 
								description: (
 | 
				
			||||||
					title: values.title,
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
					word_count: values.word_count,
 | 
										<code className="text-white">{JSON.stringify(values, null, 2)}</code>
 | 
				
			||||||
				},
 | 
									</pre>
 | 
				
			||||||
				genres: values.genres
 | 
								),
 | 
				
			||||||
			})
 | 
							})
 | 
				
			||||||
			//server actions return undefined if middleware authentication fails
 | 
							createStory(values)
 | 
				
			||||||
			if (!res?.success) throw new Error("something went wrong")
 | 
							console.log(values)
 | 
				
			||||||
			toast({ title: "Success!", description: res.success })
 | 
					 | 
				
			||||||
			router.refresh()
 | 
					 | 
				
			||||||
			closeDialog()
 | 
					 | 
				
			||||||
		} catch (error) {
 | 
					 | 
				
			||||||
			toast({
 | 
					 | 
				
			||||||
				title: "Oh dear... ",
 | 
					 | 
				
			||||||
				description: error.message
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function onErrors(errors) {
 | 
						function onErrors(errors) {
 | 
				
			||||||
		toast({
 | 
							toast({
 | 
				
			||||||
			description: (<Ban />)
 | 
								title: "You have errors",
 | 
				
			||||||
 | 
								description: (
 | 
				
			||||||
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(errors, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
 | 
								),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		console.log(JSON.stringify(errors))
 | 
							console.log(JSON.stringify(errors))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -93,7 +85,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de
 | 
				
			||||||
						)}
 | 
											)}
 | 
				
			||||||
					/>
 | 
										/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					<div className="inline-flex flex-wrap justify-around items-start w-full gap-x-16 gap-y-8 items-baseline max-w-full">
 | 
										<div className="inline-flex flex-wrap w-full gap-x-16 gap-y-8 items-baseline max-w-full">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<GenrePicker
 | 
											<GenrePicker
 | 
				
			||||||
							genres={genres}
 | 
												genres={genres}
 | 
				
			||||||
| 
						 | 
					@ -104,10 +96,10 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de
 | 
				
			||||||
							control={form.control}
 | 
												control={form.control}
 | 
				
			||||||
							name="word_count"
 | 
												name="word_count"
 | 
				
			||||||
							render={({ field }) => (
 | 
												render={({ field }) => (
 | 
				
			||||||
								<FormItem className="flex flex-col ">
 | 
													<FormItem className="flex flex-col">
 | 
				
			||||||
									<FormLabel className="h-5">Word count</FormLabel>
 | 
														<FormLabel className="h-5">Word count</FormLabel>
 | 
				
			||||||
									<FormControl>
 | 
														<FormControl>
 | 
				
			||||||
										<Input className=" w-24" type="number" step={500} {...field}></Input>
 | 
															<Input className=" w-24" type="number" step={500} min={1} {...field}></Input>
 | 
				
			||||||
									</FormControl>
 | 
														</FormControl>
 | 
				
			||||||
									<FormMessage />
 | 
														<FormMessage />
 | 
				
			||||||
								</FormItem>
 | 
													</FormItem>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,18 +32,54 @@ import {
 | 
				
			||||||
	SelectValue,
 | 
						SelectValue,
 | 
				
			||||||
} from "@/components/ui/select"
 | 
					} from "@/components/ui/select"
 | 
				
			||||||
import { useState } from "react"
 | 
					import { useState } from "react"
 | 
				
			||||||
 | 
					import { editSubmission } from "app/lib/edit"
 | 
				
			||||||
import { createSub } from "app/lib/create"
 | 
					import { createSub } from "app/lib/create"
 | 
				
			||||||
import { subSchema } from "./schemas"
 | 
					 | 
				
			||||||
import { useRouter } from "next/navigation"
 | 
					 | 
				
			||||||
import { Ban } from "lucide-react"
 | 
					 | 
				
			||||||
import { Story } from "@prisma/client"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type SubForm = z.infer<typeof subSchema>
 | 
					export const formSchema = z.object({
 | 
				
			||||||
 | 
						id: z.number().optional(),
 | 
				
			||||||
 | 
						storyId: z.coerce.number(),
 | 
				
			||||||
 | 
						pubId: z.coerce.number(),
 | 
				
			||||||
 | 
						submitted: z.coerce.date().transform((date) => date.toString()),
 | 
				
			||||||
 | 
						responded: z.coerce.date().transform((date) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (date.toString() !== new Date(null).toString()) {
 | 
				
			||||||
 | 
								return date.toString()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return null
 | 
				
			||||||
 | 
						}).optional(),
 | 
				
			||||||
 | 
						responseId: z.coerce.number()
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
						.refine(object => {
 | 
				
			||||||
 | 
							const submitted = new Date(object.submitted)
 | 
				
			||||||
 | 
							const responded = object.responded ? new Date(object.responded) : null
 | 
				
			||||||
 | 
							return responded >= submitted || responded === null
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								path: ["responded"],
 | 
				
			||||||
 | 
								message: "'Responded' must be a later date than 'submitted'"
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						.refine(object => {
 | 
				
			||||||
 | 
							if (object.responded) {
 | 
				
			||||||
 | 
								//there is a 'responded' date and the response is not 'pending'
 | 
				
			||||||
 | 
								return object.responseId !== 7
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!object.responded) {
 | 
				
			||||||
 | 
								//there is not a 'responded' date and the response is pending
 | 
				
			||||||
 | 
								return object.responseId === 7
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								path: ["responseId"],
 | 
				
			||||||
 | 
								message: "A pending response cannot have a date, and a non-pending response must have a date"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type SubForm = z.infer<typeof formSchema>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function SubmissionForm({ stories, pubs, responses, defaults, closeDialog }: { stories: any, pubs: any, responses: any, defaults?: any, closeDialog?: () => void }) {
 | 
					export default function SubmissionForm({ stories, pubs, responses, defaults }) {
 | 
				
			||||||
	const form = useForm<z.infer<typeof subSchema>>({
 | 
						const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
		resolver: zodResolver(subSchema),
 | 
							resolver: zodResolver(formSchema),
 | 
				
			||||||
		defaultValues: {
 | 
							defaultValues: {
 | 
				
			||||||
			responseId: responses[0].id,
 | 
								responseId: responses[0].id,
 | 
				
			||||||
			...defaults
 | 
								...defaults
 | 
				
			||||||
| 
						 | 
					@ -51,45 +87,51 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	const [isSubCalendarOpen, setIsSubCalendarOpen] = useState(false);
 | 
						const [isSubCalendarOpen, setIsSubCalendarOpen] = useState(false);
 | 
				
			||||||
	const [isRespCalendarOpen, setIsRespCalendarOpen] = useState(false);
 | 
						const [isRespCalendarOpen, setIsRespCalendarOpen] = useState(false);
 | 
				
			||||||
	const storiesSelectItems = stories.map((e: Story) => (
 | 
						const storiesSelectItems = stories.map(e => (
 | 
				
			||||||
		<SelectItem value={e.id?.toString()} key={e.title}>
 | 
							<SelectItem value={e.id.toString()} key={e.title}>
 | 
				
			||||||
			{e.title}
 | 
								{e.title}
 | 
				
			||||||
		</SelectItem>
 | 
							</SelectItem>
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
	const pubsSelectItems = pubs.map(e => (
 | 
						const pubsSelectItems = pubs.map(e => (
 | 
				
			||||||
		<SelectItem value={e.id} key={e.title}>
 | 
							<SelectItem value={e.id}>
 | 
				
			||||||
			{e.title}
 | 
								{e.title}
 | 
				
			||||||
		</SelectItem>
 | 
							</SelectItem>
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const reponsesSelectItems = responses.map(e => (
 | 
						const reponsesSelectItems = responses.map(e => (
 | 
				
			||||||
		<SelectItem value={e.id} key={e.title}>
 | 
							<SelectItem value={e.id}>
 | 
				
			||||||
			{e.response}
 | 
								{e.response}
 | 
				
			||||||
		</SelectItem>
 | 
							</SelectItem>
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const router = useRouter()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	async function onSubmit(values: z.infer<typeof subSchema>) {
 | 
						// 2. Define a submit handler.
 | 
				
			||||||
		try {
 | 
						function onSubmit(values: z.infer<typeof formSchema>) {
 | 
				
			||||||
			//@ts-ignore
 | 
							// Do something with the form values.
 | 
				
			||||||
			const res = await createSub(values)
 | 
							// ✅ This will be type-safe and validated.
 | 
				
			||||||
			if (!res) throw new Error("something went wrong")
 | 
							toast({
 | 
				
			||||||
			toast({ title: "Successfully created new submission!" })
 | 
								title: "You submitted the following values:",
 | 
				
			||||||
			router.refresh()
 | 
								description: (
 | 
				
			||||||
			closeDialog()
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
		} catch (error) {
 | 
										<code className="text-white">{JSON.stringify(values, null, 2)}</code>
 | 
				
			||||||
			toast({
 | 
									</pre>
 | 
				
			||||||
				title: "UH-OH",
 | 
								),
 | 
				
			||||||
				description: error.message
 | 
							})
 | 
				
			||||||
			})
 | 
							if (values.id) {
 | 
				
			||||||
 | 
								editSubmission(values)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								createSub(values)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							console.log(values)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	function onErrors(errors) {
 | 
						function onErrors(errors) {
 | 
				
			||||||
		toast({
 | 
							toast({
 | 
				
			||||||
 | 
								title: "You have errors",
 | 
				
			||||||
			description: (
 | 
								description: (
 | 
				
			||||||
				<Ban />
 | 
									<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
										<code className="text-white">{JSON.stringify(errors, null, 2)}</code>
 | 
				
			||||||
 | 
									</pre>
 | 
				
			||||||
			),
 | 
								),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		console.log(JSON.stringify(errors))
 | 
							console.log(JSON.stringify(errors))
 | 
				
			||||||
| 
						 | 
					@ -97,14 +139,14 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return (
 | 
						return (
 | 
				
			||||||
		<Form {...form}>
 | 
							<Form {...form}>
 | 
				
			||||||
			<form id="subform" onSubmit={form.handleSubmit(onSubmit, onErrors)} className="space-y-2 md:space-y-8 text-xs">
 | 
								<form id="subform" onSubmit={form.handleSubmit(onSubmit, onErrors)} className="space-y-8">
 | 
				
			||||||
				<FormField
 | 
									<FormField
 | 
				
			||||||
					control={form.control}
 | 
										control={form.control}
 | 
				
			||||||
					name="storyId"
 | 
										name="storyId"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem>
 | 
											<FormItem>
 | 
				
			||||||
							<FormLabel>Story</FormLabel>
 | 
												<FormLabel>Story</FormLabel>
 | 
				
			||||||
							<Select onValueChange={field.onChange} defaultValue={field.value?.toString()}>
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
								<FormControl>
 | 
													<FormControl>
 | 
				
			||||||
									<SelectTrigger>
 | 
														<SelectTrigger>
 | 
				
			||||||
										<SelectValue placeholder="Select something">
 | 
															<SelectValue placeholder="Select something">
 | 
				
			||||||
| 
						 | 
					@ -116,7 +158,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									{storiesSelectItems}
 | 
														{storiesSelectItems}
 | 
				
			||||||
								</SelectContent>
 | 
													</SelectContent>
 | 
				
			||||||
							</Select>
 | 
												</Select>
 | 
				
			||||||
							<FormDescription className="text-xs md:text-base">The piece you submitted</FormDescription>
 | 
												<FormDescription>The piece you submitted</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
						</FormItem>
 | 
											</FormItem>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
| 
						 | 
					@ -126,8 +168,8 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
					name="pubId"
 | 
										name="pubId"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem>
 | 
											<FormItem>
 | 
				
			||||||
							<FormLabel className="text-sm md:text-base">Publication</FormLabel>
 | 
												<FormLabel>Publication</FormLabel>
 | 
				
			||||||
							<Select onValueChange={field.onChange} defaultValue={field.value?.toString()}>
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
								<FormControl>
 | 
													<FormControl>
 | 
				
			||||||
									<SelectTrigger>
 | 
														<SelectTrigger>
 | 
				
			||||||
										<SelectValue placeholder="Select something">
 | 
															<SelectValue placeholder="Select something">
 | 
				
			||||||
| 
						 | 
					@ -139,7 +181,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									{pubsSelectItems}
 | 
														{pubsSelectItems}
 | 
				
			||||||
								</SelectContent>
 | 
													</SelectContent>
 | 
				
			||||||
							</Select>
 | 
												</Select>
 | 
				
			||||||
							<FormDescription className="text-xs md:text-base">The market you sent it to</FormDescription>
 | 
												<FormDescription>The market you sent it to</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
						</FormItem>
 | 
											</FormItem>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
| 
						 | 
					@ -150,7 +192,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
					name="submitted"
 | 
										name="submitted"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem className="flex flex-col">
 | 
											<FormItem className="flex flex-col">
 | 
				
			||||||
							<FormLabel className="text-sm md:text-base">Date of submission</FormLabel>
 | 
												<FormLabel>Date of submission</FormLabel>
 | 
				
			||||||
							<Popover modal={true} open={isSubCalendarOpen} onOpenChange={setIsSubCalendarOpen}>
 | 
												<Popover modal={true} open={isSubCalendarOpen} onOpenChange={setIsSubCalendarOpen}>
 | 
				
			||||||
								<PopoverTrigger asChild>
 | 
													<PopoverTrigger asChild>
 | 
				
			||||||
									<FormControl>
 | 
														<FormControl>
 | 
				
			||||||
| 
						 | 
					@ -171,8 +213,9 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									</FormControl>
 | 
														</FormControl>
 | 
				
			||||||
								</PopoverTrigger>
 | 
													</PopoverTrigger>
 | 
				
			||||||
								<PopoverContent className="w-auto p-0" align="start">
 | 
													<PopoverContent className="w-auto p-0" align="start">
 | 
				
			||||||
									{/* @ts-ignore */}
 | 
														<Calendar
 | 
				
			||||||
									<Calendar mode="single" selected={field.value}
 | 
															mode="single"
 | 
				
			||||||
 | 
															selected={field.value}
 | 
				
			||||||
										onSelect={(e) => { field.onChange(e); setIsSubCalendarOpen(false); }}
 | 
															onSelect={(e) => { field.onChange(e); setIsSubCalendarOpen(false); }}
 | 
				
			||||||
										disabled={(date) =>
 | 
															disabled={(date) =>
 | 
				
			||||||
											date > new Date() || date < new Date("1900-01-01")
 | 
																date > new Date() || date < new Date("1900-01-01")
 | 
				
			||||||
| 
						 | 
					@ -181,7 +224,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									/>
 | 
														/>
 | 
				
			||||||
								</PopoverContent>
 | 
													</PopoverContent>
 | 
				
			||||||
							</Popover>
 | 
												</Popover>
 | 
				
			||||||
							<FormDescription className="text-xs md:text-base">
 | 
												<FormDescription>
 | 
				
			||||||
								The date you sent it
 | 
													The date you sent it
 | 
				
			||||||
							</FormDescription>
 | 
												</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
| 
						 | 
					@ -194,7 +237,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
					name="responded"
 | 
										name="responded"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem className="flex flex-col">
 | 
											<FormItem className="flex flex-col">
 | 
				
			||||||
							<FormLabel className="text-sm md:text-base">Date of response</FormLabel>
 | 
												<FormLabel>Date of response</FormLabel>
 | 
				
			||||||
							<Popover modal={true} open={isRespCalendarOpen} onOpenChange={setIsRespCalendarOpen}>
 | 
												<Popover modal={true} open={isRespCalendarOpen} onOpenChange={setIsRespCalendarOpen}>
 | 
				
			||||||
								<PopoverTrigger asChild>
 | 
													<PopoverTrigger asChild>
 | 
				
			||||||
									<FormControl>
 | 
														<FormControl>
 | 
				
			||||||
| 
						 | 
					@ -215,8 +258,9 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									</FormControl>
 | 
														</FormControl>
 | 
				
			||||||
								</PopoverTrigger>
 | 
													</PopoverTrigger>
 | 
				
			||||||
								<PopoverContent className="w-auto p-0" align="start">
 | 
													<PopoverContent className="w-auto p-0" align="start">
 | 
				
			||||||
									{/* @ts-ignore */}
 | 
														<Calendar
 | 
				
			||||||
									<Calendar mode="single" selected={field.value}
 | 
															mode="single"
 | 
				
			||||||
 | 
															selected={field.value}
 | 
				
			||||||
										onSelect={(e) => { field.onChange(e); setIsRespCalendarOpen(false); }}
 | 
															onSelect={(e) => { field.onChange(e); setIsRespCalendarOpen(false); }}
 | 
				
			||||||
										disabled={(date) =>
 | 
															disabled={(date) =>
 | 
				
			||||||
											date > new Date() || date < new Date("1900-01-01")
 | 
																date > new Date() || date < new Date("1900-01-01")
 | 
				
			||||||
| 
						 | 
					@ -225,7 +269,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									/>
 | 
														/>
 | 
				
			||||||
								</PopoverContent>
 | 
													</PopoverContent>
 | 
				
			||||||
							</Popover>
 | 
												</Popover>
 | 
				
			||||||
							<FormDescription className="text-xs md:text-base">
 | 
												<FormDescription>
 | 
				
			||||||
								The date they wrote back
 | 
													The date they wrote back
 | 
				
			||||||
							</FormDescription>
 | 
												</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
| 
						 | 
					@ -239,8 +283,8 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
					name="responseId"
 | 
										name="responseId"
 | 
				
			||||||
					render={({ field }) => (
 | 
										render={({ field }) => (
 | 
				
			||||||
						<FormItem>
 | 
											<FormItem>
 | 
				
			||||||
							<FormLabel className="text-sm md:text-base">Response</FormLabel>
 | 
												<FormLabel>Response</FormLabel>
 | 
				
			||||||
							<Select onValueChange={field.onChange} defaultValue={field.value?.toString()}>
 | 
												<Select onValueChange={field.onChange} defaultValue={field.value}>
 | 
				
			||||||
								<FormControl>
 | 
													<FormControl>
 | 
				
			||||||
									<SelectTrigger>
 | 
														<SelectTrigger>
 | 
				
			||||||
										<SelectValue>
 | 
															<SelectValue>
 | 
				
			||||||
| 
						 | 
					@ -252,7 +296,7 @@ export default function SubmissionForm({ stories, pubs, responses, defaults, clo
 | 
				
			||||||
									{reponsesSelectItems}
 | 
														{reponsesSelectItems}
 | 
				
			||||||
								</SelectContent>
 | 
													</SelectContent>
 | 
				
			||||||
							</Select>
 | 
												</Select>
 | 
				
			||||||
							<FormDescription className="text-xs md:text-base">The market you sent it to</FormDescription>
 | 
												<FormDescription>The market you sent it to</FormDescription>
 | 
				
			||||||
							<FormMessage />
 | 
												<FormMessage />
 | 
				
			||||||
						</FormItem>
 | 
											</FormItem>
 | 
				
			||||||
					)}
 | 
										)}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { Badge } from "@/components/ui/badge";
 | 
				
			||||||
export default function GenreBadges(props: ComponentProps<"div"> & { genres: Array<Genre> }) {
 | 
					export default function GenreBadges(props: ComponentProps<"div"> & { genres: Array<Genre> }) {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={"flex flex-wrap gap-1 justify-center " + props.className}>
 | 
					    <div className={"flex flex-wrap gap-1 justify-center " + props.className}>
 | 
				
			||||||
      {props.genres.map((e: Genre) => (<Badge className="text-xs md:text-sm" key={e.name}>{e.name}</Badge>))}
 | 
					      {props.genres.map((e: Genre) => (<Badge key={e.name}>{e.name}</Badge>))}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@ import { usePathname } from "next/navigation";
 | 
				
			||||||
import { ComponentProps } from "react";
 | 
					import { ComponentProps } from "react";
 | 
				
			||||||
import clsx from "clsx";
 | 
					import clsx from "clsx";
 | 
				
			||||||
import { twMerge } from "tailwind-merge";
 | 
					import { twMerge } from "tailwind-merge";
 | 
				
			||||||
import { ArrowUpNarrowWide, BookOpen, BookOpenText } from "lucide-react";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function NavLink(props: ComponentProps<"div"> & { href: string }) {
 | 
					function NavLink(props: ComponentProps<"div"> & { href: string }) {
 | 
				
			||||||
| 
						 | 
					@ -15,24 +14,21 @@ function NavLink(props: ComponentProps<"div"> & { href: string }) {
 | 
				
			||||||
export default function Navlinks(props: ComponentProps<"div">) {
 | 
					export default function Navlinks(props: ComponentProps<"div">) {
 | 
				
			||||||
  const pathname = usePathname()
 | 
					  const pathname = usePathname()
 | 
				
			||||||
  const links = [
 | 
					  const links = [
 | 
				
			||||||
    { link: "/story", label: "STORIES", icon: <BookOpenText /> },
 | 
					    { link: "/story", label: "STORIES" },
 | 
				
			||||||
    { link: "/publication", label: "PUBLICATIONS", icon: <BookOpen /> },
 | 
					    { link: "/publication", label: "PUBLICATIONS" },
 | 
				
			||||||
    { link: "/submission", label: "SUBMISSIONS", icon: <ArrowUpNarrowWide /> },
 | 
					    { link: "/submission", label: "SUBMISSIONS" },
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={props.className}>
 | 
					    <div className={props.className}>
 | 
				
			||||||
      <div className="text-secondary-foreground flex flex-row md:flex-col" >
 | 
					      <div className="text-secondary-foreground" >
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          links.map(e => (<NavLink key={e.link} href={e.link}
 | 
					          links.map(e => (<NavLink key={e.link} href={e.link}
 | 
				
			||||||
            className={twMerge(clsx("text-xl drop-shadow  font-black my-2 w-full mr-2 p-2 pl-6 antialiased text-secondary-foreground bg-secondary rounded-3xl md:rounded-l-3xl ",
 | 
					            className={twMerge(clsx("text-xl drop-shadow  font-black my-2 w-full p-2 pl-6 antialiased text-secondary-foreground bg-secondary rounded-l-3xl",
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
                "text-primary-foreground bg-primary": pathname.includes(e.link)
 | 
					                "text-primary-foreground bg-primary": pathname.includes(e.link)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            ))}
 | 
					            ))}
 | 
				
			||||||
          >
 | 
					          ><p className="drop-shadow-sm">{e.label}</p></NavLink >))
 | 
				
			||||||
            <p className="drop-shadow-sm hidden md:block">{e.label}</p>
 | 
					 | 
				
			||||||
            <span className="block md:hidden">{e.icon}</span>
 | 
					 | 
				
			||||||
          </NavLink >))
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      </ div>
 | 
					      </ div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,14 @@
 | 
				
			||||||
 | 
					import { Dialog, DialogTrigger, DialogClose, DialogDescription, DialogContent, DialogTitle, DialogHeader, DialogFooter } from "@/components/ui/dialog"
 | 
				
			||||||
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
import { ContextMenuContent, ContextMenuItem, ContextMenuSubTrigger, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent } from "@/components/ui/context-menu"
 | 
					import { ContextMenuContent, ContextMenuItem, ContextMenuSubTrigger, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent } from "@/components/ui/context-menu"
 | 
				
			||||||
 | 
					import { deleteRecord } from "app/lib/del"
 | 
				
			||||||
import Link from "next/link"
 | 
					import Link from "next/link"
 | 
				
			||||||
import { ComponentProps, useState } from "react"
 | 
					import { ComponentProps, useState } from "react"
 | 
				
			||||||
import { Row, Table, TableState } from "@tanstack/react-table"
 | 
					import { Row, Table, TableState } from "@tanstack/react-table"
 | 
				
			||||||
 | 
					import { tableNameToItemName } from "app/lib/nameMaps"
 | 
				
			||||||
 | 
					import EditSubmissionDialog from "app/submission/edit"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function FormContextMenu({ table, row, openEditDialog, openDeleteDialog }: ComponentProps<"div"> & { table: Table<any>, row: Row<any>, openEditDialog: (row: Row<any>) => void, openDeleteDialog: (row: Row<any>) => void }) {
 | 
					export default function FormContextMenu({ table, row, openEditDialog, openDeleteDialog }: ComponentProps<"div"> & { table: Table<any>, row: Row<any>, openEditDialog: (row: Row<any>) => void, openDeleteDialog: (row: Row<any>) => void }) {
 | 
				
			||||||
  //@ts-ignore
 | 
					 | 
				
			||||||
  const pathname = table.options.meta.pathname
 | 
					  const pathname = table.options.meta.pathname
 | 
				
			||||||
  const selectedRows = table.getSelectedRowModel().flatRows
 | 
					  const selectedRows = table.getSelectedRowModel().flatRows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,9 +24,15 @@ export default function FormContextMenu({ table, row, openEditDialog, openDelete
 | 
				
			||||||
        : ""
 | 
					        : ""
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <ContextMenuItem onClick={() => openEditDialog(row)}>
 | 
					      {
 | 
				
			||||||
        Edit
 | 
					        pathname === "/submission" ?
 | 
				
			||||||
      </ContextMenuItem>
 | 
					          <>
 | 
				
			||||||
 | 
					            <ContextMenuItem onClick={() => openEditDialog(row)}>
 | 
				
			||||||
 | 
					              Edit
 | 
				
			||||||
 | 
					            </ContextMenuItem>
 | 
				
			||||||
 | 
					          </>
 | 
				
			||||||
 | 
					          : ""
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        selectedRows.length > 0 ?
 | 
					        selectedRows.length > 0 ?
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,21 +39,17 @@ import {
 | 
				
			||||||
  TableRow,
 | 
					  TableRow,
 | 
				
			||||||
} from "@/components/ui/table"
 | 
					} from "@/components/ui/table"
 | 
				
			||||||
import { EyeIcon, Trash2 } from "lucide-react"
 | 
					import { EyeIcon, Trash2 } from "lucide-react"
 | 
				
			||||||
import { usePathname, useSearchParams } from "next/navigation"
 | 
					import { usePathname } from "next/navigation"
 | 
				
			||||||
import FormContextMenu from "./contextMenu"
 | 
					import FormContextMenu from "./contextMenu"
 | 
				
			||||||
import { deleteRecord, deleteRecords } from "app/lib/del"
 | 
					import { deleteRecord, deleteRecords } from "app/lib/del"
 | 
				
			||||||
import { Pathname } from "app/types"
 | 
					import { Pathname } from "app/types"
 | 
				
			||||||
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTrigger } from "@/components/ui/dialog"
 | 
					import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTrigger } from "@/components/ui/dialog"
 | 
				
			||||||
import pluralize from "app/lib/pluralize"
 | 
					import pluralize from "app/lib/pluralize"
 | 
				
			||||||
import { updateField, updatePub, updateStory } from "app/lib/update"
 | 
					import { updateField } from "app/lib/update"
 | 
				
			||||||
import { tableNameToItemName } from "app/lib/nameMaps"
 | 
					import { tableNameToItemName } from "app/lib/nameMaps"
 | 
				
			||||||
import { Genre, Pub, Response, Story } from "@prisma/client"
 | 
					import { Genre, Pub, Response, Story } from "@prisma/client"
 | 
				
			||||||
import EditSubmissionDialog from "app/submission/edit"
 | 
					import EditSubmissionDialog from "app/submission/edit"
 | 
				
			||||||
import { DialogTitle } from "@radix-ui/react-dialog"
 | 
					import { DialogTitle } from "@radix-ui/react-dialog"
 | 
				
			||||||
import { toast } from "@/components/ui/use-toast"
 | 
					 | 
				
			||||||
import { useRouter } from "next/navigation"
 | 
					 | 
				
			||||||
import EditStoryDialog from "app/story/edit"
 | 
					 | 
				
			||||||
import EditPubDialog from "app/publication/edit"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface DataTableProps<TData, TValue> {
 | 
					export interface DataTableProps<TData, TValue> {
 | 
				
			||||||
  columns: ColumnDef<TData, TValue>[]
 | 
					  columns: ColumnDef<TData, TValue>[]
 | 
				
			||||||
| 
						 | 
					@ -82,7 +78,7 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
  const [columnVisibility, setColumnVisibility] =
 | 
					  const [columnVisibility, setColumnVisibility] =
 | 
				
			||||||
    useState<VisibilityState>({})
 | 
					    useState<VisibilityState>({})
 | 
				
			||||||
  //
 | 
					  //
 | 
				
			||||||
  const pathname: string = usePathname()
 | 
					  const pathname: Pathname = usePathname()
 | 
				
			||||||
  const table = useReactTable({
 | 
					  const table = useReactTable({
 | 
				
			||||||
    data,
 | 
					    data,
 | 
				
			||||||
    columns,
 | 
					    columns,
 | 
				
			||||||
| 
						 | 
					@ -121,33 +117,31 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
    setIsDeleteDialogVisible(true)
 | 
					    setIsDeleteDialogVisible(true)
 | 
				
			||||||
    SetDialogRow(row)
 | 
					    SetDialogRow(row)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  function closeEditDialog() {
 | 
					 | 
				
			||||||
    setIsEditDialogVisible(false)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
  const router = useRouter()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [filterBy, setFilterBy] = useState(table.getAllColumns()[0])
 | 
					  const [filterBy, setFilterBy] = useState(table.getAllColumns()[0])
 | 
				
			||||||
  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
 | 
					  const [isContextMenuOpen, setIsContextMenuOpen] = useState(false)
 | 
				
			||||||
  return (<>
 | 
					  return (<>
 | 
				
			||||||
    <div className="flex justify-between items-center py-1 md:py-4">
 | 
					    <div className="flex justify-between items-center py-4">
 | 
				
			||||||
      <div className="flex gap-2">
 | 
					      <div className="flex gap-2">
 | 
				
			||||||
        <DropdownMenu>
 | 
					        <DropdownMenu>
 | 
				
			||||||
          <DropdownMenuTrigger asChild>
 | 
					          <DropdownMenuTrigger asChild>
 | 
				
			||||||
            <Button variant="outline" className="hidden sm:block ml-auto">
 | 
					            <Button variant="outline" className="ml-auto">
 | 
				
			||||||
              Filter by
 | 
					              Filter by
 | 
				
			||||||
            </Button>
 | 
					            </Button>
 | 
				
			||||||
          </DropdownMenuTrigger>
 | 
					          </DropdownMenuTrigger>
 | 
				
			||||||
          <DropdownMenuContent align="end">
 | 
					          <DropdownMenuContent align="end">
 | 
				
			||||||
            {/*@ts-ignore*/}
 | 
					 | 
				
			||||||
            <DropdownMenuRadioGroup value={filterBy} onValueChange={setFilterBy} >
 | 
					            <DropdownMenuRadioGroup value={filterBy} onValueChange={setFilterBy} >
 | 
				
			||||||
              {table
 | 
					              {table
 | 
				
			||||||
                .getAllColumns()
 | 
					                .getAllColumns()
 | 
				
			||||||
                .filter((column) => column.getCanFilter())
 | 
					                .filter((column) => column.getCanFilter())
 | 
				
			||||||
                //@ts-ignore
 | 
					                .map((column) => {
 | 
				
			||||||
                .map((column) => { return (<DropdownMenuRadioItem value={column} className="capitalize" key={column.id}> {column.id} </DropdownMenuRadioItem>) })}
 | 
					                  return (
 | 
				
			||||||
 | 
					                    <DropdownMenuRadioItem value={column} className="capitalize" key={column.id}>
 | 
				
			||||||
 | 
					                      {column.id}
 | 
				
			||||||
 | 
					                    </DropdownMenuRadioItem>
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                })}
 | 
				
			||||||
            </DropdownMenuRadioGroup>
 | 
					            </DropdownMenuRadioGroup>
 | 
				
			||||||
          </DropdownMenuContent>
 | 
					          </DropdownMenuContent>
 | 
				
			||||||
        </DropdownMenu>
 | 
					        </DropdownMenu>
 | 
				
			||||||
| 
						 | 
					@ -165,30 +159,12 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Dialog open={isEditDialogVisible} onOpenChange={setIsEditDialogVisible}>
 | 
					      <Dialog open={isEditDialogVisible} onOpenChange={setIsEditDialogVisible}>
 | 
				
			||||||
        <DialogContent>
 | 
					        <DialogContent>
 | 
				
			||||||
          {tableName === "sub" ?
 | 
					          <EditSubmissionDialog
 | 
				
			||||||
            <EditSubmissionDialog
 | 
					            stories={stories}
 | 
				
			||||||
              stories={stories}
 | 
					            pubs={pubs}
 | 
				
			||||||
              pubs={pubs}
 | 
					            responses={responses}
 | 
				
			||||||
              responses={responses}
 | 
					            defaults={dialogRow?.original}
 | 
				
			||||||
              defaults={dialogRow?.original}
 | 
					          />
 | 
				
			||||||
              closeDialog={closeEditDialog}
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
            : tableName === "story" ?
 | 
					 | 
				
			||||||
              < EditStoryDialog
 | 
					 | 
				
			||||||
                dbAction={updateStory}
 | 
					 | 
				
			||||||
                genres={genres}
 | 
					 | 
				
			||||||
                defaults={dialogRow?.original}
 | 
					 | 
				
			||||||
                closeDialog={closeEditDialog}
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
              : tableName === "pub" ?
 | 
					 | 
				
			||||||
                <EditPubDialog
 | 
					 | 
				
			||||||
                  dbAction={updatePub}
 | 
					 | 
				
			||||||
                  genres={genres}
 | 
					 | 
				
			||||||
                  defaults={dialogRow?.original}
 | 
					 | 
				
			||||||
                  closeDialog={closeEditDialog}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
                : ""
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        </DialogContent>
 | 
					        </DialogContent>
 | 
				
			||||||
      </Dialog>
 | 
					      </Dialog>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,12 +181,8 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
          <DialogFooter>
 | 
					          <DialogFooter>
 | 
				
			||||||
            <DialogClose asChild>
 | 
					            <DialogClose asChild>
 | 
				
			||||||
              <Button variant="destructive"
 | 
					              <Button variant="destructive"
 | 
				
			||||||
                onClick={async () => {
 | 
					                onClick={() => {
 | 
				
			||||||
                  const res = await deleteRecord(dialogRow.original.id, pathname)
 | 
					                  deleteRecord(dialogRow.original.id, pathname)
 | 
				
			||||||
                  if (!res) toast({ title: "Oh dear...", description: "Failed to delete." })
 | 
					 | 
				
			||||||
                  if (res) toast({ title: "Successfully deleted record of id:", description: dialogRow.original.id })
 | 
					 | 
				
			||||||
                  table.resetRowSelection()
 | 
					 | 
				
			||||||
                  router.refresh()
 | 
					 | 
				
			||||||
                }}>Yes, delete it!
 | 
					                }}>Yes, delete it!
 | 
				
			||||||
              </Button>
 | 
					              </Button>
 | 
				
			||||||
            </DialogClose>
 | 
					            </DialogClose>
 | 
				
			||||||
| 
						 | 
					@ -226,29 +198,23 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
        </DialogTrigger>
 | 
					        </DialogTrigger>
 | 
				
			||||||
        <DialogContent>
 | 
					        <DialogContent>
 | 
				
			||||||
          <DialogHeader>
 | 
					          <DialogHeader>
 | 
				
			||||||
            <DialogTitle>Delete items?</DialogTitle>
 | 
					 | 
				
			||||||
            {`Delete ${Object.keys(table.getState().rowSelection).length} ${pluralize(pathname.slice(1))}?`}
 | 
					            {`Delete ${Object.keys(table.getState().rowSelection).length} ${pluralize(pathname.slice(1))}?`}
 | 
				
			||||||
          </DialogHeader>
 | 
					          </DialogHeader>
 | 
				
			||||||
          <DialogDescription>
 | 
					          <DialogDescription>
 | 
				
			||||||
            {/* @ts-ignore */}
 | 
					 | 
				
			||||||
            {`Deleting ${pluralize(tableNameToItemName(table.options.meta.tableName))} cannot be undone!`}
 | 
					            {`Deleting ${pluralize(tableNameToItemName(table.options.meta.tableName))} cannot be undone!`}
 | 
				
			||||||
          </DialogDescription>
 | 
					          </DialogDescription>
 | 
				
			||||||
          <DialogFooter>
 | 
					          <DialogFooter>
 | 
				
			||||||
            <Button variant="destructive"
 | 
					            <DialogClose asChild>
 | 
				
			||||||
              onClick={async () => {
 | 
					              <Button variant="destructive"
 | 
				
			||||||
                const selectedRows = table.getState().rowSelection
 | 
					                onClick={() => {
 | 
				
			||||||
                const rowIds = Object.keys(selectedRows)
 | 
					                  const selectedRows = table.getState().rowSelection
 | 
				
			||||||
                //@ts-ignore
 | 
					                  const rowIds = Object.keys(selectedRows)
 | 
				
			||||||
                const recordIds = rowIds.map(id => Number(table.getRow(id).original.id))
 | 
					                  const recordIds = rowIds.map(id => Number(table.getRow(id).original.id))
 | 
				
			||||||
                //@ts-ignore
 | 
					                  console.table(recordIds)
 | 
				
			||||||
                const res = await deleteRecords(recordIds, pathname)
 | 
					                  deleteRecords(recordIds, pathname)
 | 
				
			||||||
                if (!res) toast({ title: "Oh dear...", description: "Failed to delete." })
 | 
					                }}>
 | 
				
			||||||
                if (res) toast({ title: "Successfully deleted records of id:", description: JSON.stringify(recordIds) })
 | 
					                Yes, delete them!</Button>
 | 
				
			||||||
                table.resetRowSelection()
 | 
					            </DialogClose>
 | 
				
			||||||
                setIsDeleteDialogVisible(false)
 | 
					 | 
				
			||||||
                router.refresh()
 | 
					 | 
				
			||||||
              }}>
 | 
					 | 
				
			||||||
              Yes, delete them!</Button>
 | 
					 | 
				
			||||||
          </DialogFooter>
 | 
					          </DialogFooter>
 | 
				
			||||||
        </DialogContent>
 | 
					        </DialogContent>
 | 
				
			||||||
      </Dialog>
 | 
					      </Dialog>
 | 
				
			||||||
| 
						 | 
					@ -284,8 +250,7 @@ export function DataTable<TData, TValue>({
 | 
				
			||||||
        </DropdownMenuContent>
 | 
					        </DropdownMenuContent>
 | 
				
			||||||
      </DropdownMenu>
 | 
					      </DropdownMenu>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div className="rounded-md">
 | 
					    <div className="rounded-md border">
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Table>
 | 
					      <Table>
 | 
				
			||||||
        <TableHeader>
 | 
					        <TableHeader>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import { FormField, FormItem, FormLabel, FormMessage, FormControl, Form } from "
 | 
				
			||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
 | 
					import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
 | 
				
			||||||
import { Button } from "@/components/ui/button"
 | 
					import { Button } from "@/components/ui/button"
 | 
				
			||||||
import { Checkbox } from "@/components/ui/checkbox"
 | 
					import { Checkbox } from "@/components/ui/checkbox"
 | 
				
			||||||
import { BaseSyntheticEvent, ComponentProps, EventHandler, useState } from "react"
 | 
					import { ComponentProps, useState } from "react"
 | 
				
			||||||
import { EventType, useForm, UseFormReturn } from "react-hook-form"
 | 
					import { EventType, useForm, UseFormReturn } from "react-hook-form"
 | 
				
			||||||
import { CellContext } from "@tanstack/react-table"
 | 
					import { CellContext } from "@tanstack/react-table"
 | 
				
			||||||
import { z } from "zod"
 | 
					import { z } from "zod"
 | 
				
			||||||
| 
						 | 
					@ -12,38 +12,36 @@ import { toast } from "@/components/ui/use-toast"
 | 
				
			||||||
import GenreBadges from "app/ui/genreBadges"
 | 
					import GenreBadges from "app/ui/genreBadges"
 | 
				
			||||||
import { updateField, updateGenres } from "app/lib/update"
 | 
					import { updateField, updateGenres } from "app/lib/update"
 | 
				
			||||||
import { Genre } from "@prisma/client"
 | 
					import { Genre } from "@prisma/client"
 | 
				
			||||||
import { useRouter } from "next/navigation"
 | 
					 | 
				
			||||||
export default function GenrePickerInputCell(props: CellContext<any, any>) {
 | 
					export default function GenrePickerInputCell(props: CellContext<any, any>) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //@ts-ignore
 | 
					
 | 
				
			||||||
  const table = props.table.options.meta.tableName
 | 
					  const table = props.table.options.meta.tableName
 | 
				
			||||||
  //@ts-ignore
 | 
					 | 
				
			||||||
  const pathname = props.table.options.meta.pathname
 | 
					  const pathname = props.table.options.meta.pathname
 | 
				
			||||||
  const id = props.row.original.id
 | 
					  const id = props.row.original.id
 | 
				
			||||||
  const column = props.column.id
 | 
					  const column = props.column.id
 | 
				
			||||||
  const value = props.cell.getValue()
 | 
					  const value = props.cell.getValue()
 | 
				
			||||||
  //@ts-ignore
 | 
					 | 
				
			||||||
  const genres = props.table.options.meta.genres
 | 
					  const genres = props.table.options.meta.genres
 | 
				
			||||||
  const [isActive, setIsActive] = useState(false)
 | 
					  const [isActive, setIsActive] = useState(false)
 | 
				
			||||||
  const router = useRouter()
 | 
					
 | 
				
			||||||
  async function onSubmit({ genres }: { genres: number[] }, event: BaseSyntheticEvent) {
 | 
					  async function onSubmit({ genres }: { genres: number[] }) {
 | 
				
			||||||
    event.preventDefault()
 | 
					    event.preventDefault()
 | 
				
			||||||
    try {
 | 
					    const genresArray = genres.map((e) => { return { id: e } })
 | 
				
			||||||
      const genresArray = genres.map((e) => { return { id: e } })
 | 
					    console.log(`genres: ${genres}, genresArray: ${JSON.stringify(genresArray)}`)
 | 
				
			||||||
      const res = await updateGenres({
 | 
					    toast({
 | 
				
			||||||
        id,
 | 
					      title: "You submitted the following values:",
 | 
				
			||||||
        table,
 | 
					      description: (
 | 
				
			||||||
        genres: genresArray,
 | 
					        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
        pathname
 | 
					          <code className="text-white">{JSON.stringify(genres)}</code>
 | 
				
			||||||
      })
 | 
					        </pre>
 | 
				
			||||||
      if (res === undefined) throw new Error("Something went wrong.")
 | 
					      ),
 | 
				
			||||||
      toast({ title: "Field updated successfully." })
 | 
					    })
 | 
				
			||||||
      router.refresh()
 | 
					    const res = await updateGenres({
 | 
				
			||||||
    } catch (error) {
 | 
					      id,
 | 
				
			||||||
      console.error(error)
 | 
					      table,
 | 
				
			||||||
      toast({ title: "Something went wrong." })
 | 
					      genres: genresArray,
 | 
				
			||||||
    }
 | 
					      pathname
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    setIsActive(false)
 | 
					    setIsActive(false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,9 +77,9 @@ export default function GenrePickerInputCell(props: CellContext<any, any>) {
 | 
				
			||||||
            control={form.control}
 | 
					            control={form.control}
 | 
				
			||||||
            name="genres"
 | 
					            name="genres"
 | 
				
			||||||
            render={({ field }) => (
 | 
					            render={({ field }) => (
 | 
				
			||||||
              <FormItem className="w-full max-w-32 flex flex-col">
 | 
					              <FormItem className="w-full max-w-xs flex flex-col">
 | 
				
			||||||
                <PopoverTrigger asChild>
 | 
					                <PopoverTrigger asChild>
 | 
				
			||||||
                  {value.length > 0 ? <Button variant="ghost" className="h-fit p-0"><GenreBadges genres={value} className="w-full" /></Button> : <Button variant="outline" type="button" className="text-xs md:text-sm w-fit m-auto">Add genres</Button>
 | 
					                  {value.length > 0 ? <Button variant="ghost" className="h-fit"><GenreBadges genres={value} className="w-full" /></Button> : <Button variant="outline" type="button" className="w-fit m-auto">Add genres</Button>
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                </PopoverTrigger>
 | 
					                </PopoverTrigger>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +101,7 @@ export default function GenrePickerInputCell(props: CellContext<any, any>) {
 | 
				
			||||||
                              <Checkbox
 | 
					                              <Checkbox
 | 
				
			||||||
                                checked={field.value?.includes(item.id)}
 | 
					                                checked={field.value?.includes(item.id)}
 | 
				
			||||||
                                onCheckedChange={(checked) => {
 | 
					                                onCheckedChange={(checked) => {
 | 
				
			||||||
 | 
					                                  console.log(field.value)
 | 
				
			||||||
                                  return checked
 | 
					                                  return checked
 | 
				
			||||||
                                    ? field.onChange(
 | 
					                                    ? field.onChange(
 | 
				
			||||||
                                      [...field.value, item.id]
 | 
					                                      [...field.value, item.id]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,56 +9,58 @@ import { zodResolver } from "@hookform/resolvers/zod";
 | 
				
			||||||
import { toast } from "@/components/ui/use-toast";
 | 
					import { toast } from "@/components/ui/use-toast";
 | 
				
			||||||
import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form";
 | 
					import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function NumberInputCell({ cellContext, className }: { cellContext: CellContext<any, any>, className: string }) {
 | 
					export default function NumberInputCell(props: CellContext<any, any>) {
 | 
				
			||||||
  const [isActive, setIsActive] = useState(false)
 | 
					  const [isActive, setIsActive] = useState(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //@ts-ignore
 | 
					  const table = props.table.options.meta.tableName
 | 
				
			||||||
  const table = cellContext.table.options.meta.tableName
 | 
					  const id = props.row.original.id
 | 
				
			||||||
  const id = cellContext.row.original.id
 | 
					  const column = props.column.id
 | 
				
			||||||
  const column = cellContext.column.id
 | 
					  const pathname = props.table.options.meta.pathname
 | 
				
			||||||
  //@ts-ignore
 | 
					  const value = props.cell.getValue()
 | 
				
			||||||
  const pathname = cellContext.table.options.meta.pathname
 | 
					  const formSchema = props.column.columnDef.meta.formSchema.pick({ [column]: true })
 | 
				
			||||||
  const value = cellContext.cell.getValue()
 | 
					 | 
				
			||||||
  //@ts-ignore
 | 
					 | 
				
			||||||
  const formSchema = cellContext.column.columnDef.meta.formSchema.pick({ [column]: true })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const form = useForm<z.infer<typeof formSchema>>({
 | 
					  const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
    resolver: zodResolver(formSchema),
 | 
					    resolver: zodResolver(formSchema),
 | 
				
			||||||
    defaultValues: {
 | 
					    defaultValues: {
 | 
				
			||||||
      [column]: cellContext.cell.getValue()
 | 
					      [column]: props.cell.getValue()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function onSubmit(value: z.infer<typeof formSchema>) {
 | 
					  function onSubmit(value: z.infer<typeof formSchema>) {
 | 
				
			||||||
    try {
 | 
					    toast({
 | 
				
			||||||
      const res = await updateField({
 | 
					      title: "You submitted the following values:",
 | 
				
			||||||
        id,
 | 
					      description: (
 | 
				
			||||||
        table,
 | 
					        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
        datum: value[column],
 | 
					          <code className="text-white">{JSON.stringify(value, null, 2)}</code>
 | 
				
			||||||
        column,
 | 
					        </pre>
 | 
				
			||||||
        pathname
 | 
					      ),
 | 
				
			||||||
      })
 | 
					    })
 | 
				
			||||||
      if (res === undefined) throw new Error("something went wrong")
 | 
					    updateField({
 | 
				
			||||||
      toast({ title: "Field updated successfully." })
 | 
					      id,
 | 
				
			||||||
    } catch (error) {
 | 
					      table,
 | 
				
			||||||
      console.error(error)
 | 
					      number: value[column],
 | 
				
			||||||
      toast({ title: "Something went wrong." })
 | 
					      column,
 | 
				
			||||||
    }
 | 
					      pathname
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    setIsActive(false)
 | 
					    setIsActive(false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function onErrors(errors: Error) {
 | 
					  function onErrors(errors) {
 | 
				
			||||||
    toast({
 | 
					    toast({
 | 
				
			||||||
      title: "You have errors",
 | 
					      title: "You have errors",
 | 
				
			||||||
      description: errors.message,
 | 
					      description: (
 | 
				
			||||||
 | 
					        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
 | 
					          <code className="text-white">{JSON.stringify(errors, null, 2)}</code>
 | 
				
			||||||
 | 
					        </pre>
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    console.log(JSON.stringify(errors))
 | 
					    console.log(JSON.stringify(errors))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      onDoubleClick={() => setIsActive(true)}
 | 
					      onDoubleClick={() => setIsActive(prev => !prev)}
 | 
				
			||||||
      className={className + " w-full h-fit flex items-center justify-center"}
 | 
					      className="w-full h-fit flex items-center justify-center"
 | 
				
			||||||
      tabIndex={0}
 | 
					      tabIndex={0}
 | 
				
			||||||
      onKeyDown={e => {
 | 
					      onKeyDown={e => {
 | 
				
			||||||
        if (e.code === "Enter" && !isActive) {
 | 
					        if (e.code === "Enter" && !isActive) {
 | 
				
			||||||
| 
						 | 
					@ -80,8 +82,13 @@ export default function NumberInputCell({ cellContext, className }: { cellContex
 | 
				
			||||||
                >
 | 
					                >
 | 
				
			||||||
                  <FormControl
 | 
					                  <FormControl
 | 
				
			||||||
                  >
 | 
					                  >
 | 
				
			||||||
                    {/* @ts-ignore */}
 | 
					                    <Input
 | 
				
			||||||
                    <Input className="md:w-24" type="number" autoFocus={true} step={cellContext.column.columnDef.meta?.step} {...field} />
 | 
					                      className="w-24"
 | 
				
			||||||
 | 
					                      type="number"
 | 
				
			||||||
 | 
					                      autoFocus={true}
 | 
				
			||||||
 | 
					                      step={props.column.columnDef.meta?.step}
 | 
				
			||||||
 | 
					                      {...field}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
                  </FormControl>
 | 
					                  </FormControl>
 | 
				
			||||||
                  <FormMessage />
 | 
					                  <FormMessage />
 | 
				
			||||||
                </FormItem>
 | 
					                </FormItem>
 | 
				
			||||||
| 
						 | 
					@ -90,7 +97,7 @@ export default function NumberInputCell({ cellContext, className }: { cellContex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          </form>
 | 
					          </form>
 | 
				
			||||||
        </Form>
 | 
					        </Form>
 | 
				
			||||||
        : <p>{cellContext.cell.getValue()}</p>
 | 
					        : <p>{props.cell.getValue()}</p>
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    </div >
 | 
					    </div >
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,43 +9,40 @@ import { zodResolver } from "@hookform/resolvers/zod";
 | 
				
			||||||
import { toast } from "@/components/ui/use-toast";
 | 
					import { toast } from "@/components/ui/use-toast";
 | 
				
			||||||
import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form";
 | 
					import { Form, FormControl, FormField, FormItem, FormMessage } from "@/components/ui/form";
 | 
				
			||||||
import TitleContainer from "app/ui/titleContainer";
 | 
					import TitleContainer from "app/ui/titleContainer";
 | 
				
			||||||
import { useRouter } from "next/navigation";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function TextInputCell({ cellContext, className }: { className: string, cellContext: CellContext<any, any> }) {
 | 
					export function TextInputCell(props: CellContext<any, any>) {
 | 
				
			||||||
  const [isActive, setIsActive] = useState(false)
 | 
					  const [isActive, setIsActive] = useState(false)
 | 
				
			||||||
  //@ts-ignore
 | 
					
 | 
				
			||||||
  const table = cellContext.table.options.meta.tableName
 | 
					  const table = props.table.options.meta.tableName
 | 
				
			||||||
  const id = cellContext.row.original.id
 | 
					  const id = props.row.original.id
 | 
				
			||||||
  const column = cellContext.column.id
 | 
					  const column = props.column.id
 | 
				
			||||||
  //@ts-ignore
 | 
					  const pathname = props.table.options.meta.pathname
 | 
				
			||||||
  const pathname = cellContext.table.options.meta.pathname
 | 
					  const value = props.cell.getValue()
 | 
				
			||||||
  const value = cellContext.cell.getValue()
 | 
					  const formSchema = props.column.columnDef.meta.formSchema.pick({ [column]: true })
 | 
				
			||||||
  //@ts-ignore
 | 
					 | 
				
			||||||
  const formSchema = cellContext.column.columnDef.meta.formSchema.pick({ [column]: true })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const form = useForm<z.infer<typeof formSchema>>({
 | 
					  const form = useForm<z.infer<typeof formSchema>>({
 | 
				
			||||||
    resolver: zodResolver(formSchema),
 | 
					    resolver: zodResolver(formSchema),
 | 
				
			||||||
    defaultValues: {
 | 
					    defaultValues: {
 | 
				
			||||||
      [column]: cellContext.cell.getValue()
 | 
					      [column]: props.cell.getValue()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  const router = useRouter()
 | 
					
 | 
				
			||||||
  async function onSubmit(value: z.infer<typeof formSchema>) {
 | 
					  async function onSubmit(value: z.infer<typeof formSchema>) {
 | 
				
			||||||
    try {
 | 
					    toast({
 | 
				
			||||||
      const res = await updateField({
 | 
					      title: "You submitted the following values:",
 | 
				
			||||||
        id,
 | 
					      description: (
 | 
				
			||||||
        table,
 | 
					        <pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
 | 
				
			||||||
        datum: value[column],
 | 
					          <code className="text-white">{JSON.stringify(value, null, 2)}</code>
 | 
				
			||||||
        column,
 | 
					        </pre>
 | 
				
			||||||
        pathname
 | 
					      ),
 | 
				
			||||||
      })
 | 
					    })
 | 
				
			||||||
      if (res === undefined) throw new Error("something went wrong")
 | 
					    const res = await updateField({
 | 
				
			||||||
      toast({ title: "Field updated successfully." })
 | 
					      id,
 | 
				
			||||||
      router.refresh()
 | 
					      table,
 | 
				
			||||||
    } catch (error) {
 | 
					      datum: value[column],
 | 
				
			||||||
      console.error(error)
 | 
					      column,
 | 
				
			||||||
      toast({ title: "Something went wrong." })
 | 
					      pathname
 | 
				
			||||||
    }
 | 
					    })
 | 
				
			||||||
    setIsActive(false)
 | 
					    setIsActive(false)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,7 +60,7 @@ export function TextInputCell({ cellContext, className }: { className: string, c
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      onDoubleClick={() => setIsActive(prev => !prev)}
 | 
					      onDoubleClick={() => setIsActive(prev => !prev)}
 | 
				
			||||||
      className={className + " w-full h-fit flex items-center justify-left"}
 | 
					      className="w-full h-fit flex items-center justify-left"
 | 
				
			||||||
      tabIndex={0}
 | 
					      tabIndex={0}
 | 
				
			||||||
      onKeyDown={e => {
 | 
					      onKeyDown={e => {
 | 
				
			||||||
        if (e.code === "Enter" && !isActive) {
 | 
					        if (e.code === "Enter" && !isActive) {
 | 
				
			||||||
| 
						 | 
					@ -99,7 +96,7 @@ export function TextInputCell({ cellContext, className }: { className: string, c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          </form>
 | 
					          </form>
 | 
				
			||||||
        </Form>
 | 
					        </Form>
 | 
				
			||||||
        : <TitleContainer>{cellContext.cell.getValue()}</TitleContainer>
 | 
					        : <TitleContainer>{props.cell.getValue()}</TitleContainer>
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    </div >
 | 
					    </div >
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,8 @@ export const selectCol = {
 | 
				
			||||||
  id: "select",
 | 
					  id: "select",
 | 
				
			||||||
  header: (props: HeaderContext<any, any>) => {
 | 
					  header: (props: HeaderContext<any, any>) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className="flex items-start justify-left mx-auto">
 | 
					      <div className="flex items-center justify-center">
 | 
				
			||||||
        <Checkbox
 | 
					        <Checkbox
 | 
				
			||||||
          className="mr-4 ml-2"
 | 
					 | 
				
			||||||
          checked={props.table.getIsAllRowsSelected()}
 | 
					          checked={props.table.getIsAllRowsSelected()}
 | 
				
			||||||
          onCheckedChange={props.table.toggleAllRowsSelected}
 | 
					          onCheckedChange={props.table.toggleAllRowsSelected}
 | 
				
			||||||
          aria-label="select/deselect all rows"
 | 
					          aria-label="select/deselect all rows"
 | 
				
			||||||
| 
						 | 
					@ -19,9 +18,8 @@ export const selectCol = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cell: (props: CellContext<any, any>) => {
 | 
					  cell: (props: CellContext<any, any>) => {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className="flex items-start justify-left">
 | 
					      <div className="flex items-center justify-center">
 | 
				
			||||||
        <Checkbox
 | 
					        <Checkbox
 | 
				
			||||||
          className="mr-4 ml-2"
 | 
					 | 
				
			||||||
          checked={props.row.getIsSelected()}
 | 
					          checked={props.row.getIsSelected()}
 | 
				
			||||||
          onCheckedChange={props.row.toggleSelected}
 | 
					          onCheckedChange={props.row.toggleSelected}
 | 
				
			||||||
          aria-label="select/deselect row"
 | 
					          aria-label="select/deselect row"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
import { ComponentProps } from "react";
 | 
					import { ComponentProps } from "react";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function TitleContainer({ children }: ComponentProps<"div">) {
 | 
					export default function itleContainer({ children }: ComponentProps<"div">) {
 | 
				
			||||||
  let classes = "w-full text-left m-auto h-fit flex align-center text-xs md:text-sm"
 | 
					  let classes = "w-full text-left m-auto"
 | 
				
			||||||
 | 
					  console.table(children)
 | 
				
			||||||
  if (children == "RECORD DELETED") {
 | 
					  if (children == "RECORD DELETED") {
 | 
				
			||||||
 | 
					    console.log("BINGO")
 | 
				
			||||||
    classes = classes + " text-destructive font-bold"
 | 
					    classes = classes + " text-destructive font-bold"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return <p className={classes}>{children}</p>
 | 
					  return <span className="h-10 flex align-center"><p className={classes}>{children}</p></span>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue