implement 'edit' functionality for pubs
This commit is contained in:
		
							parent
							
								
									9bc1750848
								
							
						
					
					
						commit
						5b4e961859
					
				
							
								
								
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -6,6 +6,8 @@ import { revalidatePath } from "next/cache" | ||||||
| import { redirect } from "next/navigation" | import { redirect } from "next/navigation" | ||||||
| import { storySchema, subSchema } from "app/ui/forms/schemas" | import { storySchema, subSchema } from "app/ui/forms/schemas" | ||||||
| import { z } from "zod" | import { z } from "zod" | ||||||
|  | import { StoryWithGenres } from "app/story/page" | ||||||
|  | import { PubWithGenres } from "app/publication/page" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 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 }) { | ||||||
|  | @ -22,7 +24,7 @@ export async function updateField({ datum, table, column, id, pathname }: { datu | ||||||
| 		return res | 		return res | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		console.error(error) | 		console.error(error) | ||||||
| 		return false | 		return null | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -40,11 +42,11 @@ export async function updateGenres({ genres, table, id, pathname }: { genres: { | ||||||
| 		return res | 		return res | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		console.error(error) | 		console.error(error) | ||||||
| 		return false | 		return null | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function updateSub(data: Sub): Promise<Sub | boolean> { | export async function updateSub(data: Sub): Promise<Sub> { | ||||||
| 	"use server" | 	"use server" | ||||||
| 	try { | 	try { | ||||||
| 		subSchema.parse(data) | 		subSchema.parse(data) | ||||||
|  | @ -53,12 +55,12 @@ export async function updateSub(data: Sub): Promise<Sub | boolean> { | ||||||
| 		return res | 		return res | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		console.error(error) | 		console.error(error) | ||||||
| 		return false | 		return null | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export async function updateStory(data: Story & { genres: number[] }): Promise<Story | boolean> { | export async function updateStory(data: StoryWithGenres): Promise<{ success: string }> { | ||||||
| 	"use server" | 	"use server" | ||||||
| 
 | 
 | ||||||
| 	try { | 	try { | ||||||
|  | @ -66,25 +68,25 @@ export async function updateStory(data: Story & { genres: number[] }): Promise<S | ||||||
| 		const storyData = await prepStoryData(data) | 		const storyData = await prepStoryData(data) | ||||||
| 		const genresArray = await prepGenreData(data.genres) | 		const genresArray = await prepGenreData(data.genres) | ||||||
| 		//submit
 | 		//submit
 | ||||||
| 		const res = prisma.story.update({ | 		const res = await prisma.story.update({ | ||||||
| 			where: { id: data.id }, | 			where: { id: data.id }, | ||||||
| 			data: storyData | 			data: storyData | ||||||
| 		}) | 		}) | ||||||
| 		await prisma.story.update({ | 		const genreRes = await prisma.story.update({ | ||||||
| 			where: { id: data.id }, | 			where: { id: data.id }, | ||||||
| 			data: { | 			data: { | ||||||
| 				genres: { set: genresArray } | 				genres: { set: genresArray } | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 		return res | 		return { success: "Updated the story '" + res.title + "'." } | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		console.error(error) | 		console.error(error) | ||||||
| 		return false | 		return null | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export async function updatePub(data: Pub & { genres: number[] }): Promise<Pub | boolean> { | export async function updatePub(data: PubWithGenres): Promise<{ success: string }> { | ||||||
| 	"use server" | 	"use server" | ||||||
| 
 | 
 | ||||||
| 	try { | 	try { | ||||||
|  | @ -93,7 +95,7 @@ export async function updatePub(data: Pub & { genres: number[] }): Promise<Pub | | ||||||
| 			(data) | 			(data) | ||||||
| 		const genresArray = await prepGenreData(data.genres) | 		const genresArray = await prepGenreData(data.genres) | ||||||
| 		//submit
 | 		//submit
 | ||||||
| 		const res = prisma.pub.update({ | 		const res = await prisma.pub.update({ | ||||||
| 			where: { id: data.id }, | 			where: { id: data.id }, | ||||||
| 			data: pubData | 			data: pubData | ||||||
| 		}) | 		}) | ||||||
|  | @ -103,10 +105,10 @@ export async function updatePub(data: Pub & { genres: number[] }): Promise<Pub | | ||||||
| 				genres: { set: genresArray } | 				genres: { set: genresArray } | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 		return res | 		return { success: "Updated the publication '" + res.title + "'" } | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		console.error(error) | 		console.error(error) | ||||||
| 		return false | 		return null | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { z } from "zod"; | ||||||
| import { storySchema } from "app/ui/forms/schemas"; | import { storySchema } from "app/ui/forms/schemas"; | ||||||
| import { Pub, Story } from "@prisma/client"; | import { Pub, Story } from "@prisma/client"; | ||||||
| import { pubSchema } from "app/ui/forms/schemas"; | import { pubSchema } from "app/ui/forms/schemas"; | ||||||
|  | import { StoryWithGenres } from "app/story/page"; | ||||||
| 
 | 
 | ||||||
| //schemas
 | //schemas
 | ||||||
| 
 | 
 | ||||||
|  | @ -10,7 +11,7 @@ const pubSchemaTrimmed = pubSchema.omit({ genres: true }) | ||||||
| const genreSchema = z.object({ id: z.number() }) | const genreSchema = z.object({ id: z.number() }) | ||||||
| const genresSchema = z.array(genreSchema) | const genresSchema = z.array(genreSchema) | ||||||
| 
 | 
 | ||||||
| export async function prepStoryData(data: Story & { genres: number[] }): Promise<{ title: string, word_count: number }> { | export async function prepStoryData(data: StoryWithGenres): Promise<{ title: string, word_count: number }> { | ||||||
| 	const storyData = structuredClone(data) | 	const storyData = structuredClone(data) | ||||||
| 	delete storyData.genres | 	delete storyData.genres | ||||||
| 	//throw an error if validation fails
 | 	//throw an error if validation fails
 | ||||||
|  | @ -18,7 +19,7 @@ export async function prepStoryData(data: Story & { genres: number[] }): Promise | ||||||
| 	return storyData | 	return storyData | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export async function prepPubData(data: Pub & { genres: number[] }): Promise<Pub | Boolean> { | export async function prepPubData(data: Pub & { genres: number[] }): Promise<Pub> { | ||||||
| 	const pubData = structuredClone(data) | 	const pubData = structuredClone(data) | ||||||
| 	delete pubData.genres | 	delete pubData.genres | ||||||
| 	pubSchemaTrimmed.safeParse(pubData) | 	pubSchemaTrimmed.safeParse(pubData) | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 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, BookType, Clock, Drama, SquareArrowOutUpRight } from "lucide-react" | ||||||
| import { Button } from "@/components/ui/button" | import { Button } from "@/components/ui/button" | ||||||
| import { PubsWithGenres } from "./page" | import { PubWithGenres } 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" | ||||||
|  | @ -10,9 +10,9 @@ import { pubSchema } from "app/ui/forms/schemas" | ||||||
| import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput" | import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const columnHelper = createColumnHelper<PubsWithGenres>() | const columnHelper = createColumnHelper<PubWithGenres>() | ||||||
| 
 | 
 | ||||||
| export const columns: ColumnDef<PubsWithGenres>[] = [ | export const columns: ColumnDef<PubWithGenres>[] = [ | ||||||
|   selectCol, |   selectCol, | ||||||
|   { |   { | ||||||
|     accessorKey: "title", |     accessorKey: "title", | ||||||
|  | @ -26,7 +26,7 @@ export const columns: ColumnDef<PubsWithGenres>[] = [ | ||||||
|             Title |             Title | ||||||
|           </span> |           </span> | ||||||
|           <span className="block sm:hidden"><BookType /></span> |           <span className="block sm:hidden"><BookType /></span> | ||||||
|           <ArrowUpDown className="ml-2 h-4 w-4" /> |           <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" /> | ||||||
|         </Button> |         </Button> | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -7,34 +7,23 @@ 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 { Plus } from "lucide-react"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
|  | import { PubWithGenres } from "./page"; | ||||||
| 
 | 
 | ||||||
| export default function CreatePubDialog({ genres }: ComponentProps<"div"> & { genres: Genre[] }) { | export default function EditPubDialog({ genres, closeDialog, defaults, dbAction }: ComponentProps<"div"> & { genres: Genre[], closeDialog: () => void, defaults: PubWithGenres, dbAction: (data: PubWithGenres) => Promise<{ success: string }> }) { | ||||||
| 
 | 
 | ||||||
|   const [isOpen, setIsOpen] = useState(false) |  | ||||||
|   function closeDialog() { |  | ||||||
|     setIsOpen(false) |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
| 
 | 
 | ||||||
|     <Dialog open={isOpen} onOpenChange={setIsOpen}> |     <> | ||||||
|       <DialogTrigger asChild> |       <DialogHeader> | ||||||
|         <Button> |         <DialogTitle>Edit publication</DialogTitle> | ||||||
|           <span className="hidden md:block">Create new publication</span> |         <DialogDescription>Modify an entry for an existing publication.</DialogDescription> | ||||||
|           <Plus className="block md:hidden" /> |       </DialogHeader> | ||||||
|         </Button> |       <PubForm dbAction={dbAction} genres={genres} closeDialog={closeDialog} defaults={defaults} /> | ||||||
|       </DialogTrigger> |       <DialogFooter> | ||||||
|       <DialogContent> |         <Button form="pubform">Submit</Button> | ||||||
|         <DialogHeader> |       </DialogFooter> | ||||||
|           <DialogTitle>Edit publication</DialogTitle> |     </> | ||||||
|           <DialogDescription>Modify an entry for an existing publication.</DialogDescription> |  | ||||||
|         </DialogHeader> |  | ||||||
|         <PubForm dbAction={createPub} genres={genres} closeDialog={closeDialog} /> |  | ||||||
|         <DialogFooter> |  | ||||||
|           <Button form="pubform">Submit</Button> |  | ||||||
|         </DialogFooter> |  | ||||||
| 
 | 
 | ||||||
|       </DialogContent> |  | ||||||
|     </Dialog> |  | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 PubsWithGenres = Pub & { genres: Array<Genre> } | export type PubWithGenres = Pub & { genres: Array<Genre> } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ export const columns: ColumnDef<StoryWithGenres>[] = [ | ||||||
|             Title |             Title | ||||||
|           </span> |           </span> | ||||||
|           <span className="block sm:hidden"><BookType /></span> |           <span className="block sm:hidden"><BookType /></span> | ||||||
|           <ArrowUpDown className="ml-2 h-4 w-4" /> |           <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" /> | ||||||
|         </Button> |         </Button> | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|  | @ -52,7 +52,7 @@ export const columns: ColumnDef<StoryWithGenres>[] = [ | ||||||
|           <span className="sm:hidden"> |           <span className="sm:hidden"> | ||||||
|             <Tally5 /> |             <Tally5 /> | ||||||
|           </span> |           </span> | ||||||
|           <ArrowUpDown className="ml-2 h-4 w-4" /> |           <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" /> | ||||||
|         </Button> |         </Button> | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| "use client" | "use client" | ||||||
| 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, useState } from "react"; | ||||||
| import { Genre, Story } from "@prisma/client"; | import { Genre, Story } from "@prisma/client"; | ||||||
| import StoryForm from "app/ui/forms/story"; | import StoryForm from "app/ui/forms/story"; | ||||||
|  | import { StoryWithGenres } from "./page"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export default function EditStoryDialog({ genres, closeDialog, defaults }: ComponentProps<"div"> & { genres: Genre[], closeDialog: () => void, defaults: Story }) { | export default function EditStoryDialog({ genres, closeDialog, defaults, dbAction }: ComponentProps<"div"> & { genres: Genre[], closeDialog: () => void, defaults: StoryWithGenres, dbAction: (data: StoryWithGenres) => Promise<{ success: string }> }) { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|  | @ -16,7 +16,7 @@ export default function EditStoryDialog({ genres, closeDialog, defaults }: Compo | ||||||
|         <DialogTitle>Edit story</DialogTitle> |         <DialogTitle>Edit 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} defaults={defaults} /> |       <StoryForm dbAction={dbAction} genres={genres} className="" closeDialog={closeDialog} defaults={defaults} /> | ||||||
|       <DialogFooter> |       <DialogFooter> | ||||||
|         <Button form="storyform">Submit</Button> |         <Button form="storyform">Submit</Button> | ||||||
|       </DialogFooter> |       </DialogFooter> | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ export const columns: ColumnDef<SubComplete>[] = [ | ||||||
|       return ( |       return ( | ||||||
|         <Button |         <Button | ||||||
|           variant="ghost" |           variant="ghost" | ||||||
|           className="p-0" |           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> |           <span className="hidden md:block"> Date Submitted </span> | ||||||
|  | @ -76,7 +76,7 @@ export const columns: ColumnDef<SubComplete>[] = [ | ||||||
|       return ( |       return ( | ||||||
|         <Button |         <Button | ||||||
|           variant="ghost" |           variant="ghost" | ||||||
|           className="p-0" |           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> |           <span className="hidden md:block"> Date Responded </span> | ||||||
|  | @ -105,7 +105,7 @@ export const columns: ColumnDef<SubComplete>[] = [ | ||||||
|       return ( |       return ( | ||||||
|         <Button |         <Button | ||||||
|           variant="ghost" |           variant="ghost" | ||||||
|           className="p-0" |           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"> Response </span> |           <span className="hidden md:block"> Response </span> | ||||||
|  |  | ||||||
|  | @ -706,6 +706,10 @@ body { | ||||||
|   z-index: 100; |   z-index: 100; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .float-right { | ||||||
|  |   float: right; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .m-auto { | .m-auto { | ||||||
|   margin: auto; |   margin: auto; | ||||||
| } | } | ||||||
|  | @ -777,14 +781,6 @@ body { | ||||||
|   margin-top: 1.5rem; |   margin-top: 1.5rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .mt-auto { |  | ||||||
|   margin-top: auto; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .mb-4 { |  | ||||||
|   margin-bottom: 1rem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .block { | .block { | ||||||
|   display: block; |   display: block; | ||||||
| } | } | ||||||
|  | @ -887,10 +883,6 @@ body { | ||||||
|   height: 100vh; |   height: 100vh; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .h-5\/6 { |  | ||||||
|   height: 83.333333%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .max-h-96 { | .max-h-96 { | ||||||
|   max-height: 24rem; |   max-height: 24rem; | ||||||
| } | } | ||||||
|  | @ -976,8 +968,8 @@ body { | ||||||
|   width: 100vw; |   width: 100vw; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .w-5\/6 { | .w-1\/2 { | ||||||
|   width: 83.333333%; |   width: 50%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .min-w-\[8rem\] { | .min-w-\[8rem\] { | ||||||
|  | @ -1021,6 +1013,18 @@ body { | ||||||
|   max-width: 20rem; |   max-width: 20rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .max-w-40 { | ||||||
|  |   max-width: 10rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .max-w-\[50\%\] { | ||||||
|  |   max-width: 50%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .max-w-60 { | ||||||
|  |   max-width: 15rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .shrink-0 { | .shrink-0 { | ||||||
|   flex-shrink: 0; |   flex-shrink: 0; | ||||||
| } | } | ||||||
|  | @ -1105,6 +1109,18 @@ body { | ||||||
|   flex-wrap: wrap; |   flex-wrap: wrap; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .content-center { | ||||||
|  |   align-content: center; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .content-start { | ||||||
|  |   align-content: flex-start; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .content-between { | ||||||
|  |   align-content: space-between; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .items-start { | .items-start { | ||||||
|   align-items: flex-start; |   align-items: flex-start; | ||||||
| } | } | ||||||
|  | @ -1129,6 +1145,10 @@ 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; | ||||||
| } | } | ||||||
|  | @ -1220,6 +1240,10 @@ body { | ||||||
|   margin-bottom: calc(2rem * var(--tw-space-y-reverse)); |   margin-bottom: calc(2rem * var(--tw-space-y-reverse)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .self-end { | ||||||
|  |   align-self: flex-end; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .justify-self-end { | .justify-self-end { | ||||||
|   justify-self: end; |   justify-self: end; | ||||||
| } | } | ||||||
|  | @ -1267,11 +1291,6 @@ body { | ||||||
|   border-top-right-radius: 1.5rem; |   border-top-right-radius: 1.5rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .rounded-l-3xl { |  | ||||||
|   border-top-left-radius: 1.5rem; |  | ||||||
|   border-bottom-left-radius: 1.5rem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .rounded-tl-3xl { | .rounded-tl-3xl { | ||||||
|   border-top-left-radius: 1.5rem; |   border-top-left-radius: 1.5rem; | ||||||
| } | } | ||||||
|  | @ -1493,6 +1512,10 @@ body { | ||||||
|   text-align: center; |   text-align: center; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .align-top { | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .align-middle { | .align-middle { | ||||||
|   vertical-align: middle; |   vertical-align: middle; | ||||||
| } | } | ||||||
|  | @ -1536,6 +1559,11 @@ body { | ||||||
|   line-height: 1rem; |   line-height: 1rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .text-6xl { | ||||||
|  |   font-size: 3.75rem; | ||||||
|  |   line-height: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .font-black { | .font-black { | ||||||
|   font-weight: 900; |   font-weight: 900; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,15 +22,17 @@ import GenrePicker from "./genrePicker" | ||||||
| import { pubSchema } from "./schemas" | import { pubSchema } from "./schemas" | ||||||
| import { useRouter } from "next/navigation" | import { useRouter } from "next/navigation" | ||||||
| import { Ban } from "lucide-react" | 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?: Pub }) { | export default function PubForm({ genres, dbAction, className, closeDialog, defaults }: ComponentProps<"div"> & { genres: Array<Genre>, dbAction: (data: any) => Promise<{ success: string }>, closeDialog: () => void, defaults?: PubWithGenres }) { | ||||||
| 	const form = useForm<z.infer<typeof pubSchema>>({ | 	const form = useForm<z.infer<typeof pubSchema>>({ | ||||||
| 		resolver: zodResolver(pubSchema), | 		resolver: zodResolver(pubSchema), | ||||||
| 		defaultValues: { | 		defaultValues: { | ||||||
| 			title: "", | 			id: defaults?.id, | ||||||
| 			link: "", | 			title: defaults?.title ?? "", | ||||||
| 			query_after_days: 30, | 			link: defaults?.link ?? "", | ||||||
| 			genres: [] | 			query_after_days: defaults?.query_after_days ?? 30, | ||||||
|  | 			genres: defaults?.genres.map(e => e.id) ?? [] | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | @ -39,7 +41,7 @@ export default function PubForm({ genres, dbAction, className, closeDialog, defa | ||||||
| 	async function onSubmit(values: z.infer<typeof pubSchema>) { | 	async function onSubmit(values: z.infer<typeof pubSchema>) { | ||||||
| 		try { | 		try { | ||||||
| 			const res = await dbAction(values) | 			const res = await dbAction(values) | ||||||
| 			if (!res) throw new Error("something went wrong") | 			if (!res?.success) throw new Error("something went wrong") | ||||||
| 			toast({ title: "Success!", description: res.success }) | 			toast({ title: "Success!", description: res.success }) | ||||||
| 			router.refresh() | 			router.refresh() | ||||||
| 			closeDialog() | 			closeDialog() | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ export const storySchema = z.object({ | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| export const pubSchema = z.object({ | export const pubSchema = z.object({ | ||||||
|  | 	id: z.coerce.number().optional(), | ||||||
| 	title: z.string().min(2).max(50), | 	title: z.string().min(2).max(50), | ||||||
| 	link: z.string(), | 	link: z.string(), | ||||||
| 	query_after_days: z.coerce.number().min(30), | 	query_after_days: z.coerce.number().min(30), | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de | ||||||
| 	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, | ||||||
| 			title: defaults?.title ?? "", | 			title: defaults?.title ?? "", | ||||||
| 			word_count: defaults?.word_count ?? 500, | 			word_count: defaults?.word_count ?? 500, | ||||||
| 			genres: defaults?.genres.map(e => e.id) ?? [] | 			genres: defaults?.genres.map(e => e.id) ?? [] | ||||||
|  | @ -47,7 +48,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de | ||||||
| 		try { | 		try { | ||||||
| 			const res = await dbAction(values) | 			const res = await dbAction(values) | ||||||
| 			//server actions return undefined if middleware authentication fails
 | 			//server actions return undefined if middleware authentication fails
 | ||||||
| 			if (!res.success) throw new Error("something went wrong") | 			if (!res?.success) throw new Error("something went wrong") | ||||||
| 			toast({ title: "Success!", description: res.success }) | 			toast({ title: "Success!", description: res.success }) | ||||||
| 			router.refresh() | 			router.refresh() | ||||||
| 			closeDialog() | 			closeDialog() | ||||||
|  | @ -86,7 +87,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de | ||||||
| 						)} | 						)} | ||||||
| 					/> | 					/> | ||||||
| 
 | 
 | ||||||
| 					<div className="inline-flex flex-wrap w-full gap-x-16 gap-y-8 items-baseline max-w-full"> | 					<div className="inline-flex flex-wrap justify-around items-start w-full gap-x-16 gap-y-8 items-baseline max-w-full"> | ||||||
| 
 | 
 | ||||||
| 						<GenrePicker | 						<GenrePicker | ||||||
| 							genres={genres} | 							genres={genres} | ||||||
|  | @ -97,7 +98,7 @@ 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} {...field}></Input> | ||||||
|  |  | ||||||
|  | @ -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="" key={e.name}>{e.name}</Badge>))} |       {props.genres.map((e: Genre) => (<Badge className="text-xs md:text-sm" key={e.name}>{e.name}</Badge>))} | ||||||
|     </div> |     </div> | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ 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 } from "app/lib/update" | import { updateField, updatePub, updateStory } 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" | ||||||
|  | @ -53,6 +53,7 @@ import { DialogTitle } from "@radix-ui/react-dialog" | ||||||
| import { toast } from "@/components/ui/use-toast" | import { toast } from "@/components/ui/use-toast" | ||||||
| import { useRouter } from "next/navigation" | import { useRouter } from "next/navigation" | ||||||
| import EditStoryDialog from "app/story/edit" | 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>[] | ||||||
|  | @ -178,12 +179,19 @@ export function DataTable<TData, TValue>({ | ||||||
|             /> |             /> | ||||||
|             : tableName === "story" ? |             : tableName === "story" ? | ||||||
|               < EditStoryDialog |               < EditStoryDialog | ||||||
|  |                 dbAction={updateStory} | ||||||
|                 genres={genres} |                 genres={genres} | ||||||
|                 // TODO: prepare genre data so that it can be read by StoryForm 
 |  | ||||||
|                 defaults={dialogRow?.original} |                 defaults={dialogRow?.original} | ||||||
|                 closeDialog={closeEditDialog} |                 closeDialog={closeEditDialog} | ||||||
|               /> |               /> | ||||||
|               : "" |               : tableName === "pub" ? | ||||||
|  |                 <EditPubDialog | ||||||
|  |                   dbAction={updatePub} | ||||||
|  |                   genres={genres} | ||||||
|  |                   defaults={dialogRow?.original} | ||||||
|  |                   closeDialog={closeEditDialog} | ||||||
|  |                 /> | ||||||
|  |                 : "" | ||||||
|           } |           } | ||||||
|         </DialogContent> |         </DialogContent> | ||||||
|       </Dialog> |       </Dialog> | ||||||
|  | @ -205,6 +213,7 @@ export function DataTable<TData, TValue>({ | ||||||
|                   const res = await deleteRecord(dialogRow.original.id, pathname) |                   const res = await deleteRecord(dialogRow.original.id, pathname) | ||||||
|                   if (!res) toast({ title: "Oh dear...", description: "Failed to delete." }) |                   if (!res) toast({ title: "Oh dear...", description: "Failed to delete." }) | ||||||
|                   if (res) toast({ title: "Successfully deleted record of id:", description: dialogRow.original.id }) |                   if (res) toast({ title: "Successfully deleted record of id:", description: dialogRow.original.id }) | ||||||
|  |                   table.resetRowSelection() | ||||||
|                   router.refresh() |                   router.refresh() | ||||||
|                 }}>Yes, delete it! |                 }}>Yes, delete it! | ||||||
|               </Button> |               </Button> | ||||||
|  | @ -235,6 +244,7 @@ export function DataTable<TData, TValue>({ | ||||||
|                 const res = await deleteRecords(recordIds, pathname) |                 const res = await deleteRecords(recordIds, pathname) | ||||||
|                 if (!res) toast({ title: "Oh dear...", description: "Failed to delete." }) |                 if (!res) toast({ title: "Oh dear...", description: "Failed to delete." }) | ||||||
|                 if (res) toast({ title: "Successfully deleted records of id:", description: JSON.stringify(recordIds) }) |                 if (res) toast({ title: "Successfully deleted records of id:", description: JSON.stringify(recordIds) }) | ||||||
|  |                 table.resetRowSelection() | ||||||
|                 router.refresh() |                 router.refresh() | ||||||
|                 setIsDeleteDialogVisible(false) |                 setIsDeleteDialogVisible(false) | ||||||
|               }}> |               }}> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue