add prettier lint rule

This commit is contained in:
Brandon Egger
2023-04-18 23:57:57 -05:00
parent e10f2911d9
commit 192c594d4f
20 changed files with 1684 additions and 1182 deletions

View File

@ -1,140 +1,173 @@
import { type NextPage } from "next/types";
import Image from 'next/image';
import Image from "next/image";
interface QuickLink {
label: string,
href: string,
label: string;
href: string;
}
const links: QuickLink[] = [
{
label: "Communication Sciences and Disorders",
href: "https://csd.uiowa.edu/",
},
{
label: "Wendell Johnson",
href: "https://www.facilities.uiowa.edu/named-building/wendell-johnson-speech-and-hearing-center",
}
]
{
label: "Communication Sciences and Disorders",
href: "https://csd.uiowa.edu/",
},
{
label: "Wendell Johnson",
href: "https://www.facilities.uiowa.edu/named-building/wendell-johnson-speech-and-hearing-center",
},
];
const QuickLink = ({label, href}: QuickLink) => {
return (
<a className="hover:underline space-x-2" target="_blank" href={href}>
<Image className="inline" alt="external link" width={16} height={16} src="/open-external-link-icon.svg" />
<span className="inline">{label}</span>
</a>
)
const QuickLink = ({ label, href }: QuickLink) => {
return (
<a className="space-x-2 hover:underline" target="_blank" href={href}>
<Image
className="inline"
alt="external link"
width={16}
height={16}
src="/open-external-link-icon.svg"
/>
<span className="inline">{label}</span>
</a>
);
};
interface ContactInfo {
name: string,
title: string,
email?: string,
phone?: string,
name: string;
title: string;
email?: string;
phone?: string;
}
const contacts: ContactInfo[] = [
{
name: "Olivia Adamson",
title: "Audiology Graduate Student Clinician",
email: "olivia-adamson@uiowa.edu",
},
{
name: "Eun Kyung (Julie) Jeon",
title: "Clinical Assistant Professor",
email: "eunkyung-jeon@uiowa.edu",
phone: "3194671476"
}
]
{
name: "Olivia Adamson",
title: "Audiology Graduate Student Clinician",
email: "olivia-adamson@uiowa.edu",
},
{
name: "Eun Kyung (Julie) Jeon",
title: "Clinical Assistant Professor",
email: "eunkyung-jeon@uiowa.edu",
phone: "3194671476",
},
];
const ContactInfo = ({name, title, email, phone}: ContactInfo) => {
return (
<section className="py-4 space-y-2">
<h1 className="text-md">{name}</h1>
<p className="italic text-sm text-neutral-400">{title}</p>
{ email ?
<section className="space-x-2">
<Image className="inline" alt="email" width={20} height={20} src="/mail-icon-white.svg"/>
<h2 className="text-sm inline">{email}</h2>
</section>
: undefined}
{ phone ?
<section className="space-x-2">
<Image className="inline" alt="phone" width={20} height={20} src="/phone-call-icon.svg"/>
<h2 className="text-sm inline">{phone}</h2>
</section>
: undefined}
const ContactInfo = ({ name, title, email, phone }: ContactInfo) => {
return (
<section className="space-y-2 py-4">
<h1 className="text-md">{name}</h1>
<p className="text-sm italic text-neutral-400">{title}</p>
{email ? (
<section className="space-x-2">
<Image
className="inline"
alt="email"
width={20}
height={20}
src="/mail-icon-white.svg"
/>
<h2 className="inline text-sm">{email}</h2>
</section>
)
}
) : undefined}
{phone ? (
<section className="space-x-2">
<Image
className="inline"
alt="phone"
width={20}
height={20}
src="/phone-call-icon.svg"
/>
<h2 className="inline text-sm">{phone}</h2>
</section>
) : undefined}
</section>
);
};
const FooterLabeledSection = ({title, children}: {
title: string,
children: JSX.Element[] | JSX.Element,
const FooterLabeledSection = ({
title,
children,
}: {
title: string;
children: JSX.Element[] | JSX.Element;
}) => {
return (
<div className="flex flex-col px-2 sm:px-8">
<h1 className="font-bold text-xl text-neutral-400">{title}</h1>
{children}
</div>
)
}
return (
<div className="flex flex-col px-2 sm:px-8">
<h1 className="text-xl font-bold text-neutral-400">{title}</h1>
{children}
</div>
);
};
const Footer: NextPage = () => {
return (
<div className="w-full bg-neutral-800">
{/** yellow stripe */}
<div className="bg-yellow-400 border-t-[1px] border-neutral-400 p-[4px]"></div>
return (
<div className="w-full bg-neutral-800">
{/** yellow stripe */}
<div className="border-t-[1px] border-neutral-400 bg-yellow-400 p-[4px]"></div>
{/** Main footer area */}
<div className="mx-auto max-w-5xl p-4 flex flex-col-reverse md:flex-row justify-between">
{/** Wendell Johnson Info */}
<div className="flex-col mt-8 sm:mt-0">
<Image alt="University of Iowa logo" width={128} height={64} src="/IOWA-gold-text.png" />
<div className="px-2 text-neutral-100 space-y-8">
<section>
<h1 className="text-yellow-300 text-md">Communication Sciences and Disorders</h1>
<h2 className="text-yellow-100 italic text-sm">College of Liberal Arts and Sciences</h2>
</section>
<section>
<h3 className="text-sm italic">Wendell Johnson Speech and Hearing Center</h3>
<p className="text-sm">250 Hawkins Dr</p>
<p className="text-sm">Iowa City, IA 52242</p>
</section>
<section>
<p className="text-sm text-neutral-400 italic">
Site Designed and Built by <a target="_blank" href="https://brandonegger.com" className="hover:underline">
Brandon Egger
</a>
</p>
</section>
</div>
</div>
{/** Header and tabs */}
<div className="flex flex-row text-neutral-200 mx-auto md:mx-0 sm:px-4 divide-x divide-neutral-500">
<FooterLabeledSection title="Quick Links">
<div className="flex flex-col pt-4 space-y-2">
{links.map((quickLink, index) => {
return (
<QuickLink key={index} {...quickLink}/>
)
})}
</div>
</FooterLabeledSection>
<FooterLabeledSection title="Contact">
<div className="flex flex-col divide-y divide-neutral-500">
{contacts.map((contactInfo, index) => {
return (
<ContactInfo key={index} {...contactInfo} />
)
})}
</div>
</FooterLabeledSection>
</div>
</div>
{/** Main footer area */}
<div className="mx-auto flex max-w-5xl flex-col-reverse justify-between p-4 md:flex-row">
{/** Wendell Johnson Info */}
<div className="mt-8 flex-col sm:mt-0">
<Image
alt="University of Iowa logo"
width={128}
height={64}
src="/IOWA-gold-text.png"
/>
<div className="space-y-8 px-2 text-neutral-100">
<section>
<h1 className="text-md text-yellow-300">
Communication Sciences and Disorders
</h1>
<h2 className="text-sm italic text-yellow-100">
College of Liberal Arts and Sciences
</h2>
</section>
<section>
<h3 className="text-sm italic">
Wendell Johnson Speech and Hearing Center
</h3>
<p className="text-sm">250 Hawkins Dr</p>
<p className="text-sm">Iowa City, IA 52242</p>
</section>
<section>
<p className="text-sm italic text-neutral-400">
Site Designed and Built by{" "}
<a
target="_blank"
href="https://brandonegger.com"
className="hover:underline"
>
Brandon Egger
</a>
</p>
</section>
</div>
</div>
)
}
{/** Header and tabs */}
<div className="mx-auto flex flex-row divide-x divide-neutral-500 text-neutral-200 sm:px-4 md:mx-0">
<FooterLabeledSection title="Quick Links">
<div className="flex flex-col space-y-2 pt-4">
{links.map((quickLink, index) => {
return <QuickLink key={index} {...quickLink} />;
})}
</div>
</FooterLabeledSection>
<FooterLabeledSection title="Contact">
<div className="flex flex-col divide-y divide-neutral-500">
{contacts.map((contactInfo, index) => {
return <ContactInfo key={index} {...contactInfo} />;
})}
</div>
</FooterLabeledSection>
</div>
</div>
</div>
);
};
export default Footer;

View File

@ -1,101 +1,141 @@
import { type NextPage } from "next";
import Image from 'next/image';
import Image from "next/image";
import Link from "next/link";
import { ChevronDownIcon } from '@heroicons/react/24/outline';
import { ChevronDownIcon } from "@heroicons/react/24/outline";
interface DropdownOption {
label: string;
href: string;
label: string;
href: string;
}
interface NavBarLinkProps {
href: string;
label: string;
dropdown?: DropdownOption[];
href: string;
label: string;
dropdown?: DropdownOption[];
}
const NavBarLink = ({href, label, dropdown}: NavBarLinkProps) => {
const DropDown = ({dropdownOptions}: {dropdownOptions: DropdownOption[]}) => {
const options = dropdownOptions.map((dropdownOption, index) => {
return (
<Link key={index} href={dropdownOption.href}>
<span className="block w-full px-4 py-2 bg-gradient-to-t hover:from-neutral-500 from-neutral-900 hover:to-neutral-500 to-neutral-700 text-white">{dropdownOption.label}</span>
</Link>
)
});
return (
<div className="right-0 rounded-b border-l-2 border-r-2 border-b-2 border-neutral-900 absolute w-full left-0 hidden group-hover:flex flex-col top-full">
{options}
</div>
)
}
const NavBarLink = ({ href, label, dropdown }: NavBarLinkProps) => {
const DropDown = ({
dropdownOptions,
}: {
dropdownOptions: DropdownOption[];
}) => {
const options = dropdownOptions.map((dropdownOption, index) => {
return (
<Link key={index} href={dropdownOption.href}>
<span className="block w-full bg-gradient-to-t from-neutral-900 to-neutral-700 px-4 py-2 text-white hover:from-neutral-500 hover:to-neutral-500">
{dropdownOption.label}
</span>
</Link>
);
});
return (
<li className="group relative">
<Link href={href} className={"h-14 block border-neutral-800 box-border" + (dropdown ? "" : " hover:border-b-2")}>
<div className="h-full flex flex-row space-x-[4px]">
<div className="inline-block my-auto">
<span className="inline-block font-bold text-lg py-2 align-text-middle">{label}</span>
</div>
{dropdown ? <ChevronDownIcon className="w-4"/> : <></>}
</div>
</Link>
{dropdown && dropdown.length > 0 ? <DropDown dropdownOptions={dropdown} /> : <></>}
</li>
<div className="absolute right-0 left-0 top-full hidden w-full flex-col rounded-b border-l-2 border-r-2 border-b-2 border-neutral-900 group-hover:flex">
{options}
</div>
);
}
};
const NavBar = () => {
const resourcesDropDown: DropdownOption[] = [
{
label: "search",
href: "/resources/search",
},
{
label: "view all",
href: "/resources"
return (
<li className="group relative">
<Link
href={href}
className={
"box-border block h-14 border-neutral-800" +
(dropdown ? "" : " hover:border-b-2")
}
]
return (
<nav className="sticky top-0 z-50 border-b border-neutral-400 bg-gradient-to-b from-amber-300 to-amber-300 w-full shadow-black drop-shadow-md">
<li className="mx-auto max-w-5xl flex flex-row sm:justify-between px-4">
<ul id="left-nav-links" className="flex flex-row space-x-10">
<NavBarLink href='/' label='Home'/>
<NavBarLink dropdown={resourcesDropDown} href='/resources' label='Resources'/>
<NavBarLink href='/about' label='About'/>
</ul>
<ul id="right-nav-links" className="hidden sm:flex flex-row space-x-10">
<li className="group relative">
<a target="_blank" href='https://forms.gle/FD2abgwBuTaipysZ6' className="h-14 block border-neutral-800 box-border hover:border-b-2">
<div className="h-full flex flex-row space-x-[4px]">
<div className="inline-block my-auto">
<span className="inline-block font-bold text-lg py-2 align-text-middle">Provide Feedback</span>
</div>
</div>
</a>
</li>
<NavBarLink href='/contact' label='Contact Us'/>
</ul>
</li>
</nav>
)
}
const Header: NextPage = () => {
return <>
<div id="logo-row" className="flex flex-row p-4 justify-center border-b border-yellow bg-neutral-800 drop-shadow-xl">
<div className="shadow-md shadow-yellow-500/50 bg-yellow-100 rounded-xl p-2">
<Image alt="Ear listening" src="/listening-ear.svg" width={64} height={64}/>
</div>
<div id="header-title" className="w-64 grid place-items-center">
<h1 className="text-center text-2xl font-bold text-neutral-200">Center for Auditory Training Resources</h1>
</div>
>
<div className="flex h-full flex-row space-x-[4px]">
<div className="my-auto inline-block">
<span className="align-text-middle inline-block py-2 text-lg font-bold">
{label}
</span>
</div>
{dropdown ? <ChevronDownIcon className="w-4" /> : <></>}
</div>
<NavBar/>
</>
</Link>
{dropdown && dropdown.length > 0 ? (
<DropDown dropdownOptions={dropdown} />
) : (
<></>
)}
</li>
);
};
export default Header;
const NavBar = () => {
const resourcesDropDown: DropdownOption[] = [
{
label: "search",
href: "/resources/search",
},
{
label: "view all",
href: "/resources",
},
];
return (
<nav className="sticky top-0 z-50 w-full border-b border-neutral-400 bg-gradient-to-b from-amber-300 to-amber-300 shadow-black drop-shadow-md">
<li className="mx-auto flex max-w-5xl flex-row px-4 sm:justify-between">
<ul id="left-nav-links" className="flex flex-row space-x-10">
<NavBarLink href="/" label="Home" />
<NavBarLink
dropdown={resourcesDropDown}
href="/resources"
label="Resources"
/>
<NavBarLink href="/about" label="About" />
</ul>
<ul id="right-nav-links" className="hidden flex-row space-x-10 sm:flex">
<li className="group relative">
<a
target="_blank"
href="https://forms.gle/FD2abgwBuTaipysZ6"
className="box-border block h-14 border-neutral-800 hover:border-b-2"
>
<div className="flex h-full flex-row space-x-[4px]">
<div className="my-auto inline-block">
<span className="align-text-middle inline-block py-2 text-lg font-bold">
Provide Feedback
</span>
</div>
</div>
</a>
</li>
<NavBarLink href="/contact" label="Contact Us" />
</ul>
</li>
</nav>
);
};
const Header: NextPage = () => {
return (
<>
<div
id="logo-row"
className="border-yellow flex flex-row justify-center border-b bg-neutral-800 p-4 drop-shadow-xl"
>
<div className="rounded-xl bg-yellow-100 p-2 shadow-md shadow-yellow-500/50">
<Image
alt="Ear listening"
src="/listening-ear.svg"
width={64}
height={64}
/>
</div>
<div id="header-title" className="grid w-64 place-items-center">
<h1 className="text-center text-2xl font-bold text-neutral-200">
Center for Auditory Training Resources
</h1>
</div>
</div>
<NavBar />
</>
);
};
export default Header;

View File

@ -1,297 +1,386 @@
import { type PlatformLink, type PaymentType, type AuditoryResource, type Skill, type SkillLevel, type Manufacturer } from '@prisma/client';
import { CurrencyDollarIcon, ArrowPathRoundedSquareIcon } from '@heroicons/react/24/solid';
import { ClipboardDocumentListIcon } from '@heroicons/react/24/outline';
import Image from 'next/image';
import Link from 'next/link';
import { translateEnumPlatform, translateEnumSkill } from '~/utils/enumWordLut';
import { type ChangeEvent } from 'react';
import { ChevronDownIcon } from '@heroicons/react/24/outline';
import { type ParsedUrlQuery, type ParsedUrlQueryInput } from 'querystring';
import { useRouter } from 'next/router';
import {
type PlatformLink,
type PaymentType,
type AuditoryResource,
type Skill,
type SkillLevel,
type Manufacturer,
} from "@prisma/client";
import {
CurrencyDollarIcon,
ArrowPathRoundedSquareIcon,
} from "@heroicons/react/24/solid";
import { ClipboardDocumentListIcon } from "@heroicons/react/24/outline";
import Image from "next/image";
import Link from "next/link";
import { translateEnumPlatform, translateEnumSkill } from "~/utils/enumWordLut";
import { type ChangeEvent } from "react";
import { ChevronDownIcon } from "@heroicons/react/24/outline";
import { type ParsedUrlQuery, type ParsedUrlQueryInput } from "querystring";
import { useRouter } from "next/router";
export const ResourceInfo = ({resource, showMoreInfo}: {resource: AuditoryResource, showMoreInfo?: boolean}) => {
const PriceIcons = ({type}: {type: PaymentType}) => {
switch(type) {
case "FREE": {
return (
<div className="pt-2 space-x-1" title="Free">
<span className="bg-amber-100 italic rounded-lg border border-neutral-900 text-black px-2 py-[1px]">
free
</span>
</div>
)
}
case "SUBSCRIPTION_MONTHLY": {
<div className="space-x-1" title="Monthly recurring subscription">
<ArrowPathRoundedSquareIcon className="inline h-6 w-6" />
<CurrencyDollarIcon className="inline h-6 w-6 text-lime-800"/>
</div>
}
case "SUBSCRIPTION_WEEKLY": {
return (
<div className="space-x-1" title="Weekly recurring subscription">
<ArrowPathRoundedSquareIcon className="inline h-6 w-6" />
<CurrencyDollarIcon className="inline h-6 w-6 text-lime-800"/>
</div>
)
}
}
}
const PlatformInfo = ({platformLinks}: {platformLinks: PlatformLink[]}) => {
const platformsStr = platformLinks.map((platformLink) => {
return translateEnumPlatform(platformLink.platform);
}).join(', ');
export const ResourceInfo = ({
resource,
showMoreInfo,
}: {
resource: AuditoryResource;
showMoreInfo?: boolean;
}) => {
const PriceIcons = ({ type }: { type: PaymentType }) => {
switch (type) {
case "FREE": {
return (
<p>{platformsStr}</p>
)
<div className="space-x-1 pt-2" title="Free">
<span className="rounded-lg border border-neutral-900 bg-amber-100 px-2 py-[1px] italic text-black">
free
</span>
</div>
);
}
case "SUBSCRIPTION_MONTHLY": {
<div className="space-x-1" title="Monthly recurring subscription">
<ArrowPathRoundedSquareIcon className="inline h-6 w-6" />
<CurrencyDollarIcon className="inline h-6 w-6 text-lime-800" />
</div>;
}
case "SUBSCRIPTION_WEEKLY": {
return (
<div className="space-x-1" title="Weekly recurring subscription">
<ArrowPathRoundedSquareIcon className="inline h-6 w-6" />
<CurrencyDollarIcon className="inline h-6 w-6 text-lime-800" />
</div>
);
}
}
return (
<div className="p-4 space-x-4 flex flex-row">
<div className="h-full my-auto">
{showMoreInfo ?
<Link href={`resources/${resource.id}`}>
<div className="w-20 sm:w-28 flex space-y-2 flex-col justify-center">
<Image className="bg-white w-full rounded-xl drop-shadow-lg border border-neutral-400" src={`/resource_logos/${resource.icon}`} alt={`${resource.name} logo`} width={512} height={512}/>
<span
className="block bg-neutral-900 hover:bg-neutral-500 border border-neutral-900 text-center py-[1px] text-white rounded-lg">
more info
</span>
</div>
</Link>
:
<div className="w-20 sm:w-28 flex space-y-2 flex-col justify-center">
<Image className="bg-white w-full rounded-xl drop-shadow-lg border border-neutral-400" src={`/resource_logos/${resource.icon}`} alt={`${resource.name} logo`} width={512} height={512}/>
</div>
}
</div>
<div className="grid place-items-center">
<div className="">
<h2 className="text-xs italic text-gray-600">{resource.manufacturer?.name}</h2>
<h1 className="font-bold text-xl">{resource.name}</h1>
<PlatformInfo platformLinks={resource.platform_links}/>
<PriceIcons type={resource?.payment_options[0] ?? 'FREE'} />
</div>
};
const PlatformInfo = ({
platformLinks,
}: {
platformLinks: PlatformLink[];
}) => {
const platformsStr = platformLinks
.map((platformLink) => {
return translateEnumPlatform(platformLink.platform);
})
.join(", ");
return <p>{platformsStr}</p>;
};
return (
<div className="flex flex-row space-x-4 p-4">
<div className="my-auto h-full">
{showMoreInfo ? (
<Link href={`resources/${resource.id}`}>
<div className="flex w-20 flex-col justify-center space-y-2 sm:w-28">
<Image
className="w-full rounded-xl border border-neutral-400 bg-white drop-shadow-lg"
src={`/resource_logos/${resource.icon}`}
alt={`${resource.name} logo`}
width={512}
height={512}
/>
<span className="block rounded-lg border border-neutral-900 bg-neutral-900 py-[1px] text-center text-white hover:bg-neutral-500">
more info
</span>
</div>
</Link>
) : (
<div className="flex w-20 flex-col justify-center space-y-2 sm:w-28">
<Image
className="w-full rounded-xl border border-neutral-400 bg-white drop-shadow-lg"
src={`/resource_logos/${resource.icon}`}
alt={`${resource.name} logo`}
width={512}
height={512}
/>
</div>
)}
</div>
<div className="grid place-items-center">
<div className="">
<h2 className="text-xs italic text-gray-600">
{resource.manufacturer?.name}
</h2>
<h1 className="text-xl font-bold">{resource.name}</h1>
<PlatformInfo platformLinks={resource.platform_links} />
<PriceIcons type={resource?.payment_options[0] ?? "FREE"} />
</div>
)
}
</div>
</div>
);
};
export const ResourceDescription = ({manufacturer, description}: {manufacturer: null | Manufacturer, description: string}) => {
return (
<div className="h-full flex flex-col">
{ manufacturer?.required ?
<div className="bg-neutral-600 border-t-[4px] border-neutral-700 p-2">
<h3 className="text-sm font-bold text-neutral-100">IMPORTANT</h3>
<p className="text-sm text-neutral-300">
This resource requires the patient to have a {manufacturer.name} device
</p>
</div>
: undefined}
<div className="p-2">
<p>{description}</p>
</div>
export const ResourceDescription = ({
manufacturer,
description,
}: {
manufacturer: null | Manufacturer;
description: string;
}) => {
return (
<div className="flex h-full flex-col">
{manufacturer?.required ? (
<div className="border-t-[4px] border-neutral-700 bg-neutral-600 p-2">
<h3 className="text-sm font-bold text-neutral-100">IMPORTANT</h3>
<p className="text-sm text-neutral-300">
This resource requires the patient to have a {manufacturer.name}{" "}
device
</p>
</div>
)
}
) : undefined}
<div className="p-2">
<p>{description}</p>
</div>
</div>
);
};
const ResourceEntry = ({resource}: {resource: AuditoryResource}) => {
const ResourceSkills = ({skills, skillLevels}: {skills: Skill[], skillLevels: SkillLevel[]}) => {
const SkillRanking = ({skillLevels}: {skillLevels: SkillLevel[]}) => {
return (
<div className='flex flex-row space-x-2 overflow-x-auto'>
{skillLevels.includes('BEGINNER') ?
<div className="rounded-lg px-[3px] border-green-600 border-2 bg-green-300">
<h2 className="text-neutral-900 italic text-sm text-right">Beginner</h2>
</div> : undefined
}
{skillLevels.includes('INTERMEDIATE') ?
<div className="rounded-lg px-[3px] border-orange-600 border-2 bg-orange-300">
<h2 className="text-neutral-900 text-sm italic text-right">Intermediate</h2>
</div> : undefined
}
{skillLevels.includes('ADVANCED') ?
<div className="rounded-lg px-[3px] border-red-600 border-2 bg-red-300">
<h2 className="text-neutral-900 text-sm italic text-right">Advanced</h2>
</div> : undefined
}
</div>
)
}
const Skill = ({label}: {label:string}) => {
return (
<li className="space-x-2 flex flex-row px-2 py-[1px]">
<ClipboardDocumentListIcon className="w-4" />
<div className="inline">
<h3>{label}</h3>
</div>
</li>
)
}
const skillsComponents = skills.map((skill, index) => {
return <Skill key={index} label={translateEnumSkill(skill)}/>
});
return (
<div className="m-2 flex space-y-4 flex-col">
{ skillsComponents.length > 0 ?
<div className='rounded-lg bg-gray-100 drop-shadow border border-neutral-900'>
<ul className="divide-y-2">
{skillsComponents}
</ul>
</div> : <></>
}
<SkillRanking skillLevels={skillLevels} />
</div>
)
}
return (
<tr className="divide-x-[1px] divide-slate-400">
<td className="max-w-xs">
<ResourceInfo showMoreInfo resource={resource} />
</td>
<td className="w-1/4 align-top">
<ResourceSkills skills={resource.skills} skillLevels={resource.skill_levels} />
</td>
<td className="align-top hidden md:table-cell">
<ResourceDescription manufacturer={resource.manufacturer} description={resource.description} />
</td>
</tr>
)
}
interface PagesNavigationProps {
query?: ParsedUrlQuery;
currentPage: number;
pageCount: number;
resultsPerPage: number;
}
const PagesNavigation = ({query, currentPage, pageCount, resultsPerPage}: PagesNavigationProps) => {
const router = useRouter();
const PageButton = ({number}: {number: number}) => {
const redirectQueryData: ParsedUrlQueryInput = {...query};
redirectQueryData.page = number;
return (
<li>
<Link className={"block py px-[9px] m-1 rounded " + (currentPage !== number ? "hover:bg-neutral-400 hover:text-white" : "bg-neutral-800 text-white")}
href={{ pathname: `/resources`, query: {...redirectQueryData} }}>
<span className={"text-lg text-center"}>{number}</span>
</Link>
</li>
)
}
const pages = Array.from(Array(pageCount).keys()).map((pageNumber) => {
const ResourceEntry = ({ resource }: { resource: AuditoryResource }) => {
const ResourceSkills = ({
skills,
skillLevels,
}: {
skills: Skill[];
skillLevels: SkillLevel[];
}) => {
const SkillRanking = ({ skillLevels }: { skillLevels: SkillLevel[] }) => {
return (
<PageButton key={pageNumber} number={pageNumber+1} />
)
<div className="flex flex-row space-x-2 overflow-x-auto">
{skillLevels.includes("BEGINNER") ? (
<div className="rounded-lg border-2 border-green-600 bg-green-300 px-[3px]">
<h2 className="text-right text-sm italic text-neutral-900">
Beginner
</h2>
</div>
) : undefined}
{skillLevels.includes("INTERMEDIATE") ? (
<div className="rounded-lg border-2 border-orange-600 bg-orange-300 px-[3px]">
<h2 className="text-right text-sm italic text-neutral-900">
Intermediate
</h2>
</div>
) : undefined}
{skillLevels.includes("ADVANCED") ? (
<div className="rounded-lg border-2 border-red-600 bg-red-300 px-[3px]">
<h2 className="text-right text-sm italic text-neutral-900">
Advanced
</h2>
</div>
) : undefined}
</div>
);
};
const Skill = ({ label }: { label: string }) => {
return (
<li className="flex flex-row space-x-2 px-2 py-[1px]">
<ClipboardDocumentListIcon className="w-4" />
<div className="inline">
<h3>{label}</h3>
</div>
</li>
);
};
const skillsComponents = skills.map((skill, index) => {
return <Skill key={index} label={translateEnumSkill(skill)} />;
});
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
if (!query) {
router.push({
pathname: '/resources',
query: {
perPage: event.target.value,
}
}).catch((reason) => {
console.error(reason);
});
return;
}
query['perPage'] = event.target.value;
router.push({
pathname: '/resources',
query: {
...query,
}
}).catch((reason) => {
console.error(reason);
});
};
return (
<div className="flex flex-row justify-between pl-2 pr-4 py-2 bg-amber-100">
<div className="flex flex-row w-64 space-x-2">
<div className="relative inline-flex">
<select
className="block appearance-none w-full bg-white border border-gray-400 hover:border-gray-500 px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
value={resultsPerPage}
onChange={handleChange}
>
<option value={5}>5</option>
<option value={10}>10</option>
<option value={20}>20</option>
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<ChevronDownIcon className="h-4 w-4"/>
</div>
</div>
<div className="m-auto">
<h1 className="text-md"> Results Per Page</h1>
</div>
</div>
<ul className="max-w-[10rem] sm:max-w-none overflow-x-auto my-auto flex flex-row bg-white rounded border-gray-400 hover:border-gray-500 border shadow">
{pages}
</ul>
<div className="m-2 flex flex-col space-y-4">
{skillsComponents.length > 0 ? (
<div className="rounded-lg border border-neutral-900 bg-gray-100 drop-shadow">
<ul className="divide-y-2">{skillsComponents}</ul>
</div>
) : (
<></>
)}
<SkillRanking skillLevels={skillLevels} />
</div>
)
}
);
};
const ResourceTable = ({resources, resourcesPerPage, currentPage, totalPages, query}: {
resources: AuditoryResource[],
resourcesPerPage: number,
currentPage: number,
totalPages: number,
query: ParsedUrlQuery
return (
<tr className="divide-x-[1px] divide-slate-400">
<td className="max-w-xs">
<ResourceInfo showMoreInfo resource={resource} />
</td>
<td className="w-1/4 align-top">
<ResourceSkills
skills={resource.skills}
skillLevels={resource.skill_levels}
/>
</td>
<td className="hidden align-top md:table-cell">
<ResourceDescription
manufacturer={resource.manufacturer}
description={resource.description}
/>
</td>
</tr>
);
};
interface PagesNavigationProps {
query?: ParsedUrlQuery;
currentPage: number;
pageCount: number;
resultsPerPage: number;
}
const PagesNavigation = ({
query,
currentPage,
pageCount,
resultsPerPage,
}: PagesNavigationProps) => {
const router = useRouter();
const PageButton = ({ number }: { number: number }) => {
const redirectQueryData: ParsedUrlQueryInput = { ...query };
redirectQueryData.page = number;
return (
<li>
<Link
className={
"py m-1 block rounded px-[9px] " +
(currentPage !== number
? "hover:bg-neutral-400 hover:text-white"
: "bg-neutral-800 text-white")
}
href={{ pathname: `/resources`, query: { ...redirectQueryData } }}
>
<span className={"text-center text-lg"}>{number}</span>
</Link>
</li>
);
};
const pages = Array.from(Array(pageCount).keys()).map((pageNumber) => {
return <PageButton key={pageNumber} number={pageNumber + 1} />;
});
const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
if (!query) {
router
.push({
pathname: "/resources",
query: {
perPage: event.target.value,
},
})
.catch((reason) => {
console.error(reason);
});
return;
}
query["perPage"] = event.target.value;
router
.push({
pathname: "/resources",
query: {
...query,
},
})
.catch((reason) => {
console.error(reason);
});
};
return (
<div className="flex flex-row justify-between bg-amber-100 py-2 pl-2 pr-4">
<div className="flex w-64 flex-row space-x-2">
<div className="relative inline-flex">
<select
className="focus:shadow-outline block w-full appearance-none rounded border border-gray-400 bg-white px-4 py-2 pr-8 leading-tight shadow hover:border-gray-500 focus:outline-none"
value={resultsPerPage}
onChange={handleChange}
>
<option value={5}>5</option>
<option value={10}>10</option>
<option value={20}>20</option>
</select>
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<ChevronDownIcon className="h-4 w-4" />
</div>
</div>
<div className="m-auto">
<h1 className="text-md"> Results Per Page</h1>
</div>
</div>
<ul className="my-auto flex max-w-[10rem] flex-row overflow-x-auto rounded border border-gray-400 bg-white shadow hover:border-gray-500 sm:max-w-none">
{pages}
</ul>
</div>
);
};
const ResourceTable = ({
resources,
resourcesPerPage,
currentPage,
totalPages,
query,
}: {
resources: AuditoryResource[];
resourcesPerPage: number;
currentPage: number;
totalPages: number;
query: ParsedUrlQuery;
}) => {
const resourceElements = resources.map((resource, index) => {
return (<ResourceEntry key={index} resource={resource} />);
const resourceElements =
resources.map((resource, index) => {
return <ResourceEntry key={index} resource={resource} />;
}) ?? [];
return(
return (
<div className="w-full">
<div className="mx-auto rounded-xl overflow-hidden border border-neutral-400 drop-shadow-md overflow-hidden">
<PagesNavigation query={query} resultsPerPage={resourcesPerPage} currentPage={currentPage} pageCount={totalPages} />
<table className="w-full table-fixed bg-neutral-200 border-b border-neutral-400">
<thead className="bg-gradient-to-t from-neutral-900 to-neutral-700 drop-shadow-md">
<tr>
<th className="w-1/3 max-w-xs">
<span className="text-gray-300 block px-4 py-2 text-left">
Resource
</span>
</th>
<th className="w-1/4 max-w-xs">
<span className="text-gray-300 block px-4 py-2 text-left">
Skills
</span>
</th>
<th className="hidden md:table-cell">
<span className="text-gray-300 block px-4 py-2 text-left">
Description
</span>
</th>
</tr>
</thead>
<tbody className="divide-y-[1px] divide-slate-400 overflow-y-scroll">
{resourceElements}
</tbody>
</table>
{(resources && resources.length > 4) ?
<PagesNavigation query={query} resultsPerPage={resourcesPerPage} currentPage={currentPage} pageCount={totalPages} />
: undefined}
</div>
<div className="mx-auto overflow-hidden overflow-hidden rounded-xl border border-neutral-400 drop-shadow-md">
<PagesNavigation
query={query}
resultsPerPage={resourcesPerPage}
currentPage={currentPage}
pageCount={totalPages}
/>
<table className="w-full table-fixed border-b border-neutral-400 bg-neutral-200">
<thead className="bg-gradient-to-t from-neutral-900 to-neutral-700 drop-shadow-md">
<tr>
<th className="w-1/3 max-w-xs">
<span className="block px-4 py-2 text-left text-gray-300">
Resource
</span>
</th>
<th className="w-1/4 max-w-xs">
<span className="block px-4 py-2 text-left text-gray-300">
Skills
</span>
</th>
<th className="hidden md:table-cell">
<span className="block px-4 py-2 text-left text-gray-300">
Description
</span>
</th>
</tr>
</thead>
<tbody className="divide-y-[1px] divide-slate-400 overflow-y-scroll">
{resourceElements}
</tbody>
</table>
{resources && resources.length > 4 ? (
<PagesNavigation
query={query}
resultsPerPage={resourcesPerPage}
currentPage={currentPage}
pageCount={totalPages}
/>
) : undefined}
</div>
</div>
);
);
};
export default ResourceTable;

View File

@ -1,274 +1,383 @@
import { type PaymentType, type Platform, type Skill, type SkillLevel } from "@prisma/client"
import {
type PaymentType,
type Platform,
type Skill,
type SkillLevel,
} from "@prisma/client";
import { type Dispatch, type SetStateAction, useState, useEffect } from "react";
export type QuestionTypes = Platform | Skill | SkillLevel | PaymentType | string;
export type QuestionTypes =
| Platform
| Skill
| SkillLevel
| PaymentType
| string;
export interface Option<T> {
label: string,
value: T,
label: string;
value: T;
}
export interface Question<T> {
for: string,
header: string,
question: string,
maxSelect?: number,
optional: true,
options: Option<T>[]
for: string;
header: string;
question: string;
maxSelect?: number;
optional: true;
options: Option<T>[];
}
const GreetingPage = ({updatePage}: {
updatePage: (pageNumber: number) => void,
const GreetingPage = ({
updatePage,
}: {
updatePage: (pageNumber: number) => void;
}) => {
const getStartedClick = () => {
updatePage(1);
}
const getStartedClick = () => {
updatePage(1);
};
return (
<div className="flex flex-col text-center">
<h1 className="text-center text-xl font-extrabold mb-8 max-w-sm">Welcome to the auditory training resource search tool!</h1>
<p className="mx-auto text-center text-neutral-500 italic max-w-sm">We will ask a few questions about the patient and then recommend the best auditory training resources based on your answers!</p>
<button onClick={getStartedClick} className="bottom-0 mt-8 py-2 px-4 bg-yellow-100 mx-auto rounded-md border border-neutral-900 ease-out duration-200 shadow-lg hover:shadow-md hover:bg-yellow-300">Get Started!</button>
</div>
)
}
return (
<div className="flex flex-col text-center">
<h1 className="mb-8 max-w-sm text-center text-xl font-extrabold">
Welcome to the auditory training resource search tool!
</h1>
<p className="mx-auto max-w-sm text-center italic text-neutral-500">
We will ask a few questions about the patient and then recommend the
best auditory training resources based on your answers!
</p>
<button
onClick={getStartedClick}
className="bottom-0 mx-auto mt-8 rounded-md border border-neutral-900 bg-yellow-100 py-2 px-4 shadow-lg duration-200 ease-out hover:bg-yellow-300 hover:shadow-md"
>
Get Started!
</button>
</div>
);
};
/**
* Single question component for a guided search
*/
const QuestionPage = ({isLastPage, page, question, updatePage, formData, updateFormData, dontCareData, setDontCareData}: {
isLastPage: boolean,
page: number,
question: Question<QuestionTypes>,
updatePage: (pageNumber: number) => void,
formData: Record<string, QuestionTypes[]>,
updateFormData: Dispatch<SetStateAction<Record<string, QuestionTypes[]>>>,
dontCareData: Record<string, boolean>,
setDontCareData: Dispatch<SetStateAction<Record<string, boolean>>>,
const QuestionPage = ({
isLastPage,
page,
question,
updatePage,
formData,
updateFormData,
dontCareData,
setDontCareData,
}: {
isLastPage: boolean;
page: number;
question: Question<QuestionTypes>;
updatePage: (pageNumber: number) => void;
formData: Record<string, QuestionTypes[]>;
updateFormData: Dispatch<SetStateAction<Record<string, QuestionTypes[]>>>;
dontCareData: Record<string, boolean>;
setDontCareData: Dispatch<SetStateAction<Record<string, boolean>>>;
}) => {
const dontCare = dontCareData[question.for] ?? false;
const dontCare = dontCareData[question.for] ?? false;
const OptionToggle = ({option}: {option: Option<QuestionTypes>}) => {
const selected = formData[question.for]?.includes(option.value) ?? false;
const handleToggle = () => {
const newFormData = {
...formData
};
const OptionToggle = ({ option }: { option: Option<QuestionTypes> }) => {
const selected = formData[question.for]?.includes(option.value) ?? false;
if (!newFormData[question.for]) {
newFormData[question.for] = [option.value];
} else if (newFormData[question.for]?.includes(option.value)) {
newFormData[question.for] = newFormData[question.for]?.filter(function(item) {
return item !== option.value
}) ?? [];
} else {
newFormData[question.for] = [...newFormData[question.for] ?? [], option.value];
}
const handleToggle = () => {
const newFormData = {
...formData,
};
updateFormData(newFormData);
}
if (!newFormData[question.for]) {
newFormData[question.for] = [option.value];
} else if (newFormData[question.for]?.includes(option.value)) {
newFormData[question.for] =
newFormData[question.for]?.filter(function (item) {
return item !== option.value;
}) ?? [];
} else {
newFormData[question.for] = [
...(newFormData[question.for] ?? []),
option.value,
];
}
if (dontCare) {
return (
<button disabled type="button" onClick={handleToggle} className={"line-through mx-auto w-64 py-2 shadow rounded-lg border border-neutral-400 " + (selected ? "bg-amber-200" : "bg-white")}>
{option.label}
</button>
)
}
return (
<button type="button" onClick={handleToggle} className={"mx-auto w-64 py-2 shadow rounded-lg border border-neutral-400 " + (selected ? "bg-amber-200" : "bg-white")}>
{option.label}
</button>
)
}
useEffect(() => {
if (!formData[question.for]) {
const newFormData = {...formData};
newFormData[question.for] = [];
updateFormData(newFormData);
}
});
const dontCareToggle = () => {
if (formData[question.for]) {
const newFormData = {
...formData
};
newFormData[question.for] = [];
updateFormData(newFormData);
}
const newDontCareData = {
...dontCareData
}
newDontCareData[question.for] = !dontCare;
setDontCareData(newDontCareData);
}
const nextClick = () => {
updatePage(page + 1);
}
const backClick = () => {
updatePage(page - 1);
}
const AdvanceButtons = () => {
return (
<section className="">
{!isLastPage ?
<div className="space-x-4">
<button onClick={backClick} className="inline mx-auto bottom-0 py-2 px-4 bg-yellow-100 mx-auto rounded-md border border-neutral-900 ease-out duration-200 shadow-lg hover:shadow-md hover:bg-yellow-300">back</button>
<button onClick={nextClick} className="inline mx-auto bottom-0 py-2 px-4 bg-yellow-100 mx-auto rounded-md border border-neutral-900 ease-out duration-200 shadow-lg hover:shadow-md hover:bg-yellow-300">next</button>
</div>
:
<div className="flex flex-col space-y-2 mt-4">
<button onClick={backClick} className="mx-auto bottom-0 py-2 px-4 bg-yellow-100 mx-auto rounded-md border border-neutral-900 ease-out duration-200 shadow-lg hover:shadow-md hover:bg-yellow-300">back</button>
<button form="search-form" type="submit" className="mx-auto bottom-0 py-2 px-4 bg-yellow-100 mx-auto rounded-md border border-neutral-900 ease-out duration-200 shadow-lg hover:shadow-md hover:bg-yellow-300">submit</button>
</div>
}
</section>
)
updateFormData(newFormData);
};
return (
<div className="h-full flex flex-col justify-between text-center">
<section className="mt-4">
<h2 className="text-neutral-400 italic text-xl">{question.header}</h2>
<h1 className="text-neutral-900 font-bold text-xl">{question.question}</h1>
<h3 className="text-neutral-600 text-sm">Select all that apply from below</h3>
</section>
if (dontCare) {
return (
<button
disabled
type="button"
onClick={handleToggle}
className={
"mx-auto w-64 rounded-lg border border-neutral-400 py-2 line-through shadow " +
(selected ? "bg-amber-200" : "bg-white")
}
>
{option.label}
</button>
);
}
<section className="flex flex-col space-y-1 justify-center">
{question.options.map((option, index) => {
return (
<OptionToggle key={index} option={option} />
);
})}
</section>
{question.optional ?
<button type="button" onClick={dontCareToggle} className={"mx-auto w-64 py-2 shadow rounded-lg border border-neutral-400 " + (dontCare ? "bg-amber-200" : "bg-white")}>
No Preference
</button>
: undefined}
<div className="mb-4">
<AdvanceButtons />
</div>
</div>
)
}
return (
<button
type="button"
onClick={handleToggle}
className={
"mx-auto w-64 rounded-lg border border-neutral-400 py-2 shadow " +
(selected ? "bg-amber-200" : "bg-white")
}
>
{option.label}
</button>
);
};
useEffect(() => {
if (!formData[question.for]) {
const newFormData = { ...formData };
newFormData[question.for] = [];
updateFormData(newFormData);
}
});
const dontCareToggle = () => {
if (formData[question.for]) {
const newFormData = {
...formData,
};
newFormData[question.for] = [];
updateFormData(newFormData);
}
const newDontCareData = {
...dontCareData,
};
newDontCareData[question.for] = !dontCare;
setDontCareData(newDontCareData);
};
const nextClick = () => {
updatePage(page + 1);
};
const backClick = () => {
updatePage(page - 1);
};
const AdvanceButtons = () => {
return (
<section className="">
{!isLastPage ? (
<div className="space-x-4">
<button
onClick={backClick}
className="bottom-0 mx-auto mx-auto inline rounded-md border border-neutral-900 bg-yellow-100 py-2 px-4 shadow-lg duration-200 ease-out hover:bg-yellow-300 hover:shadow-md"
>
back
</button>
<button
onClick={nextClick}
className="bottom-0 mx-auto mx-auto inline rounded-md border border-neutral-900 bg-yellow-100 py-2 px-4 shadow-lg duration-200 ease-out hover:bg-yellow-300 hover:shadow-md"
>
next
</button>
</div>
) : (
<div className="mt-4 flex flex-col space-y-2">
<button
onClick={backClick}
className="bottom-0 mx-auto mx-auto rounded-md border border-neutral-900 bg-yellow-100 py-2 px-4 shadow-lg duration-200 ease-out hover:bg-yellow-300 hover:shadow-md"
>
back
</button>
<button
form="search-form"
type="submit"
className="bottom-0 mx-auto mx-auto rounded-md border border-neutral-900 bg-yellow-100 py-2 px-4 shadow-lg duration-200 ease-out hover:bg-yellow-300 hover:shadow-md"
>
submit
</button>
</div>
)}
</section>
);
};
return (
<div className="flex h-full flex-col justify-between text-center">
<section className="mt-4">
<h2 className="text-xl italic text-neutral-400">{question.header}</h2>
<h1 className="text-xl font-bold text-neutral-900">
{question.question}
</h1>
<h3 className="text-sm text-neutral-600">
Select all that apply from below
</h3>
</section>
<section className="flex flex-col justify-center space-y-1">
{question.options.map((option, index) => {
return <OptionToggle key={index} option={option} />;
})}
</section>
{question.optional ? (
<button
type="button"
onClick={dontCareToggle}
className={
"mx-auto w-64 rounded-lg border border-neutral-400 py-2 shadow " +
(dontCare ? "bg-amber-200" : "bg-white")
}
>
No Preference
</button>
) : undefined}
<div className="mb-4">
<AdvanceButtons />
</div>
</div>
);
};
/**
* Wrapper for last and current page to enable the transition animation
*/
const PageTransition = ({backwards, lastPage, currentPage}: {
backwards: boolean,
lastPage: JSX.Element | null,
currentPage: JSX.Element,
const PageTransition = ({
backwards,
lastPage,
currentPage,
}: {
backwards: boolean;
lastPage: JSX.Element | null;
currentPage: JSX.Element;
}) => {
return (
<div className={"h-[500px] w-[200%] flex " + (backwards ? "flex-row animate-slide_search_page_backwards" : "flex-row-reverse translate-x-[-50%] animate-slide_search_page")}>
<div className="relative w-1/2 h-full grid place-items-center">
{currentPage}
</div>
return (
<div
className={
"flex h-[500px] w-[200%] " +
(backwards
? "animate-slide_search_page_backwards flex-row"
: "translate-x-[-50%] animate-slide_search_page flex-row-reverse")
}
>
<div className="relative grid h-full w-1/2 place-items-center">
{currentPage}
</div>
{/** last page */}
<div className="relative w-1/2 h-full grid place-items-center">
{lastPage}
</div>
</div>
);
{/** last page */}
<div className="relative grid h-full w-1/2 place-items-center">
{lastPage}
</div>
</div>
);
};
/**
* Main guided search component.
* Page 0 = greeting page.
*/
const GuidedSearch = ({questions}: {
questions: Question<QuestionTypes>[],
const GuidedSearch = ({
questions,
}: {
questions: Question<QuestionTypes>[];
}) => {
const [page, setPage] = useState<number>(0);
const [formData, setFormData] = useState<(Record<string, QuestionTypes[]>)>({});
const [dontCareData, setDoneCareData] = useState<(Record<string, boolean>)>({});
const [previousPage, setPreviousPage] = useState<number | undefined>(undefined);
const [page, setPage] = useState<number>(0);
const [formData, setFormData] = useState<Record<string, QuestionTypes[]>>({});
const [dontCareData, setDoneCareData] = useState<Record<string, boolean>>({});
const [previousPage, setPreviousPage] = useState<number | undefined>(
undefined
);
const updatePage = (pageNumber: number) => {
setPreviousPage(page);
setPage(pageNumber);
};
const updatePage = (pageNumber: number) => {
setPreviousPage(page);
setPage(pageNumber);
};
const SearchPage = ({pageNumber}: {
pageNumber?: number,
}) => {
if (pageNumber === undefined) {
return null;
}
if (pageNumber === 0) {
return (
<GreetingPage updatePage={updatePage} />
);
}
const question = questions[pageNumber-1];
if (!question) {
return null;
}
const isLastPage = pageNumber === questions.length
return (
<QuestionPage dontCareData={dontCareData} setDontCareData={setDoneCareData} isLastPage={isLastPage} page={page} formData={formData} updateFormData={setFormData} updatePage={updatePage} question={question} />
);
const SearchPage = ({ pageNumber }: { pageNumber?: number }) => {
if (pageNumber === undefined) {
return null;
}
/**
* Renders the hidden html form selectors
*/
const HTMLQuestion = ({question}: {question: Question<QuestionTypes>}) => {
return (
<select className="hidden" name={question.for} multiple>
{question.options.map((option, index) => {
return (
<option key={index} selected={formData[question.for]?.includes(option.value)} value={option.value.toString()}>
{option.label}
</option>
);
})
}
</select>
);
if (pageNumber === 0) {
return <GreetingPage updatePage={updatePage} />;
}
const lastPage = <SearchPage pageNumber={previousPage} />;
const currentPage = <SearchPage pageNumber={page} />;
const backwards = (previousPage ?? -1) >= page;
const question = questions[pageNumber - 1];
if (!question) {
return null;
}
const isLastPage = pageNumber === questions.length;
return (
<div>
<div className="px-4 py-2 bg-gradient-to-t from-neutral-900 to-neutral-700 mx-auto overflow-hidden">
<h1 className="text-gray-300 font-bold">Search</h1>
</div>
<QuestionPage
dontCareData={dontCareData}
setDontCareData={setDoneCareData}
isLastPage={isLastPage}
page={page}
formData={formData}
updateFormData={setFormData}
updatePage={updatePage}
question={question}
/>
);
};
<PageTransition backwards={backwards} key={page} lastPage={lastPage} currentPage={currentPage}/>
/**
* Renders the hidden html form selectors
*/
const HTMLQuestion = ({
question,
}: {
question: Question<QuestionTypes>;
}) => {
return (
<select className="hidden" name={question.for} multiple>
{question.options.map((option, index) => {
return (
<option
key={index}
selected={formData[question.for]?.includes(option.value)}
value={option.value.toString()}
>
{option.label}
</option>
);
})}
</select>
);
};
{/** Hidden html */}
<form action="/resources" id='search-form' className="hidden">
{questions.map((question, index) => {
return <HTMLQuestion key={index} question={question} />
})}
</form>
</div>
)
}
const lastPage = <SearchPage pageNumber={previousPage} />;
const currentPage = <SearchPage pageNumber={page} />;
const backwards = (previousPage ?? -1) >= page;
export {
GuidedSearch,
}
return (
<div>
<div className="mx-auto overflow-hidden bg-gradient-to-t from-neutral-900 to-neutral-700 px-4 py-2">
<h1 className="font-bold text-gray-300">Search</h1>
</div>
<PageTransition
backwards={backwards}
key={page}
lastPage={lastPage}
currentPage={currentPage}
/>
{/** Hidden html */}
<form action="/resources" id="search-form" className="hidden">
{questions.map((question, index) => {
return <HTMLQuestion key={index} question={question} />;
})}
</form>
</div>
);
};
export { GuidedSearch };