implement number input data validation (basic functionality)
This commit is contained in:
		
							parent
							
								
									773633d103
								
							
						
					
					
						commit
						23058ed48b
					
				
							
								
								
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								prisma/dev.db
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -8,7 +8,7 @@ import { actions } from "app/ui/tables/actions" | ||||||
| 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 { formSchema } from "app/ui/forms/story" | ||||||
| 
 | 
 | ||||||
| const columnHelper = createColumnHelper<StoryWithGenres>() | const columnHelper = createColumnHelper<StoryWithGenres>() | ||||||
| 
 | 
 | ||||||
|  | @ -27,7 +27,8 @@ export const columns: ColumnDef<StoryWithGenres>[] = [ | ||||||
|         </Button> |         </Button> | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|     cell: TextInputCell |     cell: TextInputCell, | ||||||
|  |     meta: { formSchema } | ||||||
| 
 | 
 | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|  | @ -46,7 +47,8 @@ export const columns: ColumnDef<StoryWithGenres>[] = [ | ||||||
|     enableColumnFilter: false, |     enableColumnFilter: false, | ||||||
|     cell: NumberInputCell, |     cell: NumberInputCell, | ||||||
|     meta: { |     meta: { | ||||||
|       step: 50 |       step: 50, | ||||||
|  |       formSchema | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   columnHelper.accessor("genres", { |   columnHelper.accessor("genres", { | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| import { z } from "zod" | import { z } from "zod" | ||||||
| import { zodResolver } from "@hookform/resolvers/zod" | import { zodResolver } from "@hookform/resolvers/zod" | ||||||
| import { useForm } from "react-hook-form" | import { useForm } from "react-hook-form" | ||||||
| import { Button } from "@/components/ui/button" |  | ||||||
| import { | import { | ||||||
| 	Form, | 	Form, | ||||||
| 	FormControl, | 	FormControl, | ||||||
|  | @ -16,18 +15,12 @@ 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 { |  | ||||||
| 	Popover, |  | ||||||
| 	PopoverContent, |  | ||||||
| } from "@/components/ui/popover" |  | ||||||
| import { ComponentProps } 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 { usePathname } from "next/navigation" |  | ||||||
| import GenrePicker from "./genrePicker" | import GenrePicker from "./genrePicker" | ||||||
| import { StoryWithGenres } from "app/story/page" |  | ||||||
| 
 | 
 | ||||||
| const formSchema = z.object({ | export const formSchema = z.object({ | ||||||
| 	id: z.number().optional(), | 	id: z.number().optional(), | ||||||
| 	title: z.string().min(2).max(50), | 	title: z.string().min(2).max(50), | ||||||
| 	word_count: z.coerce.number().min(100), | 	word_count: z.coerce.number().min(100), | ||||||
|  | @ -106,7 +99,7 @@ export default function StoryForm({ genres, createStory, className, existingData | ||||||
| 								<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} min={0} {...field}></Input> | 										<Input className=" w-24" type="number" step={500} min={1} {...field}></Input> | ||||||
| 									</FormControl> | 									</FormControl> | ||||||
| 									<FormMessage /> | 									<FormMessage /> | ||||||
| 								</FormItem> | 								</FormItem> | ||||||
|  |  | ||||||
|  | @ -253,7 +253,6 @@ export function DataTable<TData, TValue>({ | ||||||
|           size="sm" |           size="sm" | ||||||
|           onClick={() => table.previousPage()} |           onClick={() => table.previousPage()} | ||||||
|           disabled={!table.getCanPreviousPage()} |           disabled={!table.getCanPreviousPage()} | ||||||
|           onBlur={true} |  | ||||||
|         > |         > | ||||||
|           Previous |           Previous | ||||||
|         </Button> |         </Button> | ||||||
|  |  | ||||||
|  | @ -1,52 +1,91 @@ | ||||||
|  | "use client" | ||||||
| import { Input } from "@/components/ui/input"; | import { Input } from "@/components/ui/input"; | ||||||
| import { CellContext } from "@tanstack/react-table"; | import { CellContext } from "@tanstack/react-table"; | ||||||
| import { updateField } from "app/lib/update"; | import { updateField } from "app/lib/update"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import { TableInputContainer } from "./inputContainer"; | import { TableInputContainer } from "./inputContainer"; | ||||||
| 
 | import { z } from "zod"; | ||||||
|  | import { useForm } from "react-hook-form"; | ||||||
|  | import { zodResolver } from "@hookform/resolvers/zod"; | ||||||
|  | import { toast } from "@/components/ui/use-toast"; | ||||||
|  | import { Form, FormControl, FormField, FormItem } from "@/components/ui/form"; | ||||||
|  | import { Button } from "@/components/ui/button"; | ||||||
| 
 | 
 | ||||||
| export default function NumberInputCell(props: CellContext<any, any>) { | export default function NumberInputCell(props: CellContext<any, any>) { | ||||||
|   let initialValue = props.getValue() |  | ||||||
|   const [value, setValue] = useState(initialValue) |  | ||||||
|   const [isActive, setIsActive] = useState(false) |   const [isActive, setIsActive] = useState(false) | ||||||
| 
 | 
 | ||||||
|   const table = props.table.options.meta.tableName |   const table = props.table.options.meta.tableName | ||||||
|   const id = props.row.original.id |   const id = props.row.original.id | ||||||
|   const column = props.column.id |   const column = props.column.id | ||||||
|   const pathname = props.table.options.meta.pathname |   const pathname = props.table.options.meta.pathname | ||||||
|  |   let formSchema = props.column.columnDef.meta.formSchema | ||||||
| 
 | 
 | ||||||
|   function handleConfirm() { | 
 | ||||||
|     if (value === initialValue) { |   formSchema = formSchema.pick({ [column]: true }) | ||||||
|       setIsActive(false) | 
 | ||||||
|       return |   const form = useForm<z.infer<typeof formSchema>>({ | ||||||
|  |     resolver: zodResolver(formSchema), | ||||||
|  |     defaultValues: { | ||||||
|  |       [column]: props.cell.getValue() | ||||||
|     } |     } | ||||||
|     updateField({ |   }) | ||||||
|       id, | 
 | ||||||
|       table, | 
 | ||||||
|       number: Number(value), |   function onSubmit(value: z.infer<typeof formSchema>) { | ||||||
|       column, |     toast({ | ||||||
|       pathname |       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(value, null, 2)}</code> | ||||||
|  |         </pre> | ||||||
|  |       ), | ||||||
|     }) |     }) | ||||||
|     initialValue = value |     // updateField({
 | ||||||
|     setIsActive(false) |     //   id,
 | ||||||
|   } |     //   table,
 | ||||||
|   function handleExit() { |     //   number: value[column],
 | ||||||
|     setValue(initialValue) |     //   column,
 | ||||||
|     setIsActive(false) |     //   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)) | ||||||
|  |   } | ||||||
|   return ( |   return ( | ||||||
|     <TableInputContainer isActive={isActive} setIsActive={setIsActive}> |     <TableInputContainer isActive={isActive} setIsActive={setIsActive}> | ||||||
|       {isActive ? |       {isActive ? | ||||||
|         <Input type="number" |         <Form {...form}> | ||||||
|           onBlur={handleExit} |           <form onSubmit={form.handleSubmit(onSubmit, onErrors)}> | ||||||
|           value={value} |             <FormField | ||||||
|           autoFocus={true} |               control={form.control} | ||||||
|           onChange={e => setValue(e.target.value)}         // onBlur={handleClose}
 |               name={column} | ||||||
|           onKeyDown={e => { if (e.code === "Enter") { handleConfirm() } }} |               render={({ field }) => ( | ||||||
|           step={props.column.columnDef.meta.step} |                 <FormItem> | ||||||
|         /> |                   <FormControl> | ||||||
|         : <p>{value}</p> |                     <Input | ||||||
|  |                       type="number" | ||||||
|  |                       autoFocus={true} | ||||||
|  |                       step={props.column.columnDef.meta.step} | ||||||
|  |                       onKeyDown={e => { if (e.code === "Enter") { form.handleSubmit(onSubmit, onErrors) } }} | ||||||
|  |                       {...field} | ||||||
|  |                     /> | ||||||
|  |                   </FormControl> | ||||||
|  |                 </FormItem> | ||||||
|  |               )} | ||||||
|  |             /> | ||||||
|  |           </form> | ||||||
|  |         </Form> | ||||||
|  |         : <p>{props.cell.getValue()}</p> | ||||||
|       } |       } | ||||||
|     </TableInputContainer> |     </TableInputContainer> | ||||||
|   ) |   ) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue