add skill level selector
This commit is contained in:
parent
8810e03894
commit
d4cd046c4b
@ -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,
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user