update resource table with details
This commit is contained in:
		| @@ -3,16 +3,19 @@ import Image from "next/image"; | |||||||
| import { PencilSquareIcon } from "@heroicons/react/24/solid"; | import { PencilSquareIcon } from "@heroicons/react/24/solid"; | ||||||
| import { ChevronDownIcon } from "@heroicons/react/24/outline"; | import { ChevronDownIcon } from "@heroicons/react/24/outline"; | ||||||
| import { | import { | ||||||
|   MultiSelector, |  | ||||||
|   MultiSelectorMany, |   MultiSelectorMany, | ||||||
|   MultiSelectorOption, |   MultiSelectorOption, | ||||||
|   SelectedUniqueContext, |   SelectedManyContext, | ||||||
|   SimpleSelectorManyOption, |   SimpleSelectorManyOption, | ||||||
| } from "../../forms/selectors"; | } from "../../forms/selectors"; | ||||||
| import { InfoInputLine } from "~/components/forms/textInput"; | import { InfoInputLine } from "~/components/forms/textInput"; | ||||||
| import { PriceIcon } from "~/prices/Icons"; | import { PriceIcon } from "~/prices/Icons"; | ||||||
| import { useState } from "react"; | import { useState } from "react"; | ||||||
| import { type UseFormRegister } from "react-hook-form"; | import { | ||||||
|  |   type UseFormReturn, | ||||||
|  |   FormProvider, | ||||||
|  |   useFormContext, | ||||||
|  | } from "react-hook-form"; | ||||||
| import { type RouterInputs } from "~/utils/api"; | import { type RouterInputs } from "~/utils/api"; | ||||||
|  |  | ||||||
| export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"]; | export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"]; | ||||||
| @@ -79,11 +82,11 @@ const PaymentTypeOption = ({ | |||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|     <MultiSelectorOption value={type}> |     <MultiSelectorOption value={type}> | ||||||
|       <SelectedUniqueContext.Consumer> |       <SelectedManyContext.Consumer> | ||||||
|         {(selected) => ( |         {(selected) => ( | ||||||
|           <div |           <div | ||||||
|             className={ |             className={ | ||||||
|               (selected === type ? "bg-stone-800" : "bg-white") + |               (selected.includes(type) ? "bg-stone-800" : "bg-white") + | ||||||
|               " flex flex-row space-x-2 whitespace-nowrap rounded-xl border border-neutral-400 py-[1px] pl-[1px] pr-2" |               " flex flex-row space-x-2 whitespace-nowrap rounded-xl border border-neutral-400 py-[1px] pl-[1px] pr-2" | ||||||
|             } |             } | ||||||
|           > |           > | ||||||
| @@ -92,7 +95,7 @@ const PaymentTypeOption = ({ | |||||||
|             </span> |             </span> | ||||||
|             <span |             <span | ||||||
|               className={ |               className={ | ||||||
|                 (selected === type ? "text-white" : "text black") + |                 (selected.includes(type) ? "text-white" : "text black") + | ||||||
|                 " my-auto inline-block whitespace-nowrap text-sm" |                 " my-auto inline-block whitespace-nowrap text-sm" | ||||||
|               } |               } | ||||||
|             > |             > | ||||||
| @@ -100,7 +103,7 @@ const PaymentTypeOption = ({ | |||||||
|             </span> |             </span> | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
|       </SelectedUniqueContext.Consumer> |       </SelectedManyContext.Consumer> | ||||||
|     </MultiSelectorOption> |     </MultiSelectorOption> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| @@ -109,12 +112,12 @@ const PaymentTypeOption = ({ | |||||||
|  * Resource summary inputs - ie description, manufacturer, etc. |  * Resource summary inputs - ie description, manufacturer, etc. | ||||||
|  */ |  */ | ||||||
| function ResourceSummarySubForm({ | function ResourceSummarySubForm({ | ||||||
|   register, |  | ||||||
|   resource, |   resource, | ||||||
| }: { | }: { | ||||||
|   register: UseFormRegister<ResourceUpdateInput>; |  | ||||||
|   resource?: ResourceUpdateInput; |   resource?: ResourceUpdateInput; | ||||||
| }) { | }) { | ||||||
|  |   const { register } = useFormContext<ResourceUpdateInput>(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="space-y-4 px-4"> |     <div className="space-y-4 px-4"> | ||||||
|       <div className="flex flex-row space-x-4 sm:mt-4"> |       <div className="flex flex-row space-x-4 sm:mt-4"> | ||||||
| @@ -144,10 +147,11 @@ function ResourceSummarySubForm({ | |||||||
|           </span> |           </span> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <MultiSelector |       <MultiSelectorMany | ||||||
|  |         details={register("payment_options", { required: true })} | ||||||
|         label="Price Category" |         label="Price Category" | ||||||
|         defaultValue={ |         defaultValues={ | ||||||
|           resource?.payment_options?.toString() ?? PaymentType.FREE.toString() |           resource?.payment_options ?? [PaymentType.FREE.toString()] | ||||||
|         } |         } | ||||||
|       > |       > | ||||||
|         <PaymentTypeOption type={PaymentType.FREE} label="Free" /> |         <PaymentTypeOption type={PaymentType.FREE} label="Free" /> | ||||||
| @@ -159,9 +163,10 @@ function ResourceSummarySubForm({ | |||||||
|           type={PaymentType.SUBSCRIPTION_WEEKLY} |           type={PaymentType.SUBSCRIPTION_WEEKLY} | ||||||
|           label="Weekly Subscription" |           label="Weekly Subscription" | ||||||
|         /> |         /> | ||||||
|       </MultiSelector> |       </MultiSelectorMany> | ||||||
|  |  | ||||||
|       <MultiSelectorMany |       <MultiSelectorMany | ||||||
|  |         details={register("skill_levels", { required: true })} | ||||||
|         label="Skill Level" |         label="Skill Level" | ||||||
|         defaultValues={resource?.skill_levels ?? []} |         defaultValues={resource?.skill_levels ?? []} | ||||||
|       > |       > | ||||||
| @@ -177,6 +182,7 @@ function ResourceSummarySubForm({ | |||||||
|       </MultiSelectorMany> |       </MultiSelectorMany> | ||||||
|  |  | ||||||
|       <MultiSelectorMany |       <MultiSelectorMany | ||||||
|  |         details={register("skills", { required: true })} | ||||||
|         label="Skills Covered" |         label="Skills Covered" | ||||||
|         defaultValues={resource?.skills ?? []} |         defaultValues={resource?.skills ?? []} | ||||||
|       > |       > | ||||||
| @@ -194,14 +200,9 @@ function ResourceSummarySubForm({ | |||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| const ResourceDescriptionSubForm = ({ | const ResourceDescriptionSubForm = () => { | ||||||
|   register, |  | ||||||
|   resource, |  | ||||||
| }: { |  | ||||||
|   register: UseFormRegister<ResourceUpdateInput>; |  | ||||||
|   resource?: ResourceUpdateInput; |  | ||||||
| }) => { |  | ||||||
|   const [dropdownOpen, toggleDropdown] = useState(false); |   const [dropdownOpen, toggleDropdown] = useState(false); | ||||||
|  |   const { register } = useFormContext(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <div className="mx-4"> |     <div className="mx-4"> | ||||||
| @@ -241,29 +242,31 @@ const ResourceDescriptionSubForm = ({ | |||||||
| }; | }; | ||||||
|  |  | ||||||
| const ResourceForm = ({ | const ResourceForm = ({ | ||||||
|  |   methods, | ||||||
|   resource, |   resource, | ||||||
|   register, |  | ||||||
|   error, |   error, | ||||||
| }: { | }: { | ||||||
|   resource?: ResourceUpdateInput; |   resource?: ResourceUpdateInput; | ||||||
|   register: UseFormRegister<ResourceUpdateInput>; |   methods: UseFormReturn<ResourceUpdateInput>; | ||||||
|   error?: string; |   error?: string; | ||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|     <form className="mx-auto flex max-w-2xl flex-col flex-col-reverse py-1 sm:flex-row sm:divide-x sm:py-4"> |     <FormProvider {...methods}> | ||||||
|       <div className="my-5 mr-4 flex flex-col text-lg font-bold"> |       <form className="mx-auto flex max-w-2xl flex-col flex-col-reverse py-1 sm:flex-row sm:divide-x sm:py-4"> | ||||||
|         <ResourceLinkSubForm /> {/** //resource={resource} /> */} |         <div className="my-5 mr-4 flex flex-col text-lg font-bold"> | ||||||
|       </div> |           <ResourceLinkSubForm /> {/** //resource={resource} /> */} | ||||||
|       <div> |  | ||||||
|         <h1 className="mx-4 mb-2 border-b border-neutral-400 text-xl font-bold sm:hidden"> |  | ||||||
|           General |  | ||||||
|         </h1> |  | ||||||
|         <div className="justify-left mx-auto flex max-w-lg flex-col space-y-4 pb-5"> |  | ||||||
|           <ResourceSummarySubForm register={register} resource={resource} /> |  | ||||||
|           <ResourceDescriptionSubForm register={register} resource={resource} /> |  | ||||||
|         </div> |         </div> | ||||||
|       </div> |         <div> | ||||||
|     </form> |           <h1 className="mx-4 mb-2 border-b border-neutral-400 text-xl font-bold sm:hidden"> | ||||||
|  |             General | ||||||
|  |           </h1> | ||||||
|  |           <div className="justify-left mx-auto flex max-w-lg flex-col space-y-4 pb-5"> | ||||||
|  |             <ResourceSummarySubForm resource={resource} /> | ||||||
|  |             <ResourceDescriptionSubForm /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </form> | ||||||
|  |     </FormProvider> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { createContext, useContext, useState } from "react"; | import { createContext, useContext, useState } from "react"; | ||||||
|  | import { useFormContext, type UseFormRegisterReturn } from "react-hook-form"; | ||||||
|  |  | ||||||
| // generics | // generics | ||||||
| interface ToStringable { | interface ToStringable { | ||||||
| @@ -22,11 +23,14 @@ function MultiSelectorMany<T extends ToStringable>({ | |||||||
|   label, |   label, | ||||||
|   defaultValues, |   defaultValues, | ||||||
|   children, |   children, | ||||||
|  |   details, | ||||||
| }: { | }: { | ||||||
|   label: string; |   label: string; | ||||||
|   defaultValues: T[]; |   defaultValues: T[]; | ||||||
|   children: undefined | JSX.Element | JSX.Element[]; |   children: undefined | JSX.Element | JSX.Element[]; | ||||||
|  |   details: UseFormRegisterReturn<string>; | ||||||
| }) { | }) { | ||||||
|  |   const { setValue } = useFormContext(); | ||||||
|   const [selected, setSelected] = useState<string[]>( |   const [selected, setSelected] = useState<string[]>( | ||||||
|     defaultValues.map((value) => { |     defaultValues.map((value) => { | ||||||
|       return value.toString(); |       return value.toString(); | ||||||
| @@ -35,14 +39,16 @@ function MultiSelectorMany<T extends ToStringable>({ | |||||||
|  |  | ||||||
|   const updateCallback = (value: string) => { |   const updateCallback = (value: string) => { | ||||||
|     if (selected.includes(value)) { |     if (selected.includes(value)) { | ||||||
|       setSelected( |       const filteredSelected = selected.filter((selectedValue) => { | ||||||
|         selected.filter((selectedValue) => { |         return selectedValue !== value; | ||||||
|           return selectedValue !== value; |       }); | ||||||
|         }) |  | ||||||
|       ); |       setValue(details.name, filteredSelected); | ||||||
|  |       setSelected(filteredSelected); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     setValue(details.name, [value, ...selected]); | ||||||
|     setSelected([value, ...selected]); |     setSelected([value, ...selected]); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
| @@ -54,12 +60,7 @@ function MultiSelectorMany<T extends ToStringable>({ | |||||||
|           <span className="block text-sm italic text-neutral-400"> |           <span className="block text-sm italic text-neutral-400"> | ||||||
|             Select all that apply |             Select all that apply | ||||||
|           </span> |           </span> | ||||||
|           <input |           <input {...details} readOnly type="text" className="hidden" /> | ||||||
|             readOnly |  | ||||||
|             type="text" |  | ||||||
|             className="hidden" |  | ||||||
|             value={selected ?? ""} |  | ||||||
|           /> |  | ||||||
|           <div className="mt-2 space-x-2 space-y-2 overflow-x-auto"> |           <div className="mt-2 space-x-2 space-y-2 overflow-x-auto"> | ||||||
|             {children} |             {children} | ||||||
|           </div> |           </div> | ||||||
| @@ -73,16 +74,25 @@ function MultiSelector<T extends ToStringable>({ | |||||||
|   label, |   label, | ||||||
|   defaultValue, |   defaultValue, | ||||||
|   children, |   children, | ||||||
|  |   details, | ||||||
| }: { | }: { | ||||||
|   label: string; |   label: string; | ||||||
|   defaultValue: T; |   defaultValue: T; | ||||||
|   children: undefined | JSX.Element | JSX.Element[]; |   children: undefined | JSX.Element | JSX.Element[]; | ||||||
|  |   details: UseFormRegisterReturn<string>; | ||||||
| }) { | }) { | ||||||
|   const [selected, setSelected] = useState<string>(defaultValue.toString()); |   const [selected, setSelected] = useState<string>(defaultValue.toString()); | ||||||
|  |   const { setValue } = useFormContext(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <SelectorContext.Provider |     <SelectorContext.Provider | ||||||
|       value={{ type: "one", updateCallback: setSelected }} |       value={{ | ||||||
|  |         type: "one", | ||||||
|  |         updateCallback: (value) => { | ||||||
|  |           setSelected(value); | ||||||
|  |           setValue(details.name, value); | ||||||
|  |         }, | ||||||
|  |       }} | ||||||
|     > |     > | ||||||
|       <SelectedUniqueContext.Provider value={selected}> |       <SelectedUniqueContext.Provider value={selected}> | ||||||
|         <div className="flex flex-col"> |         <div className="flex flex-col"> | ||||||
| @@ -90,12 +100,7 @@ function MultiSelector<T extends ToStringable>({ | |||||||
|           <span className="block text-sm italic text-neutral-400"> |           <span className="block text-sm italic text-neutral-400"> | ||||||
|             Select one from below |             Select one from below | ||||||
|           </span> |           </span> | ||||||
|           <input |           <input {...details} readOnly type="text" className="hidden" /> | ||||||
|             readOnly |  | ||||||
|             type="text" |  | ||||||
|             className="hidden" |  | ||||||
|             value={selected ?? ""} |  | ||||||
|           /> |  | ||||||
|           <div className="space-x-2 space-y-2 overflow-x-auto">{children}</div> |           <div className="space-x-2 space-y-2 overflow-x-auto">{children}</div> | ||||||
|         </div> |         </div> | ||||||
|       </SelectedUniqueContext.Provider> |       </SelectedUniqueContext.Provider> | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ const EditResourcePage = ( | |||||||
| ) => { | ) => { | ||||||
|   const { resource } = props; |   const { resource } = props; | ||||||
|   const [serverError, setServerError] = useState<string | undefined>(undefined); |   const [serverError, setServerError] = useState<string | undefined>(undefined); | ||||||
|   const { register, getValues } = useForm<ResourceUpdateInput>({ |   const formMethods = useForm<ResourceUpdateInput>({ | ||||||
|     defaultValues: resource as ResourceUpdateInput, |     defaultValues: resource as ResourceUpdateInput, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
| @@ -90,7 +90,7 @@ const EditResourcePage = ( | |||||||
|             } |             } | ||||||
|             label="Save" |             label="Save" | ||||||
|             onClick={() => { |             onClick={() => { | ||||||
|               onSubmit(getValues()); |               onSubmit(formMethods.getValues()); | ||||||
|             }} |             }} | ||||||
|           />, |           />, | ||||||
|           <AdminActionLink |           <AdminActionLink | ||||||
| @@ -103,7 +103,7 @@ const EditResourcePage = ( | |||||||
|       > |       > | ||||||
|         <main className="mb-12"> |         <main className="mb-12"> | ||||||
|           <ResourceForm |           <ResourceForm | ||||||
|             register={register} |             methods={formMethods} | ||||||
|             error={serverError} |             error={serverError} | ||||||
|             resource={resource as ResourceUpdateInput} |             resource={resource as ResourceUpdateInput} | ||||||
|           /> |           /> | ||||||
|   | |||||||
| @@ -34,14 +34,14 @@ export const auditoryResourceRouter = createTRPCRouter({ | |||||||
|     .input( |     .input( | ||||||
|       z.object({ |       z.object({ | ||||||
|         id: z.string(), |         id: z.string(), | ||||||
|         icon: z.string().optional(), |         icon: z.string().min(1).optional(), | ||||||
|         name: z.string().optional(), |         name: z.string().min(1).optional(), | ||||||
|         description: z.string().optional(), |         description: z.string().min(1).optional(), | ||||||
|         manufacturer: z |         manufacturer: z | ||||||
|           .object({ |           .object({ | ||||||
|             name: z.string(), |             name: z.string().min(1), | ||||||
|             required: z.boolean(), |             required: z.boolean(), | ||||||
|             notice: z.string().optional().nullable(), |             notice: z.string().min(1).optional().nullable(), | ||||||
|           }) |           }) | ||||||
|           .optional(), |           .optional(), | ||||||
|         ages: z |         ages: z | ||||||
| @@ -52,7 +52,10 @@ export const auditoryResourceRouter = createTRPCRouter({ | |||||||
|         payment_options: z.array(z.nativeEnum(PaymentType)).optional(), |         payment_options: z.array(z.nativeEnum(PaymentType)).optional(), | ||||||
|         platform_links: z |         platform_links: z | ||||||
|           .array( |           .array( | ||||||
|             z.object({ platform: z.nativeEnum(Platform), link: z.string() }) |             z.object({ | ||||||
|  |               platform: z.nativeEnum(Platform), | ||||||
|  |               link: z.string().min(1), | ||||||
|  |             }) | ||||||
|           ) |           ) | ||||||
|           .optional(), |           .optional(), | ||||||
|       }) |       }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Brandon Egger
					Brandon Egger