add skill level selector
This commit is contained in:
		| @@ -13,7 +13,7 @@ const AdminBarLayout = ({ | |||||||
|   return ( |   return ( | ||||||
|     <div className="relative"> |     <div className="relative"> | ||||||
|       {data?.user.role === Role.ADMIN ? ( |       {data?.user.role === Role.ADMIN ? ( | ||||||
|         <div className="flex sticky left-0 right-0 top-[71px] z-10 mx-auto mb-3 mt-[15px] max-w-4xl flex-row justify-between rounded-xl border border-neutral-600 bg-red-300 drop-shadow-xl sm:mb-6"> |         <div className="sticky left-0 right-0 top-[71px] z-10 mx-auto mb-3 mt-[15px] flex max-w-4xl flex-row justify-between rounded-xl border border-neutral-600 bg-red-300 drop-shadow-xl sm:mb-6"> | ||||||
|           <h1 className="rounded-lg px-4 py-2 font-semibold text-black"> |           <h1 className="rounded-lg px-4 py-2 font-semibold text-black"> | ||||||
|             Admin Mode |             Admin Mode | ||||||
|           </h1> |           </h1> | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| import { PaymentType, type AuditoryResource } from "@prisma/client"; | import { PaymentType, type AuditoryResource, SkillLevel } from "@prisma/client"; | ||||||
| import Image from "next/image"; | import Image from "next/image"; | ||||||
| import { PencilSquareIcon } from "@heroicons/react/24/solid"; | import { PencilSquareIcon } from "@heroicons/react/24/solid"; | ||||||
| import { | import { | ||||||
|   MultiSelector, |   MultiSelector, | ||||||
|   MultiSelectorContext, |   MultiSelectorMany, | ||||||
|   MultiSelectorOption, |   MultiSelectorOption, | ||||||
|  |   SelectedManyContext, | ||||||
|  |   SelectedUniqueContext, | ||||||
| } 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"; | ||||||
| @@ -71,8 +73,8 @@ const PaymentTypeOption = ({ | |||||||
| }) => { | }) => { | ||||||
|   return ( |   return ( | ||||||
|     <MultiSelectorOption value={type}> |     <MultiSelectorOption value={type}> | ||||||
|       <MultiSelectorContext.Consumer> |       <SelectedUniqueContext.Consumer> | ||||||
|         {({ selected }) => ( |         {(selected) => ( | ||||||
|           <div |           <div | ||||||
|             className={ |             className={ | ||||||
|               (selected === type ? "bg-stone-800" : "bg-white") + |               (selected === type ? "bg-stone-800" : "bg-white") + | ||||||
| @@ -92,7 +94,43 @@ const PaymentTypeOption = ({ | |||||||
|             </span> |             </span> | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
|       </MultiSelectorContext.Consumer> |       </SelectedUniqueContext.Consumer> | ||||||
|  |     </MultiSelectorOption> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const SkillLevelOption = ({ | ||||||
|  |   type, | ||||||
|  |   label, | ||||||
|  | }: { | ||||||
|  |   type: SkillLevel; | ||||||
|  |   label: string; | ||||||
|  | }) => { | ||||||
|  |   return ( | ||||||
|  |     <MultiSelectorOption value={type}> | ||||||
|  |       <SelectedManyContext.Consumer> | ||||||
|  |         {(selected) => ( | ||||||
|  |           <div | ||||||
|  |             className={ | ||||||
|  |               (selected.includes(type.toString()) | ||||||
|  |                 ? "bg-stone-800" | ||||||
|  |                 : "bg-white") + | ||||||
|  |               " flex flex-row space-x-2 whitespace-nowrap rounded-xl border border-neutral-400 px-2 py-1" | ||||||
|  |             } | ||||||
|  |           > | ||||||
|  |             <span | ||||||
|  |               className={ | ||||||
|  |                 (selected.includes(type.toString()) | ||||||
|  |                   ? "text-white" | ||||||
|  |                   : "text black") + | ||||||
|  |                 " my-auto inline-block whitespace-nowrap text-sm" | ||||||
|  |               } | ||||||
|  |             > | ||||||
|  |               {label} | ||||||
|  |             </span> | ||||||
|  |           </div> | ||||||
|  |         )} | ||||||
|  |       </SelectedManyContext.Consumer> | ||||||
|     </MultiSelectorOption> |     </MultiSelectorOption> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| @@ -129,24 +167,31 @@ const ResourceSummarySubForm = ({ | |||||||
|           </span> |           </span> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div> |       <MultiSelector | ||||||
|         <MultiSelector |         label="Price Category" | ||||||
|           label="Price Category" |         defaultValue={ | ||||||
|           defaultValue={ |           resource?.payment_options.toString() ?? PaymentType.FREE.toString() | ||||||
|             resource?.payment_options.toString() ?? PaymentType.FREE.toString() |         } | ||||||
|           } |       > | ||||||
|         > |         <PaymentTypeOption type={PaymentType.FREE} label="Free" /> | ||||||
|           <PaymentTypeOption type={PaymentType.FREE} label="Free" /> |         <PaymentTypeOption | ||||||
|           <PaymentTypeOption |           type={PaymentType.SUBSCRIPTION_MONTHLY} | ||||||
|             type={PaymentType.SUBSCRIPTION_MONTHLY} |           label="Monthly Subscription" | ||||||
|             label="Monthly Subscription" |         /> | ||||||
|           /> |         <PaymentTypeOption | ||||||
|           <PaymentTypeOption |           type={PaymentType.SUBSCRIPTION_WEEKLY} | ||||||
|             type={PaymentType.SUBSCRIPTION_WEEKLY} |           label="Weekly Subscription" | ||||||
|             label="Weekly Subscription" |         /> | ||||||
|           /> |       </MultiSelector> | ||||||
|         </MultiSelector> |  | ||||||
|       </div> |       <MultiSelectorMany | ||||||
|  |         label="Skill Level" | ||||||
|  |         defaultValues={resource?.skill_levels ?? []} | ||||||
|  |       > | ||||||
|  |         <SkillLevelOption type={SkillLevel.BEGINNER} label="beginner" /> | ||||||
|  |         <SkillLevelOption type={SkillLevel.INTERMEDIATE} label="intermediate" /> | ||||||
|  |         <SkillLevelOption type={SkillLevel.ADVANCED} label="advanced" /> | ||||||
|  |       </MultiSelectorMany> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,12 +1,68 @@ | |||||||
| import { createContext, useContext, useState } from "react"; | import { createContext, useContext, useState } from "react"; | ||||||
|  |  | ||||||
| // Define contexts | // Define contexts | ||||||
| const MultiSelectorContext = createContext({ | const SelectorContext = createContext<{ | ||||||
|   selected: "", |   type: "one" | "many"; | ||||||
|  |   updateCallback: (_value: string) => void; | ||||||
|  | }>({ | ||||||
|  |   type: "one", | ||||||
|   updateCallback: (_value: string) => { |   updateCallback: (_value: string) => { | ||||||
|     return; |     return; | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  | const SelectedUniqueContext = createContext<string>(""); | ||||||
|  | const SelectedManyContext = createContext<string[]>([]); | ||||||
|  |  | ||||||
|  | function MultiSelectorMany<T extends { toString: () => string }>({ | ||||||
|  |   label, | ||||||
|  |   defaultValues, | ||||||
|  |   children, | ||||||
|  | }: { | ||||||
|  |   label: string; | ||||||
|  |   defaultValues: T[]; | ||||||
|  |   children: undefined | JSX.Element | JSX.Element[]; | ||||||
|  | }) { | ||||||
|  |   const [selected, setSelected] = useState<string[]>( | ||||||
|  |     defaultValues.map((value) => { | ||||||
|  |       return value.toString(); | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   const updateCallback = (value: string) => { | ||||||
|  |     if (selected.includes(value)) { | ||||||
|  |       setSelected( | ||||||
|  |         selected.filter((selectedValue) => { | ||||||
|  |           return selectedValue !== value; | ||||||
|  |         }) | ||||||
|  |       ); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     setSelected([value, ...selected]); | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <SelectorContext.Provider value={{ type: "many", updateCallback }}> | ||||||
|  |       <SelectedManyContext.Provider value={selected}> | ||||||
|  |         <div className="flex flex-col"> | ||||||
|  |           <label className="text-md block font-semibold">{label}</label> | ||||||
|  |           <span className="block text-sm italic text-neutral-400"> | ||||||
|  |             Select all that apply | ||||||
|  |           </span> | ||||||
|  |           <input | ||||||
|  |             readOnly | ||||||
|  |             type="text" | ||||||
|  |             className="hidden" | ||||||
|  |             value={selected ?? ""} | ||||||
|  |           /> | ||||||
|  |           <div className="flex mt-2 flex-row space-x-2 overflow-x-auto"> | ||||||
|  |             {children} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </SelectedManyContext.Provider> | ||||||
|  |     </SelectorContext.Provider> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
| function MultiSelector<T extends { toString: () => string }>({ | function MultiSelector<T extends { toString: () => string }>({ | ||||||
|   label, |   label, | ||||||
| @@ -20,20 +76,27 @@ function MultiSelector<T extends { toString: () => string }>({ | |||||||
|   const [selected, setSelected] = useState<string>(defaultValue.toString()); |   const [selected, setSelected] = useState<string>(defaultValue.toString()); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <MultiSelectorContext.Provider |     <SelectorContext.Provider | ||||||
|       value={{ selected, updateCallback: setSelected }} |       value={{ type: "many", updateCallback: setSelected }} | ||||||
|     > |     > | ||||||
|       <div className="flex flex-col"> |       <SelectedUniqueContext.Provider value={selected}> | ||||||
|         <label className="text-md block font-semibold">{label}</label> |         <div className="flex flex-col"> | ||||||
|         <span className="block text-sm italic text-neutral-400"> |           <label className="text-md block font-semibold">{label}</label> | ||||||
|           Select one from below |           <span className="block text-sm italic text-neutral-400"> | ||||||
|         </span> |             Select one from below | ||||||
|         <input readOnly type="text" className="hidden" value={selected ?? ""} /> |           </span> | ||||||
|         <div className="mt-2 flex flex-row space-x-2 overflow-x-auto"> |           <input | ||||||
|           {children} |             readOnly | ||||||
|  |             type="text" | ||||||
|  |             className="hidden" | ||||||
|  |             value={selected ?? ""} | ||||||
|  |           /> | ||||||
|  |           <div className="flex mt-2 flex-row space-x-2 overflow-x-auto"> | ||||||
|  |             {children} | ||||||
|  |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </SelectedUniqueContext.Provider> | ||||||
|     </MultiSelectorContext.Provider> |     </SelectorContext.Provider> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -44,7 +107,7 @@ function MultiSelectorOption<T extends { toString: () => string }>({ | |||||||
|   value: T; |   value: T; | ||||||
|   children: undefined | JSX.Element | JSX.Element[]; |   children: undefined | JSX.Element | JSX.Element[]; | ||||||
| }) { | }) { | ||||||
|   const { updateCallback } = useContext(MultiSelectorContext); |   const { updateCallback } = useContext(SelectorContext); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <button |     <button | ||||||
| @@ -58,4 +121,11 @@ function MultiSelectorOption<T extends { toString: () => string }>({ | |||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
| export { MultiSelectorContext, MultiSelector, MultiSelectorOption }; | export { | ||||||
|  |   SelectedUniqueContext, | ||||||
|  |   SelectorContext, | ||||||
|  |   SelectedManyContext, | ||||||
|  |   MultiSelectorMany, | ||||||
|  |   MultiSelector, | ||||||
|  |   MultiSelectorOption, | ||||||
|  | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Brandon Egger
					Brandon Egger