Refactor api requests and components out of pages

This commit is contained in:
Aaron William Po
2023-01-28 21:05:20 -05:00
parent a182f55280
commit fe277d5964
32 changed files with 1455 additions and 302 deletions

15
components/ui/Layout.tsx Normal file
View File

@@ -0,0 +1,15 @@
import { FC, ReactNode } from 'react';
import Navbar from './Navbar';
const Layout: FC<{ children: ReactNode }> = ({ children }) => {
return (
<div className="flex h-screen flex-col">
<header className="top-0">
<Navbar />
</header>
<div className="animate-in fade-in top-0 h-full flex-1">{children}</div>
</div>
);
};
export default Layout;

88
components/ui/Navbar.tsx Normal file
View File

@@ -0,0 +1,88 @@
/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
/* eslint-disable jsx-a11y/label-has-associated-control */
/* eslint-disable jsx-a11y/label-has-for */
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
interface Page {
slug: string;
name: string;
}
const Navbar = () => {
const router = useRouter();
const [currentURL, setCurrentURL] = useState('/');
useEffect(() => {
setCurrentURL(router.asPath);
}, [router.asPath]);
const pages: Page[] = [
{ slug: '/beers', name: 'Beers' },
{ slug: '/breweries', name: 'Breweries' },
];
return (
<nav className="navbar bg-base-300">
<div className="flex-1">
<Link className="btn-ghost btn text-3xl normal-case" href="/">
<span className="cursor-pointer text-xl font-bold">Aaron William Po</span>
</Link>
</div>
<div className="hidden flex-none lg:block">
<ul className="menu menu-horizontal p-0">
{pages.map((page) => {
return (
<li key={page.slug}>
<Link tabIndex={0} href={page.slug}>
<span
className={`text-lg uppercase ${
currentURL === page.slug ? 'font-extrabold' : 'font-semibold'
} text-base-content`}
>
{page.name}
</span>
</Link>
</li>
);
})}
</ul>
</div>
<div className="flex-none lg:hidden">
<div className="dropdown-end dropdown">
<label tabIndex={0} className="btn-ghost btn-circle btn">
<div className="w-10 rounded-full">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
className="inline-block h-5 w-5 stroke-white"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</div>
</label>
<ul
tabIndex={0}
className="dropdown-content menu rounded-box menu-compact mt-3 w-48 bg-base-100 p-2 shadow"
>
{pages.map((page) => (
<li key={page.slug}>
<Link href={page.slug}>
<span className="select-none">{page.name}</span>
</Link>
</li>
))}
</ul>
</div>
</div>
</nav>
);
};
export default Navbar;

View File

@@ -3,18 +3,13 @@ import { FunctionComponent } from 'react';
interface FormButtonProps {
children: string;
type: 'button' | 'submit' | 'reset';
className?: string;
}
const Button: FunctionComponent<FormButtonProps> = ({ children, type, className }) => (
const Button: FunctionComponent<FormButtonProps> = ({ children, type }) => (
// eslint-disable-next-line react/button-has-type
<button type={type} className="btn-primary btn mt-4 w-full rounded-xl">
{children}
</button>
);
Button.defaultProps = {
className: '',
};
export default Button;

View File

@@ -3,8 +3,6 @@ import { FunctionComponent } from 'react';
// import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
/**
* Component for a styled form error message.
*
* @example
* <FormError>Something went wrong!</FormError>;
*/

View File

@@ -1,10 +1,16 @@
import { FunctionComponent, ReactNode } from 'react';
/** A container for both the form error and form label. */
interface FormInfoProps {
children: Array<ReactNode> | ReactNode;
children: [ReactNode, ReactNode];
}
/**
* @example
* <FormInfo>
* <FormLabel htmlFor="name">Name</FormLabel>
* <FormError>{errors.name?.message}</FormError>
* </FormInfo>;
*/
const FormInfo: FunctionComponent<FormInfoProps> = ({ children }) => (
<div className="flex justify-between">{children}</div>
);

View File

@@ -5,9 +5,13 @@ interface FormLabelProps {
children: string;
}
/**
* @example
* <FormLabel htmlFor="name">Name</FormLabel>;
*/
const FormLabel: FunctionComponent<FormLabelProps> = ({ htmlFor, children }) => (
<label
className="block uppercase tracking-wide text-sm sm:text-xs font-extrabold my-1"
className="my-1 block text-sm font-extrabold uppercase tracking-wide sm:text-xs"
htmlFor={htmlFor}
>
{children}

View File

@@ -10,6 +10,29 @@ interface FormSelectProps {
message: string;
}
/**
* @example
* <FormSelect
* options={[
* { value: '1', text: 'One' },
* { value: '2', text: 'Two' },
* { value: '3', text: 'Three' },
* ]}
* id="test"
* formRegister={register('test')}
* error={true}
* placeholder="Test"
* message="Select an option"
* />;
*
* @param props
* @param props.options The options to display in the select.
* @param props.id The id of the select.
* @param props.formRegister The form register hook from react-hook-form.
* @param props.error Whether or not the select has an error.
* @param props.placeholder The placeholder text for the select.
* @param props.message The message to display when no option is selected.
*/
const FormSelect: FunctionComponent<FormSelectProps> = ({
options,
id,
@@ -20,7 +43,7 @@ const FormSelect: FunctionComponent<FormSelectProps> = ({
}) => (
<select
id={id}
className={`select select-bordered block w-full rounded-lg ${
className={`select-bordered select block w-full rounded-lg ${
error ? 'select-error' : ''
}`}
placeholder={placeholder}

View File

@@ -7,21 +7,36 @@ interface FormTextAreaProps {
error: boolean;
id: string;
rows: number;
className?: string;
}
/**
* @example
* <FormTextArea
* id="test"
* formValidationSchema={register('test')}
* error={true}
* placeholder="Test"
* rows={5}
* />;
*
* @param props
* @param props.placeholder The placeholder text for the textarea.
* @param props.formValidationSchema The form register hook from react-hook-form.
* @param props.error Whether or not the textarea has an error.
* @param props.id The id of the textarea.
* @param props.rows The number of rows to display in the textarea.
*/
const FormTextArea: FunctionComponent<FormTextAreaProps> = ({
placeholder = '',
formValidationSchema,
error,
id,
rows,
className,
}) => (
<textarea
id={id}
placeholder={placeholder}
className={`${className} textarea textarea-bordered w-full resize-none rounded-lg bg-clip-padding border border-solid transition ease-in-out m-0 ${
className={`textarea-bordered textarea m-0 w-full resize-none rounded-lg border border-solid bg-clip-padding transition ease-in-out ${
error ? 'textarea-error' : ''
}`}
{...formValidationSchema}
@@ -29,9 +44,4 @@ const FormTextArea: FunctionComponent<FormTextAreaProps> = ({
/>
);
FormTextArea.defaultProps = {
placeholder: '',
className: '',
};
export default FormTextArea;

View File

@@ -1,6 +1,6 @@
/* eslint-disable react/require-default-props */
import { FunctionComponent } from 'react';
import { FieldError, UseFormRegisterReturn } from 'react-hook-form';
import { UseFormRegisterReturn } from 'react-hook-form';
interface FormInputProps {
placeholder?: string;
@@ -12,6 +12,25 @@ interface FormInputProps {
height?: string;
}
/**
* @example
* <FormTextInput
* placeholder="Lorem Ipsum Lager"
* formValidationSchema={register('name')}
* error={!!errors.name}
* type="text"
* id="name"
* />;
*
* @param param0 The props for the FormTextInput component
* @param param0.placeholder The placeholder text for the input
* @param param0.formValidationSchema The validation schema for the input, provided by
* react-hook-form.
* @param param0.error Whether or not the input has an error.
* @param param0.type The input type (email, password, text, date).
* @param param0.id The id of the input.
* @param param0.height The height of the input.
*/
const FormTextInput: FunctionComponent<FormInputProps> = ({
placeholder = '',
formValidationSchema,
@@ -23,7 +42,7 @@ const FormTextInput: FunctionComponent<FormInputProps> = ({
id={id}
type={type}
placeholder={placeholder}
className={`input w-full transition ease-in-out rounded-lg input-bordered ${
className={`input-bordered input w-full rounded-lg transition ease-in-out ${
error ? 'input-error' : ''
}`}
{...formValidationSchema}