implement genre picker cell (janky)

This commit is contained in:
andrzej 2024-07-23 17:40:35 +02:00
parent 41951a2ac6
commit 79e3403902
14 changed files with 174 additions and 74 deletions

Binary file not shown.

View File

@ -1,16 +1,15 @@
"use server"
import { Genre, Story } from "@prisma/client"
import { StoryWithGenres } from "app/story/page"
import { Genre } from "@prisma/client"
import prisma from "./db"
import { revalidatePath } from "next/cache"
import { redirect } from "next/navigation"
export async function updateField({ string: string, number, table, column, id, pathname }: { string?: string, number?: number, 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 }) {
const res = await prisma[table].update({
where: { id },
data: {
[column]: string ?? number
[column]: datum
}
})
console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
@ -18,3 +17,14 @@ export async function updateField({ string: string, number, table, column, id, p
redirect(pathname)
}
export async function updateGenres({ genres, table, id, pathname }: { genres: { id: number }[], table: string, id: number, pathname: string }) {
const res = await prisma[table].update({
where: { id },
data: {
genres: { set: genres }
}
})
console.log(`updated record in ${table}: ${JSON.stringify(res)}`)
revalidatePath(pathname)
redirect(pathname)
}

View File

@ -8,6 +8,7 @@ import { selectCol } from "app/ui/tables/selectColumn"
import NumberInputCell from "app/ui/tables/inputs/numberInput"
import { formSchema } from "app/ui/forms/story"
import { TextInputCell } from "app/ui/tables/inputs/textInput"
import GenrePickerInputCell from "app/ui/tables/inputs/genrePickerInput"
const columnHelper = createColumnHelper<StoryWithGenres>()
export const columns: ColumnDef<StoryWithGenres>[] = [
@ -50,11 +51,13 @@ export const columns: ColumnDef<StoryWithGenres>[] = [
}
},
columnHelper.accessor("genres", {
cell: props => {
const genres = props.getValue()
return <GenreBadges genres={genres} />
},
filterFn: "arrIncludes"
// cell: props => {
// const genres = props.getValue()
// return <GenreBadges genres={genres} />
// },
cell: GenrePickerInputCell,
filterFn: "arrIncludes",
meta: {}
//TODO - write custom filter function, to account for an array of objects
}),

View File

@ -15,15 +15,15 @@ export type StoryWithGenres = Story & { genres: Array<Genre> }
export default async function Page() {
const genres = await getGenres()
const storiesWithGenres: Array<StoryWithGenres> = await getStoriesWithGenres()
const pubsWithGenres = await getPubsWithGenres()
return (
<div className="container mx-auto">
<DataTable columns={columns} data={storiesWithGenres} tableName="story">
<DataTable columns={columns} data={storiesWithGenres} tableName="story"
genres={genres}
>
<CreateStoryDialog genres={genres} />
{/* TODO - EDIT STORY DIALOG */}
</DataTable>
</div>
)

View File

@ -3,26 +3,9 @@ import { CellContext, ColumnDef, createColumnHelper } from "@tanstack/react-tabl
import { ArrowUpDown } from "lucide-react"
import { Button } from "@/components/ui/button"
import { SubComplete } from "./page"
import { actions } from "app/ui/tables/actions"
import { selectCol } from "app/ui/tables/selectColumn"
import EditSubmissionDialog from "./edit"
const EditSubCell = (props: CellContext<any, any>) => {
// return <EditSubmissionDialog
// stories={props.table.options.meta.stories}
// pubs={props.table.options.meta.pubs}
// responses={props.table.options.meta.responses}
// defaults={props.row.original}
// >{
// props.getValue() instanceof Date ?
// <p className="w-full text-center">
// {props.getValue().toLocaleDateString()}
// </p>
// : <p className="w-full text-left">{props.getValue()}</p>
//
// }</EditSubmissionDialog>
}
export const columns: ColumnDef<SubComplete>[] = [
selectCol,

View File

@ -17,7 +17,7 @@ type CreateSubDefaults = {
respoonseId: number
}
export default function CreateSubmissionDialog({ stories, pubs, responses, defaults }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[], defaults: CreateSubDefaults }) {
export default function CreateSubmissionDialog({ stories, pubs, responses, defaults }: ComponentProps<"div"> & { stories: Story[], pubs: Pub[], responses: Response[], defaults?: CreateSubDefaults }) {
return (
<Dialog>

View File

@ -15,12 +15,14 @@ export default async function Page() {
const stories = await getStories()
const pubs = await getPubs()
const responses = await getResponses()
const genres = await getGenres()
return (
<div className="container">
<DataTable data={subs} columns={columns} tableName="sub"
stories={stories}
pubs={pubs}
responses={responses}
genres={genres}
>
<CreateSubmissionDialog
stories={stories}

View File

@ -862,10 +862,6 @@ body {
height: fit-content;
}
.h-full {
height: 100%;
}
.h-px {
height: 1px;
}

View File

@ -1,30 +0,0 @@
import { FormItem, FormControl, FormLabel } from "@/components/ui/form"
import { Checkbox } from "@/components/ui/checkbox"
export default function GenreCheckbox({ field, item }) {
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>
)
}

View File

@ -4,9 +4,11 @@ import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Checkbox } from "@/components/ui/checkbox"
import { cn } from "@/lib/utils"
import GenreCheckbox from "./genreCheckbox"
import { ComponentProps } from "react"
import { Genre } from "@prisma/client"
import { UseFormReturn } from "react-hook-form"
export default function GenrePicker({ genres, form }) {
export default function GenrePicker({ genres, form }: ComponentProps<"div"> & { genres: Genre[], form: UseFormReturn }) {
return (
<Popover modal={true}>
<FormField

View File

@ -46,8 +46,7 @@ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, Di
import pluralize from "app/lib/pluralize"
import { updateField } from "app/lib/update"
import { tableNameToItemName } from "app/lib/nameMaps"
import { Pub, Response, Story } from "@prisma/client"
import { response } from "express"
import { Genre, Pub, Response, Story } from "@prisma/client"
export interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
@ -62,8 +61,9 @@ export function DataTable<TData, TValue>({
tableName,
stories,
pubs,
responses
}: DataTableProps<TData, TValue> & ComponentProps<"div"> & { tableName: string, stories?: Story[], pubs?: Pub[], responses?: Response[] }) {
responses,
genres
}: DataTableProps<TData, TValue> & ComponentProps<"div"> & { tableName: string, stories?: Story[], pubs?: Pub[], responses?: Response[], genres?: Genre[] }) {
//STATE
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
@ -97,7 +97,8 @@ export function DataTable<TData, TValue>({
pathname,
stories,
pubs,
responses
responses,
genres
}
})

View File

@ -0,0 +1,134 @@
"use client"
import { FormField, FormItem, FormLabel, FormMessage, FormControl, Form } from "@/components/ui/form"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { ComponentProps, useState } from "react"
import { useForm, UseFormReturn } from "react-hook-form"
import { CellContext } from "@tanstack/react-table"
import { z } from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { toast } from "@/components/ui/use-toast"
import GenreBadges from "app/ui/genreBadges"
import { updateField, updateGenres } from "app/lib/update"
export default function GenrePickerInputCell(props: CellContext<any, any>) {
const table = props.table.options.meta.tableName
const pathname = props.table.options.meta.pathname
const id = props.row.original.id
const column = props.column.id
const value = props.cell.getValue()
const genres = props.table.options.meta.genres
const [isActive, setIsActive] = useState(false)
async function onSubmit({ genres }: { genres: number[] }) {
const genresArray = genres.map((e) => { return { id: e } })
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(genresArray, null, 2)}</code>
</pre>
),
})
const res = await updateGenres({
id,
table,
genres: genresArray,
pathname
})
setIsActive(false)
}
function onErrors(errors) {
toast({
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))
}
const formSchema = z.object({
genres: z.array(z.number())
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
genres: value.map(e => e.id)
}
})
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit, onErrors)} id="editGenresForm">
<Popover modal={true}>
<FormField
control={form.control}
name="genres"
render={({ field }) => (
<FormItem className="flex flex-col">
<PopoverTrigger>
{value.length > 0 ? <GenreBadges genres={value} /> : <Button variant="ghost">Add genres</Button>
}
</PopoverTrigger>
<PopoverContent align="start">
{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) => {
console.log(field.value)
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>
)
}}
/>
))}
<Button variant="ghost" form="editGenresForm">Submit</Button>
<Button variant="link" className="p-0" onClick={() => form.setValue("genres", [])}>Clear</Button>
</PopoverContent>
<FormMessage />
</FormItem>
)}
/>
</Popover>
</form>
</Form>
)
}

View File

@ -17,7 +17,6 @@ export default function NumberInputCell(props: CellContext<any, any>) {
const column = props.column.id
const pathname = props.table.options.meta.pathname
const value = props.cell.getValue()
console.log(`|${value}|`)
const formSchema = props.column.columnDef.meta.formSchema.pick({ [column]: true })
const form = useForm<z.infer<typeof formSchema>>({

View File

@ -38,7 +38,7 @@ export function TextInputCell(props: CellContext<any, any>) {
const res = await updateField({
id,
table,
string: value[column],
datum: value[column],
column,
pathname
})
@ -59,7 +59,7 @@ export function TextInputCell(props: CellContext<any, any>) {
return (
<div
onDoubleClick={() => setIsActive(prev => !prev)}
className="w-full h-fit flex items-center justify-center"
className="w-full h-fit flex items-center justify-left"
tabIndex={0}
onKeyDown={e => {
if (e.code === "Enter" && !isActive) {