implement 'edit' functionality for pubs
This commit is contained in:
		
							parent
							
								
									8822a5449c
								
							
						
					
					
						commit
						b8466a5e38
					
				
							
								
								
									
										
											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 { storySchema, subSchema } from "app/ui/forms/schemas"
 | 
			
		||||
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 }) {
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +24,7 @@ export async function updateField({ datum, table, column, id, pathname }: { datu
 | 
			
		|||
		return res
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		console.error(error)
 | 
			
		||||
		return false
 | 
			
		||||
		return null
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,11 +42,11 @@ export async function updateGenres({ genres, table, id, pathname }: { genres: {
 | 
			
		|||
		return res
 | 
			
		||||
	} catch (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"
 | 
			
		||||
	try {
 | 
			
		||||
		subSchema.parse(data)
 | 
			
		||||
| 
						 | 
				
			
			@ -53,12 +55,12 @@ export async function updateSub(data: Sub): Promise<Sub | boolean> {
 | 
			
		|||
		return res
 | 
			
		||||
	} catch (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"
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,25 +68,25 @@ export async function updateStory(data: Story & { genres: number[] }): Promise<S
 | 
			
		|||
		const storyData = await prepStoryData(data)
 | 
			
		||||
		const genresArray = await prepGenreData(data.genres)
 | 
			
		||||
		//submit
 | 
			
		||||
		const res = prisma.story.update({
 | 
			
		||||
		const res = await prisma.story.update({
 | 
			
		||||
			where: { id: data.id },
 | 
			
		||||
			data: storyData
 | 
			
		||||
		})
 | 
			
		||||
		await prisma.story.update({
 | 
			
		||||
		const genreRes = await prisma.story.update({
 | 
			
		||||
			where: { id: data.id },
 | 
			
		||||
			data: {
 | 
			
		||||
				genres: { set: genresArray }
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		return res
 | 
			
		||||
		return { success: "Updated the story '" + res.title + "'." }
 | 
			
		||||
	} catch (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"
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +95,7 @@ export async function updatePub(data: Pub & { genres: number[] }): Promise<Pub |
 | 
			
		|||
			(data)
 | 
			
		||||
		const genresArray = await prepGenreData(data.genres)
 | 
			
		||||
		//submit
 | 
			
		||||
		const res = prisma.pub.update({
 | 
			
		||||
		const res = await prisma.pub.update({
 | 
			
		||||
			where: { id: data.id },
 | 
			
		||||
			data: pubData
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			@ -103,10 +105,10 @@ export async function updatePub(data: Pub & { genres: number[] }): Promise<Pub |
 | 
			
		|||
				genres: { set: genresArray }
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		return res
 | 
			
		||||
		return { success: "Updated the publication '" + res.title + "'" }
 | 
			
		||||
	} catch (error) {
 | 
			
		||||
		console.error(error)
 | 
			
		||||
		return false
 | 
			
		||||
		return null
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ import { z } from "zod";
 | 
			
		|||
import { storySchema } from "app/ui/forms/schemas";
 | 
			
		||||
import { Pub, Story } from "@prisma/client";
 | 
			
		||||
import { pubSchema } from "app/ui/forms/schemas";
 | 
			
		||||
import { StoryWithGenres } from "app/story/page";
 | 
			
		||||
 | 
			
		||||
//schemas
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ const pubSchemaTrimmed = pubSchema.omit({ genres: true })
 | 
			
		|||
const genreSchema = z.object({ id: z.number() })
 | 
			
		||||
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)
 | 
			
		||||
	delete storyData.genres
 | 
			
		||||
	//throw an error if validation fails
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +19,7 @@ export async function prepStoryData(data: Story & { genres: number[] }): Promise
 | 
			
		|||
	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)
 | 
			
		||||
	delete pubData.genres
 | 
			
		||||
	pubSchemaTrimmed.safeParse(pubData)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
import { ColumnDef, createColumnHelper } from "@tanstack/react-table"
 | 
			
		||||
import { ArrowUpDown, BookType, Clock, Drama, SquareArrowOutUpRight } from "lucide-react"
 | 
			
		||||
import { Button } from "@/components/ui/button"
 | 
			
		||||
import { PubsWithGenres } from "./page"
 | 
			
		||||
import { PubWithGenres } from "./page"
 | 
			
		||||
import { TextInputCell } from "app/ui/tables/inputs/textInput"
 | 
			
		||||
import { selectCol } from "app/ui/tables/selectColumn"
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const columnHelper = createColumnHelper<PubsWithGenres>()
 | 
			
		||||
const columnHelper = createColumnHelper<PubWithGenres>()
 | 
			
		||||
 | 
			
		||||
export const columns: ColumnDef<PubsWithGenres>[] = [
 | 
			
		||||
export const columns: ColumnDef<PubWithGenres>[] = [
 | 
			
		||||
  selectCol,
 | 
			
		||||
  {
 | 
			
		||||
    accessorKey: "title",
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ export const columns: ColumnDef<PubsWithGenres>[] = [
 | 
			
		|||
            Title
 | 
			
		||||
          </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>
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,34 +7,23 @@ import { createPub } from "app/lib/create";
 | 
			
		|||
import PubForm from "app/ui/forms/pub";
 | 
			
		||||
import { Plus } from "lucide-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 (
 | 
			
		||||
 | 
			
		||||
    <Dialog open={isOpen} onOpenChange={setIsOpen}>
 | 
			
		||||
      <DialogTrigger asChild>
 | 
			
		||||
        <Button>
 | 
			
		||||
          <span className="hidden md:block">Create new publication</span>
 | 
			
		||||
          <Plus className="block md:hidden" />
 | 
			
		||||
        </Button>
 | 
			
		||||
      </DialogTrigger>
 | 
			
		||||
      <DialogContent>
 | 
			
		||||
        <DialogHeader>
 | 
			
		||||
          <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>
 | 
			
		||||
    <>
 | 
			
		||||
      <DialogHeader>
 | 
			
		||||
        <DialogTitle>Edit publication</DialogTitle>
 | 
			
		||||
        <DialogDescription>Modify an entry for an existing publication.</DialogDescription>
 | 
			
		||||
      </DialogHeader>
 | 
			
		||||
      <PubForm dbAction={dbAction} genres={genres} closeDialog={closeDialog} defaults={defaults} />
 | 
			
		||||
      <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 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
 | 
			
		||||
          </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>
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ export const columns: ColumnDef<StoryWithGenres>[] = [
 | 
			
		|||
          <span className="sm:hidden">
 | 
			
		||||
            <Tally5 />
 | 
			
		||||
          </span>
 | 
			
		||||
          <ArrowUpDown className="ml-2 h-4 w-4" />
 | 
			
		||||
          <ArrowUpDown className="ml-2 h-4 w-4 hidden md:block" />
 | 
			
		||||
        </Button>
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,13 @@
 | 
			
		|||
"use client"
 | 
			
		||||
import { createStory } from "app/lib/create"
 | 
			
		||||
import { Dialog, DialogHeader, DialogTrigger, DialogContent, DialogClose, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog";
 | 
			
		||||
import { Button } from "@/components/ui/button";
 | 
			
		||||
import { ComponentProps, useState } from "react";
 | 
			
		||||
import { Genre, Story } from "@prisma/client";
 | 
			
		||||
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 (
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ export default function EditStoryDialog({ genres, closeDialog, defaults }: Compo
 | 
			
		|||
        <DialogTitle>Edit story</DialogTitle>
 | 
			
		||||
        <DialogDescription>Create an entry for a new story i.e. a thing you intend to submit for publication.</DialogDescription>
 | 
			
		||||
      </DialogHeader>
 | 
			
		||||
      <StoryForm dbAction={createStory} genres={genres} className="" closeDialog={closeDialog} defaults={defaults} />
 | 
			
		||||
      <StoryForm dbAction={dbAction} genres={genres} className="" closeDialog={closeDialog} defaults={defaults} />
 | 
			
		||||
      <DialogFooter>
 | 
			
		||||
        <Button form="storyform">Submit</Button>
 | 
			
		||||
      </DialogFooter>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
			
		|||
      return (
 | 
			
		||||
        <Button
 | 
			
		||||
          variant="ghost"
 | 
			
		||||
          className="p-0"
 | 
			
		||||
          className="p-0 flex justify-center w-full"
 | 
			
		||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
			
		||||
        >
 | 
			
		||||
          <span className="hidden md:block"> Date Submitted </span>
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +76,7 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
			
		|||
      return (
 | 
			
		||||
        <Button
 | 
			
		||||
          variant="ghost"
 | 
			
		||||
          className="p-0"
 | 
			
		||||
          className="p-0 flex justify-center w-full"
 | 
			
		||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
			
		||||
        >
 | 
			
		||||
          <span className="hidden md:block"> Date Responded </span>
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ export const columns: ColumnDef<SubComplete>[] = [
 | 
			
		|||
      return (
 | 
			
		||||
        <Button
 | 
			
		||||
          variant="ghost"
 | 
			
		||||
          className="p-0"
 | 
			
		||||
          className="p-0 flex justify-center w-full"
 | 
			
		||||
          onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
 | 
			
		||||
        >
 | 
			
		||||
          <span className="hidden md:block"> Response </span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -706,6 +706,10 @@ body {
 | 
			
		|||
  z-index: 100;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.float-right {
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.m-auto {
 | 
			
		||||
  margin: auto;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -777,14 +781,6 @@ body {
 | 
			
		|||
  margin-top: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mt-auto {
 | 
			
		||||
  margin-top: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mb-4 {
 | 
			
		||||
  margin-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.block {
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -887,10 +883,6 @@ body {
 | 
			
		|||
  height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.h-5\/6 {
 | 
			
		||||
  height: 83.333333%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.max-h-96 {
 | 
			
		||||
  max-height: 24rem;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -976,8 +968,8 @@ body {
 | 
			
		|||
  width: 100vw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.w-5\/6 {
 | 
			
		||||
  width: 83.333333%;
 | 
			
		||||
.w-1\/2 {
 | 
			
		||||
  width: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.min-w-\[8rem\] {
 | 
			
		||||
| 
						 | 
				
			
			@ -1021,6 +1013,18 @@ body {
 | 
			
		|||
  max-width: 20rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.max-w-40 {
 | 
			
		||||
  max-width: 10rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.max-w-\[50\%\] {
 | 
			
		||||
  max-width: 50%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.max-w-60 {
 | 
			
		||||
  max-width: 15rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.shrink-0 {
 | 
			
		||||
  flex-shrink: 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1105,6 +1109,18 @@ body {
 | 
			
		|||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content-center {
 | 
			
		||||
  align-content: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content-start {
 | 
			
		||||
  align-content: flex-start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.content-between {
 | 
			
		||||
  align-content: space-between;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.items-start {
 | 
			
		||||
  align-items: flex-start;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1129,6 +1145,10 @@ body {
 | 
			
		|||
  justify-content: space-between;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.justify-around {
 | 
			
		||||
  justify-content: space-around;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.gap-1 {
 | 
			
		||||
  gap: 0.25rem;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1220,6 +1240,10 @@ body {
 | 
			
		|||
  margin-bottom: calc(2rem * var(--tw-space-y-reverse));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.self-end {
 | 
			
		||||
  align-self: flex-end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.justify-self-end {
 | 
			
		||||
  justify-self: end;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1267,11 +1291,6 @@ body {
 | 
			
		|||
  border-top-right-radius: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rounded-l-3xl {
 | 
			
		||||
  border-top-left-radius: 1.5rem;
 | 
			
		||||
  border-bottom-left-radius: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.rounded-tl-3xl {
 | 
			
		||||
  border-top-left-radius: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1493,6 +1512,10 @@ body {
 | 
			
		|||
  text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.align-top {
 | 
			
		||||
  vertical-align: top;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.align-middle {
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1536,6 +1559,11 @@ body {
 | 
			
		|||
  line-height: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-6xl {
 | 
			
		||||
  font-size: 3.75rem;
 | 
			
		||||
  line-height: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.font-black {
 | 
			
		||||
  font-weight: 900;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,15 +22,17 @@ 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?: 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>>({
 | 
			
		||||
		resolver: zodResolver(pubSchema),
 | 
			
		||||
		defaultValues: {
 | 
			
		||||
			title: "",
 | 
			
		||||
			link: "",
 | 
			
		||||
			query_after_days: 30,
 | 
			
		||||
			genres: []
 | 
			
		||||
			id: defaults?.id,
 | 
			
		||||
			title: defaults?.title ?? "",
 | 
			
		||||
			link: defaults?.link ?? "",
 | 
			
		||||
			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>) {
 | 
			
		||||
		try {
 | 
			
		||||
			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 })
 | 
			
		||||
			router.refresh()
 | 
			
		||||
			closeDialog()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@ export const storySchema = z.object({
 | 
			
		|||
})
 | 
			
		||||
 | 
			
		||||
export const pubSchema = z.object({
 | 
			
		||||
	id: z.coerce.number().optional(),
 | 
			
		||||
	title: z.string().min(2).max(50),
 | 
			
		||||
	link: z.string(),
 | 
			
		||||
	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>>({
 | 
			
		||||
		resolver: zodResolver(formSchema),
 | 
			
		||||
		defaultValues: {
 | 
			
		||||
			id: defaults?.id,
 | 
			
		||||
			title: defaults?.title ?? "",
 | 
			
		||||
			word_count: defaults?.word_count ?? 500,
 | 
			
		||||
			genres: defaults?.genres.map(e => e.id) ?? []
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +48,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de
 | 
			
		|||
		try {
 | 
			
		||||
			const res = await dbAction(values)
 | 
			
		||||
			//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 })
 | 
			
		||||
			router.refresh()
 | 
			
		||||
			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
 | 
			
		||||
							genres={genres}
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,7 @@ export default function StoryForm({ genres, dbAction, className, closeDialog, de
 | 
			
		|||
							control={form.control}
 | 
			
		||||
							name="word_count"
 | 
			
		||||
							render={({ field }) => (
 | 
			
		||||
								<FormItem className="flex flex-col">
 | 
			
		||||
								<FormItem className="flex flex-col ">
 | 
			
		||||
									<FormLabel className="h-5">Word count</FormLabel>
 | 
			
		||||
									<FormControl>
 | 
			
		||||
										<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> }) {
 | 
			
		||||
  return (
 | 
			
		||||
    <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>
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ import { deleteRecord, deleteRecords } from "app/lib/del"
 | 
			
		|||
import { Pathname } from "app/types"
 | 
			
		||||
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTrigger } from "@/components/ui/dialog"
 | 
			
		||||
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 { Genre, Pub, Response, Story } from "@prisma/client"
 | 
			
		||||
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 { useRouter } from "next/navigation"
 | 
			
		||||
import EditStoryDialog from "app/story/edit"
 | 
			
		||||
import EditPubDialog from "app/publication/edit"
 | 
			
		||||
 | 
			
		||||
export interface DataTableProps<TData, TValue> {
 | 
			
		||||
  columns: ColumnDef<TData, TValue>[]
 | 
			
		||||
| 
						 | 
				
			
			@ -178,12 +179,19 @@ export function DataTable<TData, TValue>({
 | 
			
		|||
            />
 | 
			
		||||
            : tableName === "story" ?
 | 
			
		||||
              < EditStoryDialog
 | 
			
		||||
                dbAction={updateStory}
 | 
			
		||||
                genres={genres}
 | 
			
		||||
                // TODO: prepare genre data so that it can be read by StoryForm 
 | 
			
		||||
                defaults={dialogRow?.original}
 | 
			
		||||
                closeDialog={closeEditDialog}
 | 
			
		||||
              />
 | 
			
		||||
              : ""
 | 
			
		||||
              : tableName === "pub" ?
 | 
			
		||||
                <EditPubDialog
 | 
			
		||||
                  dbAction={updatePub}
 | 
			
		||||
                  genres={genres}
 | 
			
		||||
                  defaults={dialogRow?.original}
 | 
			
		||||
                  closeDialog={closeEditDialog}
 | 
			
		||||
                />
 | 
			
		||||
                : ""
 | 
			
		||||
          }
 | 
			
		||||
        </DialogContent>
 | 
			
		||||
      </Dialog>
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +213,7 @@ export function DataTable<TData, TValue>({
 | 
			
		|||
                  const res = await 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!
 | 
			
		||||
              </Button>
 | 
			
		||||
| 
						 | 
				
			
			@ -235,6 +244,7 @@ export function DataTable<TData, TValue>({
 | 
			
		|||
                const res = await 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) })
 | 
			
		||||
                table.resetRowSelection()
 | 
			
		||||
                router.refresh()
 | 
			
		||||
                setIsDeleteDialogVisible(false)
 | 
			
		||||
              }}>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue