304 lines
9.3 KiB
TypeScript
304 lines
9.3 KiB
TypeScript
import {
|
|
PaymentType,
|
|
SkillLevel,
|
|
Skill,
|
|
type PlatformLink,
|
|
} from "@prisma/client";
|
|
import Image from "next/image";
|
|
import { PencilSquareIcon } from "@heroicons/react/24/solid";
|
|
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
|
import { PlusIcon } from "@heroicons/react/20/solid";
|
|
import {
|
|
MultiSelectorMany,
|
|
MultiSelectorOption,
|
|
SelectedManyContext,
|
|
SimpleSelectorManyOption,
|
|
} from "../../forms/selectors";
|
|
import { InfoInputLine } from "~/components/forms/textInput";
|
|
import { PriceIcon } from "~/prices/Icons";
|
|
import { useState } from "react";
|
|
import {
|
|
type UseFormReturn,
|
|
FormProvider,
|
|
useFormContext,
|
|
} from "react-hook-form";
|
|
import { type RouterInputs } from "~/utils/api";
|
|
import { PlatformLinkButton } from "~/pages/resources/[id]";
|
|
|
|
export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"];
|
|
|
|
/**
|
|
* Renders the image selector for resource form.
|
|
*
|
|
* File needs to be path relative to resource_logos/
|
|
*/
|
|
const SelectImageInput = ({ file }: { file?: string }) => {
|
|
return (
|
|
<>
|
|
<label
|
|
htmlFor="resource-image-file"
|
|
className="bg-whit group relative cursor-pointer overflow-hidden rounded-xl border border-neutral-400 drop-shadow-lg"
|
|
>
|
|
<Image
|
|
className="w-fulle"
|
|
src={`/resource_logos/${file ?? ""}`}
|
|
alt={`resource logo`}
|
|
width={512}
|
|
height={512}
|
|
/>
|
|
<div className="absolute bottom-0 left-0 right-0 top-0 hidden place-items-center group-hover:grid group-hover:bg-white/70">
|
|
<PencilSquareIcon className="w-16 text-black/50" />
|
|
</div>
|
|
</label>
|
|
<input
|
|
accept="image/*"
|
|
id="resource-image-file"
|
|
type="file"
|
|
className="hidden"
|
|
></input>
|
|
</>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Contains the input fields for editing the links for a resource
|
|
* @returns
|
|
*/
|
|
const ResourceLinkSubForm = ({ links }: { links: PlatformLink[] }) => {
|
|
const { register } = useFormContext<ResourceUpdateInput>();
|
|
const [selectedLinks, setSelectedLinks] = useState(links);
|
|
|
|
return (
|
|
<div className="mx-4">
|
|
<div className="mb-2 flex flex-row justify-between space-x-2 border-b border-neutral-400">
|
|
<h1 className="text-xl">Links</h1>
|
|
<button
|
|
type="button"
|
|
className="h-6 rounded-full border border-neutral-900 bg-yellow-400 px-2 leading-tight hover:bg-yellow-200"
|
|
>
|
|
<span className="my-auto inline-block align-middle text-sm font-normal text-neutral-700">
|
|
Add
|
|
</span>
|
|
<PlusIcon className="my-auto inline-block w-4 align-middle" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mx-auto flex w-48 flex-col space-y-2">
|
|
{selectedLinks.map((link, index) => {
|
|
return (
|
|
<section key={index} className="flex flex-row">
|
|
<PlatformLinkButton platformLink={link} />
|
|
</section>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const PaymentTypeOption = ({
|
|
type,
|
|
label,
|
|
}: {
|
|
type: PaymentType;
|
|
label: string;
|
|
}) => {
|
|
return (
|
|
<MultiSelectorOption value={type}>
|
|
<SelectedManyContext.Consumer>
|
|
{(selected) => (
|
|
<div
|
|
className={
|
|
(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"
|
|
}
|
|
>
|
|
<span className="rounded-[10px] bg-white p-1">
|
|
<PriceIcon type={type} />
|
|
</span>
|
|
<span
|
|
className={
|
|
(selected.includes(type) ? "text-white" : "text black") +
|
|
" my-auto inline-block whitespace-nowrap text-sm"
|
|
}
|
|
>
|
|
{label}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</SelectedManyContext.Consumer>
|
|
</MultiSelectorOption>
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Resource summary inputs - ie description, manufacturer, etc.
|
|
*/
|
|
function ResourceSummarySubForm({
|
|
resource,
|
|
}: {
|
|
resource?: ResourceUpdateInput;
|
|
}) {
|
|
const { register } = useFormContext<ResourceUpdateInput>();
|
|
|
|
return (
|
|
<div className="space-y-4 px-4">
|
|
<div className="flex flex-row space-x-4 sm:mt-4">
|
|
<div className="flex w-20 flex-col justify-center space-y-2 sm:w-28">
|
|
<SelectImageInput file={resource?.icon} />
|
|
</div>
|
|
<div className="flex flex-col justify-center overflow-hidden rounded-xl border border-neutral-400 bg-white drop-shadow-lg sm:w-[300px] md:w-[400px]">
|
|
<h2 className="border-b border-neutral-300 px-2 text-center font-semibold">
|
|
Resource Details
|
|
</h2>
|
|
<span className="text-md">
|
|
<InfoInputLine
|
|
details={register("manufacturer.name", { required: true })}
|
|
placeholder="manufacturer"
|
|
value={resource?.manufacturer?.name ?? ""}
|
|
hint="manufacturer"
|
|
/>
|
|
</span>
|
|
<InfoInputLine
|
|
details={register("name", { required: true })}
|
|
placeholder="name"
|
|
value={resource?.name ?? ""}
|
|
hint="name"
|
|
/>
|
|
<span className="my-1 block w-full text-center text-xs italic text-neutral-400">
|
|
Edit the fields above
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<MultiSelectorMany
|
|
details={register("payment_options", { required: true })}
|
|
label="Price Category"
|
|
defaultValues={
|
|
resource?.payment_options ?? [PaymentType.FREE.toString()]
|
|
}
|
|
>
|
|
<PaymentTypeOption type={PaymentType.FREE} label="Free" />
|
|
<PaymentTypeOption
|
|
type={PaymentType.SUBSCRIPTION_MONTHLY}
|
|
label="Monthly Subscription"
|
|
/>
|
|
<PaymentTypeOption
|
|
type={PaymentType.SUBSCRIPTION_WEEKLY}
|
|
label="Weekly Subscription"
|
|
/>
|
|
</MultiSelectorMany>
|
|
|
|
<MultiSelectorMany
|
|
details={register("skill_levels", { required: true })}
|
|
label="Skill Level"
|
|
defaultValues={resource?.skill_levels ?? []}
|
|
>
|
|
{Object.values(SkillLevel).map((skillLevel, index) => {
|
|
return (
|
|
<SimpleSelectorManyOption
|
|
key={index}
|
|
type={skillLevel}
|
|
label={skillLevel.toLowerCase()}
|
|
/>
|
|
);
|
|
})}
|
|
</MultiSelectorMany>
|
|
|
|
<MultiSelectorMany
|
|
details={register("skills", { required: true })}
|
|
label="Skills Covered"
|
|
defaultValues={resource?.skills ?? []}
|
|
>
|
|
{Object.values(Skill).map((skill, index) => {
|
|
return (
|
|
<SimpleSelectorManyOption
|
|
key={index}
|
|
type={skill}
|
|
label={skill.toLowerCase()}
|
|
/>
|
|
);
|
|
})}
|
|
</MultiSelectorMany>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const ResourceDescriptionSubForm = () => {
|
|
const [dropdownOpen, toggleDropdown] = useState(false);
|
|
const { register } = useFormContext();
|
|
|
|
return (
|
|
<div className="mx-4">
|
|
<label className="text-md font-semibold">Description</label>
|
|
<div className="relative mt-4 overflow-hidden rounded-xl border border-neutral-400 bg-neutral-200 text-left shadow">
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
toggleDropdown(!dropdownOpen);
|
|
}}
|
|
className="group flex w-full flex-row justify-between border-b-[4px] border-neutral-700 bg-neutral-600 p-2 align-middle"
|
|
>
|
|
<section className="space-x-2">
|
|
<h3 className="inline text-sm font-bold text-neutral-100">
|
|
IMPORTANT
|
|
</h3>
|
|
<span className="inline italic text-neutral-300">open to edit</span>
|
|
</section>
|
|
<ChevronDownIcon className="mx-2 my-auto w-4 text-white group-hover:animate-bounce" />
|
|
</button>
|
|
<textarea
|
|
{...register("description", { required: true })}
|
|
className={
|
|
"h-48 w-full rounded-b-xl p-2" + (dropdownOpen ? " hidden" : "")
|
|
}
|
|
/>
|
|
<textarea
|
|
{...register("manufacturer.notice")}
|
|
className={
|
|
"h-48 w-full rounded-b-xl bg-neutral-800 p-2 text-white" +
|
|
(dropdownOpen ? "" : " hidden")
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const ResourceForm = ({
|
|
methods,
|
|
resource,
|
|
error,
|
|
}: {
|
|
resource?: ResourceUpdateInput;
|
|
methods: UseFormReturn<ResourceUpdateInput>;
|
|
error?: string;
|
|
}) => {
|
|
return (
|
|
<FormProvider {...methods}>
|
|
{error ? (
|
|
<h1 className="text-center font-semibold text-red-600">
|
|
Error Updating Resource:{" "}
|
|
<span className="font-normal text-red-400">{error}</span>
|
|
</h1>
|
|
) : undefined}
|
|
<form className="mx-auto flex max-w-2xl flex-col flex-col-reverse py-1 sm:flex-row sm:divide-x sm:py-4">
|
|
<div className="my-5 mr-4 flex flex-col text-lg font-bold">
|
|
<ResourceLinkSubForm links={resource?.platform_links ?? []} />{" "}
|
|
{/** //resource={resource} /> */}
|
|
</div>
|
|
<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 resource={resource} />
|
|
<ResourceDescriptionSubForm />
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</FormProvider>
|
|
);
|
|
};
|
|
|
|
export { ResourceForm };
|