diff --git a/app/components/header.tsx b/app/components/header.tsx new file mode 100644 index 0000000..645df7b --- /dev/null +++ b/app/components/header.tsx @@ -0,0 +1,40 @@ +import {LinkContainer} from 'react-router-bootstrap' +import {Container, Nav, Navbar, NavDropdown} from 'react-bootstrap'; + +function Header() { + return ( + + + + K-FRIDGE + + + + + + + + + ); +} + +export default Header; \ No newline at end of file diff --git a/app/root.tsx b/app/root.tsx index 781d6fe..44a7fb2 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,29 +1,25 @@ import type { LinksFunction } from "@remix-run/node"; import { Links, - LiveReload, - Outlet + Scripts, + Outlet, + useRouteError, + isRouteErrorResponse } from "@remix-run/react"; +import type { PropsWithChildren } from "react"; -import globalLargeStylesUrl from "~/styles/global-large.css?url"; -import globalMediumStylesUrl from "~/styles/global-medium.css?url"; -import globalStylesUrl from "~/styles/global.css?url"; +import stylesheet from "bootstrap/dist/css/bootstrap.min.css?url"; export const links: LinksFunction = () => [ - { rel: "stylesheet", href: globalStylesUrl }, - { - rel: "stylesheet", - href: globalMediumStylesUrl, - media: "print, (min-width: 640px)", - }, - { - rel: "stylesheet", - href: globalLargeStylesUrl, - media: "screen and (min-width: 1024px)", - }, + { rel: "stylesheet", href: stylesheet }, ]; -export default function App() { +import Header from "~/components/header"; + +function Document({ + children, + title = "K-FRIDGE", +}: PropsWithChildren<{ title?: string }>) { return ( @@ -32,13 +28,53 @@ export default function App() { name="viewport" content="width=device-width, initial-scale=1" /> - Remix: So great, it's funny! + {title} - - +
+ {children} + ); +} + +export default function App() { + return ( + + + + ); +} + +export function ErrorBoundary() { + const error = useRouteError(); + + if (isRouteErrorResponse(error)) { + return ( + +
+

+ {error.status} {error.statusText} +

+
+
+ ); + } + + const errorMessage = + error instanceof Error + ? error.message + : "Unknown error"; + return ( + +
+

App Error

+
{errorMessage}
+
+
+ ); } \ No newline at end of file diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 0b8a880..d5de973 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,11 +1,3 @@ -import type { LinksFunction } from "@remix-run/node"; - -import stylesUrl from "~/styles/index.css?url"; - -export const links: LinksFunction = () => [ - { rel: "stylesheet", href: stylesUrl }, -]; - export default function Index() { return (
diff --git a/app/routes/beers.$beerid.tsx b/app/routes/beers.$beerid.tsx index 531a706..4d83d04 100644 --- a/app/routes/beers.$beerid.tsx +++ b/app/routes/beers.$beerid.tsx @@ -1,9 +1,39 @@ -import type { LoaderFunctionArgs } from "@remix-run/node"; -import { json } from "@remix-run/node"; -import { useLoaderData } from "@remix-run/react"; +import type { + LoaderFunctionArgs, + ActionFunctionArgs +} from "@remix-run/node"; +import { json, redirect } from "@remix-run/node"; +import { + useLoaderData, + useParams, + isRouteErrorResponse, + useRouteError } from "@remix-run/react"; import { db } from "~/utils/db.server"; +export const action = async ({ + params, + request, + }: ActionFunctionArgs) => { + const form = await request.formData(); + if (form.get("intent") !== "delete") { + throw new Response( + `The intent ${form.get("intent")} is not supported`, + { status: 400 } + ); + } + const beer = await db.beer.findUnique({ + where: { id: params.beerid }, + }); + if (!beer) { + throw new Response("Can't delete what does not exist", { + status: 404, + }); + } + await db.beer.delete({ where: { id: params.beerid } }); + return redirect("/beers"); + }; + export const loader = async ({ params, }: LoaderFunctionArgs) => { @@ -11,7 +41,9 @@ export const loader = async ({ where: { id: params.beerid }, }); if (!beer) { - throw new Error("Beer not found"); + throw new Response("Beer not found.", { + status: 404, + }); } return json({ beer }); }; @@ -24,6 +56,45 @@ export default function BeerRoute() {

You selected this beer:

{data.beer.name}

ABV {data.beer.abv}%

+
+ +
); } + +export function ErrorBoundary() { + const { beerid } = useParams(); + const error = useRouteError(); + + if (isRouteErrorResponse(error)) { + if (error.status === 400) { + return ( +
+ What you're trying to do is not allowed. +
+ ); + } + if (error.status === 404) { + return ( +
+ Huh? What the heck is "{beerid}"? +
+ ); + } + } + + return ( +
+ There was an error loading beer by the id "{beerid}". + Sorry. +
+ ); + } \ No newline at end of file diff --git a/app/routes/beers._index.tsx b/app/routes/beers._index.tsx index e995422..9a5ca0e 100644 --- a/app/routes/beers._index.tsx +++ b/app/routes/beers._index.tsx @@ -18,7 +18,7 @@ export default function BeersIndexRoute() { return (
-

Here's a random Beer:

+

Here's a random Beer:

{data.randomBeer.name}

"{data.randomBeer.name}" Permalink @@ -26,3 +26,11 @@ export default function BeersIndexRoute() {
); } + +export function ErrorBoundary() { + return ( +
+ Something unexpected went wrong. Sorry about that. +
+ ); + } \ No newline at end of file diff --git a/app/routes/beers.new.tsx b/app/routes/beers.new.tsx index d9cd9ea..6b7dc6a 100644 --- a/app/routes/beers.new.tsx +++ b/app/routes/beers.new.tsx @@ -1,7 +1,25 @@ import type { ActionFunctionArgs } from "@remix-run/node"; import { redirect } from "@remix-run/node"; +import { useActionData } from "@remix-run/react"; import { db } from "~/utils/db.server"; +import { badRequest } from "~/utils/request.server"; + +function validateBeerAbv(abv: number) { + if (abv < 0) { + return "Negative ABV not allowed"; + } + + if (abv > 100) { + return "ABV can not be more than 100%"; + } + } + + function validateBeerName(name: string) { + if (name.length < 3) { + return "That beer's name is too short"; + } + } export const action = async ({ request, @@ -15,36 +33,115 @@ export const action = async ({ typeof name !== "string" || typeof abv !== "number" ) { - throw new Error("Form not submitted correctly."); + return badRequest({ + fieldErrors: null, + fields: null, + formError: "Form not submitted correctly.", + }); } + const fieldErrors = { + abv: validateBeerAbv(abv), + name: validateBeerName(name), + }; const fields = { name, abv }; + if (Object.values(fieldErrors).some(Boolean)) { + return badRequest({ + fieldErrors, + fields, + formError: null, + }); + } const beer = await db.beer.create({ data: fields }); return redirect(`/beers/${beer.id}`); }; export default function NewBeerRoute() { + const actionData = useActionData(); return (

Add your own beer

+ {actionData?.fieldErrors?.name ? ( + + ) : null}
+ {actionData?.fieldErrors?.abv ? ( + + ) : null}
-
); + } + + export function ErrorBoundary() { + return ( +
+ Something unexpected went wrong. Sorry about that. +
+ ); } \ No newline at end of file diff --git a/app/routes/beers.tsx b/app/routes/beers.tsx index b907327..91d6bab 100644 --- a/app/routes/beers.tsx +++ b/app/routes/beers.tsx @@ -1,4 +1,3 @@ -import type { LinksFunction } from "@remix-run/node"; import { json } from "@remix-run/node"; import { Link, @@ -6,13 +5,8 @@ import { useLoaderData, } from "@remix-run/react"; -import stylesUrl from "~/styles/beers.css?url"; import { db } from "~/utils/db.server"; -export const links: LinksFunction = () => [ - { rel: "stylesheet", href: stylesUrl }, -]; - export const loader = async () => { return json({ beerListItems: await db.beer.findMany({ @@ -27,40 +21,42 @@ export default function BeersRoute() { const data = useLoaderData(); return ( -
-
-
-

+
+
+
+

- B - Beers + B + Beers

-
-
-
- Get a random beer -

Here are a few more beers to check out:

-
    - {data.beerListItems.map(({ id, name }) => ( -
  • - {name} -
  • - ))} -
- - Add your own - -
-
- -
+
+
+
+ Get a random beer +

Here are a few more beers to check out:

+
    + {data.beerListItems.map(({ id, name }) => ( +
  • + {name} +
  • + ))} +
+
+ + Add your own + +
+
+
+ +
diff --git a/app/styles/beers.css b/app/styles/beers.css deleted file mode 100644 index 8e524dd..0000000 --- a/app/styles/beers.css +++ /dev/null @@ -1,93 +0,0 @@ -.beers-layout { - display: flex; - flex-direction: column; - min-height: inherit; - } - - .beers-header { - padding-top: 1rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--color-border); - } - - .beers-header .container { - display: flex; - justify-content: space-between; - align-items: center; - } - - .beers-header .home-link { - font-family: var(--font-display); - font-size: 3rem; - } - - .beers-header .home-link a { - color: var(--color-foreground); - } - - .beers-header .home-link a:hover { - text-decoration: none; - } - - .beers-header .logo-medium { - display: none; - } - - .beers-header a:hover { - text-decoration-style: wavy; - text-decoration-thickness: 1px; - } - - .beers-header .user-info { - display: flex; - gap: 1rem; - align-items: center; - white-space: nowrap; - } - - .beers-main { - padding-top: 2rem; - padding-bottom: 2rem; - flex: 1 1 100%; - } - - .beers-main .container { - display: flex; - gap: 1rem; - } - - .beers-list { - max-width: 12rem; - } - - .beers-outlet { - flex: 1; - } - - .beers-footer { - padding-top: 2rem; - padding-bottom: 1rem; - border-top: 1px solid var(--color-border); - } - - @media print, (min-width: 640px) { - .beers-header .logo { - display: none; - } - - .beers-header .logo-medium { - display: block; - } - - .beers-main { - padding-top: 3rem; - padding-bottom: 3rem; - } - } - - @media (max-width: 639px) { - .beers-main .container { - flex-direction: column; - } - } - \ No newline at end of file diff --git a/app/styles/global-large.css b/app/styles/global-large.css deleted file mode 100644 index 8039877..0000000 --- a/app/styles/global-large.css +++ /dev/null @@ -1,25 +0,0 @@ -h1 { - font-size: 3.75rem; - line-height: 1; - } - - h2 { - font-size: 1.875rem; - line-height: 2.25rem; - } - - h3 { - font-size: 1.5rem; - line-height: 2rem; - } - - h4 { - font-size: 1.25rem; - line-height: 1.75rem; - } - - h5 { - font-size: 1.125rem; - line-height: 1.75rem; - } - \ No newline at end of file diff --git a/app/styles/global-medium.css b/app/styles/global-medium.css deleted file mode 100644 index 69a1446..0000000 --- a/app/styles/global-medium.css +++ /dev/null @@ -1,30 +0,0 @@ -h1 { - font-size: 3rem; - line-height: 1; - } - - h2 { - font-size: 2.25rem; - line-height: 2.5rem; - } - - h3 { - font-size: 1.25rem; - line-height: 1.75rem; - } - - h4 { - font-size: 1.125rem; - line-height: 1.75rem; - } - - h5, - h6 { - font-size: 1rem; - line-height: 1.5rem; - } - - .container { - --gutter: 40px; - } - \ No newline at end of file diff --git a/app/styles/global.css b/app/styles/global.css deleted file mode 100644 index 993bcb3..0000000 --- a/app/styles/global.css +++ /dev/null @@ -1,355 +0,0 @@ - - :root { - --hs-links: 48 100%; - --color-foreground: hsl(0, 0%, 100%); - --color-background: hsl(278, 73%, 19%); - --color-links: hsl(var(--hs-links) 50%); - --color-links-hover: hsl(var(--hs-links) 45%); - --color-border: hsl(277, 85%, 38%); - --color-invalid: hsl(356, 100%, 71%); - --gradient-background: radial-gradient( - circle, - rgba(152, 11, 238, 1) 0%, - rgba(118, 15, 181, 1) 35%, - rgba(58, 13, 85, 1) 100% - ); - --font-body: -apple-system, "Segoe UI", Helvetica Neue, Helvetica, - Roboto, Arial, sans-serif, system-ui, "Apple Color Emoji", - "Segoe UI Emoji"; - --font-display: var(--font-body); - } - - html { - box-sizing: border-box; - } - - *, - *::before, - *::after { - box-sizing: inherit; - } - - :-moz-focusring { - outline: auto; - } - - :focus { - outline: var(--color-links) solid 2px; - outline-offset: 2px; - } - - html, - body { - padding: 0; - margin: 0; - color: var(--color-foreground); - background-color: var(--color-background); - } - - [data-light] { - --color-invalid: hsl(356, 70%, 39%); - color: var(--color-background); - background-color: var(--color-foreground); - } - - body { - font-family: var(--font-body); - line-height: 1.5; - background-repeat: no-repeat; - min-height: 100vh; - min-height: calc(100vh - env(safe-area-inset-bottom)); - } - - a { - color: var(--color-links); - text-decoration: none; - } - - a:hover { - color: var(--color-links-hover); - text-decoration: underline; - } - - hr { - display: block; - height: 1px; - border: 0; - background-color: var(--color-border); - margin-top: 2rem; - margin-bottom: 2rem; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--font-display); - margin: 0; - } - - h1 { - font-size: 2.25rem; - line-height: 2.5rem; - } - - h2 { - font-size: 1.5rem; - line-height: 2rem; - } - - h3 { - font-size: 1.25rem; - line-height: 1.75rem; - } - - h4 { - font-size: 1.125rem; - line-height: 1.75rem; - } - - h5, - h6 { - font-size: 0.875rem; - line-height: 1.25rem; - } - - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; - } - - .container { - --gutter: 16px; - width: 1024px; - max-width: calc(100% - var(--gutter) * 2); - margin-right: auto; - margin-left: auto; - } - - /* buttons */ - - .button { - --shadow-color: hsl(var(--hs-links) 30%); - --shadow-size: 3px; - -webkit-appearance: none; - -moz-appearance: none; - cursor: pointer; - appearance: none; - display: inline-flex; - align-items: center; - justify-content: center; - background-color: var(--color-links); - color: var(--color-background); - font-family: var(--font-display); - font-weight: bold; - line-height: 1; - font-size: 1.125rem; - margin: 0; - padding: 0.625em 1em; - border: 0; - border-radius: 4px; - box-shadow: 0 var(--shadow-size) 0 0 var(--shadow-color); - outline-offset: 2px; - transform: translateY(0); - transition: background-color 50ms ease-out, box-shadow - 50ms ease-out, - transform 100ms cubic-bezier(0.3, 0.6, 0.8, 1.25); - } - - .button:hover { - --raise: 1px; - color: var(--color-background); - text-decoration: none; - box-shadow: 0 calc(var(--shadow-size) + var(--raise)) 0 0 var( - --shadow-color - ); - transform: translateY(calc(var(--raise) * -1)); - } - - .button:active { - --press: 1px; - box-shadow: 0 calc(var(--shadow-size) - var(--press)) 0 0 var( - --shadow-color - ); - transform: translateY(var(--press)); - background-color: var(--color-links-hover); - } - - .button[disabled], - .button[aria-disabled="true"] { - transform: translateY(0); - pointer-events: none; - opacity: 0.7; - } - - .button:focus:not(:focus-visible) { - outline: none; - } - - /* forms */ - - form { - display: flex; - flex-direction: column; - gap: 1rem; - width: 100%; - } - - fieldset { - margin: 0; - padding: 0; - border: 0; - } - - legend { - display: block; - max-width: 100%; - margin-bottom: 0.5rem; - color: inherit; - white-space: normal; - } - - [type="text"], - [type="password"], - [type="date"], - [type="datetime"], - [type="datetime-local"], - [type="month"], - [type="week"], - [type="email"], - [type="number"], - [type="search"], - [type="tel"], - [type="time"], - [type="url"], - [type="color"], - textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - display: block; - display: flex; - align-items: center; - width: 100%; - height: 2.5rem; - margin: 0; - padding: 0.5rem 0.75rem; - border: 1px solid var(--color-border); - border-radius: 4px; - background-color: hsl(0 0% 100% / 10%); - background-blend-mode: luminosity; - box-shadow: none; - font-family: var(--font-body); - font-size: 1rem; - font-weight: normal; - line-height: 1.5; - color: var(--color-foreground); - transition: box-shadow 200ms, border-color 50ms ease-out, - background-color 50ms ease-out, color 50ms ease-out; - } - - [data-light] [type="text"], - [data-light] [type="password"], - [data-light] [type="date"], - [data-light] [type="datetime"], - [data-light] [type="datetime-local"], - [data-light] [type="month"], - [data-light] [type="week"], - [data-light] [type="email"], - [data-light] [type="number"], - [data-light] [type="search"], - [data-light] [type="tel"], - [data-light] [type="time"], - [data-light] [type="url"], - [data-light] [type="color"], - [data-light] textarea { - color: var(--color-background); - background-color: hsl(0 0% 0% / 10%); - } - - [type="text"][aria-invalid="true"], - [type="password"][aria-invalid="true"], - [type="date"][aria-invalid="true"], - [type="datetime"][aria-invalid="true"], - [type="datetime-local"][aria-invalid="true"], - [type="month"][aria-invalid="true"], - [type="week"][aria-invalid="true"], - [type="email"][aria-invalid="true"], - [type="number"][aria-invalid="true"], - [type="search"][aria-invalid="true"], - [type="tel"][aria-invalid="true"], - [type="time"][aria-invalid="true"], - [type="url"][aria-invalid="true"], - [type="color"][aria-invalid="true"], - textarea[aria-invalid="true"] { - border-color: var(--color-invalid); - } - - textarea { - display: block; - min-height: 50px; - max-width: 100%; - } - - textarea[rows] { - height: auto; - } - - input:disabled, - input[readonly], - textarea:disabled, - textarea[readonly] { - opacity: 0.7; - cursor: not-allowed; - } - - [type="file"], - [type="checkbox"], - [type="radio"] { - margin: 0; - } - - [type="file"] { - width: 100%; - } - - label { - margin: 0; - } - - [type="checkbox"] + label, - [type="radio"] + label { - margin-left: 0.5rem; - } - - label > [type="checkbox"], - label > [type="radio"] { - margin-right: 0.5rem; - } - - ::placeholder { - color: hsl(0 0% 100% / 65%); - } - - .form-validation-error { - margin: 0; - margin-top: 0.25em; - color: var(--color-invalid); - font-size: 0.8rem; - } - - .error-container { - background-color: hsla(356, 77%, 59%, 0.747); - border-radius: 0.25rem; - padding: 0.5rem 1rem; - } - \ No newline at end of file diff --git a/app/styles/index.css b/app/styles/index.css deleted file mode 100644 index 2f15400..0000000 --- a/app/styles/index.css +++ /dev/null @@ -1,76 +0,0 @@ -/* - * when the user visits this page, this style will apply, when they leave, it - * will get unloaded, so don't worry so much about conflicting styles between - * pages! - */ - - body { - background-image: var(--gradient-background); - } - - .container { - min-height: inherit; - } - - .container, - .content { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - } - - .content { - padding-top: 3rem; - padding-bottom: 3rem; - } - - h1 { - margin: 0; - text-shadow: 0 3px 0 rgba(0, 0, 0, 0.75); - text-align: center; - line-height: 0.5; - } - - h1 span { - display: block; - font-size: 4.5rem; - line-height: 1; - text-transform: uppercase; - text-shadow: 0 0.2em 0.5em rgba(0, 0, 0, 0.5), 0 5px 0 - rgba(0, 0, 0, 0.75); - } - - nav ul { - list-style: none; - margin: 0; - padding: 0; - display: flex; - gap: 1rem; - font-family: var(--font-display); - font-size: 1.125rem; - line-height: 1; - } - - nav ul a:hover { - text-decoration-style: wavy; - text-decoration-thickness: 1px; - } - - @media print, (min-width: 640px) { - h1 span { - font-size: 6rem; - } - - nav ul { - font-size: 1.25rem; - gap: 1.5rem; - } - } - - @media screen and (min-width: 1024px) { - h1 span { - font-size: 8rem; - } - } - \ No newline at end of file diff --git a/app/utils/request.server.ts b/app/utils/request.server.ts new file mode 100644 index 0000000..5100e1a --- /dev/null +++ b/app/utils/request.server.ts @@ -0,0 +1,8 @@ +import { json } from "@remix-run/node"; + +/** + * This helper function helps us to return the accurate HTTP status, + * 400 Bad Request, to the client. + */ +export const badRequest = (data: T) => + json(data, { status: 400 }); diff --git a/package-lock.json b/package-lock.json index edfa6c4..39fc83a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,15 +10,20 @@ "@remix-run/node": "^2.9.1", "@remix-run/react": "^2.9.1", "@remix-run/serve": "^2.9.1", + "bootstrap": "^5.3.3", "isbot": "^4.1.0", "react": "^18.2.0", + "react-bootstrap": "^2.10.2", "react-dom": "^18.2.0", + "react-router-bootstrap": "^0.26.2", "tsx": "^4.7.3" }, "devDependencies": { "@remix-run/dev": "^2.9.1", + "@tailwindcss/forms": "^0.5.7", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", + "@types/react-router-bootstrap": "^0.26.6", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "eslint": "^8.38.0", @@ -45,6 +50,19 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -608,7 +626,6 @@ "version": "7.24.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1455,6 +1472,15 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@prisma/client": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.13.0.tgz", @@ -1517,6 +1543,20 @@ "@prisma/debug": "5.13.0" } }, + "node_modules/@react-aria/ssr": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.2.tgz", + "integrity": "sha512-0gKkgDYdnq1w+ey8KzG9l+H5Z821qh9vVjztk55rUg71vTk/Eaebeir+WtzcLLwTjw3m/asIjx8Y59y1lJZhBw==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@remix-run/dev": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/@remix-run/dev/-/dev-2.9.1.tgz", @@ -1780,6 +1820,45 @@ "web-streams-polyfill": "^3.1.1" } }, + "node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.8.tgz", + "integrity": "sha512-6ndCv3oZ7r9vuP1Ok9KH55TM1/UkdBnP/fSraW0DFDMbPMzWKhVKeFAIEUCRCSdzayjZDcFYK6xbMlipN9dmMA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.16.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz", @@ -1988,6 +2067,26 @@ "win32" ] }, + "node_modules/@swc/helpers": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.11.tgz", + "integrity": "sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", + "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "dev": true, + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -2112,14 +2211,12 @@ "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { "version": "18.3.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.0.tgz", "integrity": "sha512-DiUcKjzE6soLyln8NNZmyhcQjVv+WsUIFSqetMN0p8927OztKT4VTfFTqsbAi5oAGIcgOmOajlfBqyptDDjZRw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2134,6 +2231,23 @@ "@types/react": "*" } }, + "node_modules/@types/react-router-bootstrap": { + "version": "0.26.6", + "resolved": "https://registry.npmjs.org/@types/react-router-bootstrap/-/react-router-bootstrap-0.26.6.tgz", + "integrity": "sha512-uqDwCdHp/qkXUPd8iOgZ92HQnTfyjpIwqgvbenW5SejrN3l6bkcObZQ/kkirHk39AQK4MJVpQBvcSu/SE6Crnw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -2146,6 +2260,11 @@ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", "dev": true }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -2531,6 +2650,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "peer": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2897,6 +3023,24 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -3072,6 +3216,16 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001612", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", @@ -3190,6 +3344,11 @@ "node": ">=10" } }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3260,6 +3419,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -3443,8 +3612,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -3619,7 +3787,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -3633,6 +3800,13 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "peer": true + }, "node_modules/diff": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", @@ -3654,6 +3828,13 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "peer": true + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3666,6 +3847,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -5563,6 +5753,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6139,6 +6337,16 @@ "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "dev": true }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6295,6 +6503,13 @@ "url": "https://github.com/sponsors/antonk52" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, "node_modules/loader-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", @@ -7327,6 +7542,15 @@ "node": ">=6" } }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, "node_modules/minimatch": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", @@ -7579,6 +7803,18 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "peer": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -7707,11 +7943,20 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -8145,6 +8390,26 @@ "node": ">=0.10" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-types": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.0.tgz", @@ -8204,6 +8469,44 @@ "postcss": "^8.2.15" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "peer": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "peer": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, "node_modules/postcss-load-config": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", @@ -8317,6 +8620,26 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "peer": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.16", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", @@ -8429,13 +8752,24 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/property-information": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", @@ -8563,6 +8897,35 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.2.tgz", + "integrity": "sha512-UvB7mRqQjivdZNxJNEA2yOQRB7L9N43nBnKc33K47+cH90/ujmnMwatTCwQLu83gLhrzAl8fsa6Lqig/KLghaA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.8", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.3.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.0.tgz", @@ -8578,8 +8941,12 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-refresh": { "version": "0.14.1", @@ -8604,6 +8971,18 @@ "react": ">=16.8" } }, + "node_modules/react-router-bootstrap": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/react-router-bootstrap/-/react-router-bootstrap-0.26.2.tgz", + "integrity": "sha512-YlpI9Xi+Uqp6zFAUO8D/wu6P8mr1ujqq+0V5MhJG1kx9dr/95fAMoGk4J+/CsysOkwtR3tYSac4DDWmHwXvC8w==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16.13.1", + "react-router-dom": ">=6.0.0" + } + }, "node_modules/react-router-dom": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz", @@ -8620,6 +8999,31 @@ "react-dom": ">=16.8" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "peer": true, + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -8669,8 +9073,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", @@ -9582,6 +9985,29 @@ "inline-style-parser": "0.1.1" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -9606,6 +10032,67 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwindcss": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", + "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", + "dev": true, + "peer": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -9721,6 +10208,29 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "peer": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "peer": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -9827,6 +10337,13 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "peer": true + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -9925,6 +10442,11 @@ "node": ">=6" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/tsx": { "version": "4.7.3", "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.3.tgz", @@ -10473,6 +10995,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/undici": { "version": "6.14.1", "resolved": "https://registry.npmjs.org/undici/-/undici-6.14.1.tgz", @@ -11301,6 +11837,14 @@ "@esbuild/win32-x64": "0.20.2" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index 841d16e..8898bcb 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,20 @@ "@remix-run/node": "^2.9.1", "@remix-run/react": "^2.9.1", "@remix-run/serve": "^2.9.1", + "bootstrap": "^5.3.3", "isbot": "^4.1.0", "react": "^18.2.0", + "react-bootstrap": "^2.10.2", "react-dom": "^18.2.0", + "react-router-bootstrap": "^0.26.2", "tsx": "^4.7.3" }, "devDependencies": { "@remix-run/dev": "^2.9.1", + "@tailwindcss/forms": "^0.5.7", "@types/react": "^18.2.20", "@types/react-dom": "^18.2.7", + "@types/react-router-bootstrap": "^0.26.6", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", "eslint": "^8.38.0", @@ -43,4 +48,4 @@ "prisma": { "seed": "tsx prisma/seed.ts" } -} \ No newline at end of file +}