Automatic data reload, scroll restoration and missing drink pages

This commit is contained in:
2024-05-19 14:27:13 +02:00
parent 9f922bb2e0
commit fa30304a27
11 changed files with 227 additions and 104 deletions
+3 -1
View File
@@ -6,7 +6,8 @@ import {
useRouteError,
isRouteErrorResponse,
json,
useLoaderData
useLoaderData,
ScrollRestoration
} from "@remix-run/react";
import type { PropsWithChildren } from "react";
@@ -37,6 +38,7 @@ function Document({
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
@@ -0,0 +1,71 @@
import type {
LoaderFunctionArgs
} from "@remix-run/node";
import { json } from "@remix-run/node";
import {
useLoaderData,
useParams,
isRouteErrorResponse,
useRouteError } from "@remix-run/react";
import DrinkPage from "~/components/cards/drink.page";
import { db } from "~/utils/db.server";
import { enhance } from "@zenstackhq/runtime";
import { findIdFromSlug } from "~/models/drinks.server";
export const loader = async ({
params,
}: LoaderFunctionArgs) => {
const dbe = enhance(db);
const cocktailid = await findIdFromSlug(params.cocktailslug);
const cocktail = await dbe.cocktail.findUnique({
where: { id: cocktailid },
include: { manufacturer: true, containers: { include: { section: true}}}
});
if (!cocktail) {
throw new Response("Cocktail not found.", {
status: 404,
});
}
return json({ cocktail });
};
export default function CocktailRoute() {
const data = useLoaderData<typeof loader>();
return (
<DrinkPage drink={data.cocktail}></DrinkPage>
);
}
export function ErrorBoundary() {
const { cocktailid } = useParams();
const error = useRouteError();
if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
if (error.status === 404) {
return (
<div className="error-container">
Huh? What the heck is "{cocktailid}"?
</div>
);
}
}
return (
<div className="error-container">
There was an error loading cocktail by the id "{cocktailid}".
Sorry.
</div>
);
}
+15 -1
View File
@@ -1,5 +1,6 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { Outlet, json, useLoaderData } from "@remix-run/react";
import { Outlet, json, useLoaderData, useRevalidator } from "@remix-run/react";
import { useEffect } from "react";
import { getSession } from "~/auth/session";
import Header, { HeaderData } from "~/components/header";
@@ -20,8 +21,21 @@ export async function loader({
return json({data});
}
export default function InventoryLayout() {
let data = useLoaderData<typeof loader>();
let revalidator = useRevalidator();
useEffect(() => {
const timer = setTimeout(() => {
if (revalidator.state === "idle") {
revalidator.revalidate();
}
}, 5000);
return () => clearTimeout(timer);
});
return (
<>
+1 -3
View File
@@ -31,9 +31,7 @@ export default function BeersRoute() {
const [searchParams, setSearchParams] = useSearchParams();
function CustomToggle({ children, eventKey } : any) {
const decoratedOnClick = useAccordionButton(eventKey, () =>
console.log('totally custom!'),
);
const decoratedOnClick = useAccordionButton(eventKey, () => {});
return (
<Button
+47 -49
View File
@@ -1,73 +1,71 @@
import type {
LoaderFunctionArgs
LoaderFunctionArgs
} from "@remix-run/node";
import { json } from "@remix-run/node";
import {
useLoaderData,
useParams,
isRouteErrorResponse,
useRouteError } from "@remix-run/react";
import DrinkCard from "~/components/cards/drink.card";
useLoaderData,
useParams,
isRouteErrorResponse,
useRouteError } from "@remix-run/react";
import DrinkPage from "~/components/cards/drink.page";
import { db } from "~/utils/db.server";
import { enhance } from "@zenstackhq/runtime";
import { findIdFromSlug } from "~/models/drinks.server";
export const loader = async ({
params,
params,
}: LoaderFunctionArgs) => {
const dbe = enhance(db);
const dbe = enhance(db);
const sodaid = await findIdFromSlug(params.sodaslug);
const sodaid = await findIdFromSlug(params.sodaslug);
const soda = await dbe.soda.findUnique({
include: { manufacturer: true, containers: true},
where: { id: sodaid }
const soda = await dbe.soda.findUnique({
where: { id: sodaid },
include: { manufacturer: true, containers: { include: { section: true}}}
});
if (!soda) {
throw new Response("Soda not found.", {
status: 404,
});
if (!soda) {
throw new Response("Soda not found.", {
status: 404,
});
}
return json({ soda });
}
return json({ soda });
};
export default function SodaRoute() {
const data = useLoaderData<typeof loader>();
const data = useLoaderData<typeof loader>();
return (
<div>
<DrinkCard drink={data.soda}></DrinkCard>
</div>
);
return (
<DrinkPage drink={data.soda}></DrinkPage>
);
}
export function ErrorBoundary() {
const { sodaid } = useParams();
const error = useRouteError();
const { sodaid } = useParams();
const error = useRouteError();
if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
if (error.status === 404) {
return (
<div className="error-container">
Huh? What the heck is "{sodaid}"?
</div>
);
}
if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
if (error.status === 404) {
return (
<div className="error-container">
Huh? What the heck is "{sodaid}"?
</div>
);
}
}
return (
<div className="error-container">
There was an error loading soda by the id "{sodaid}".
Sorry.
</div>
);
}
return (
<div className="error-container">
There was an error loading soda by the id "{sodaid}".
Sorry.
</div>
);
}
+47 -49
View File
@@ -1,73 +1,71 @@
import type {
LoaderFunctionArgs
LoaderFunctionArgs
} from "@remix-run/node";
import { json } from "@remix-run/node";
import {
useLoaderData,
useParams,
isRouteErrorResponse,
useRouteError } from "@remix-run/react";
import DrinkCard from "~/components/cards/drink.card";
useLoaderData,
useParams,
isRouteErrorResponse,
useRouteError } from "@remix-run/react";
import DrinkPage from "~/components/cards/drink.page";
import { db } from "~/utils/db.server";
import { enhance } from "@zenstackhq/runtime";
import { findIdFromSlug } from "~/models/drinks.server";
export const loader = async ({
params,
params,
}: LoaderFunctionArgs) => {
const dbe = enhance(db);
const dbe = enhance(db);
const wineid = await findIdFromSlug(params.wineslug);
const wineid = await findIdFromSlug(params.wineslug);
const wine = await dbe.wine.findUnique({
include: { style: true, manufacturer: true, containers: true},
where: { id: wineid },
const wine = await dbe.wine.findUnique({
where: { id: wineid },
include: { style: true, manufacturer: true, containers: { include: { section: true}}}
});
if (!wine) {
throw new Response("Wine not found.", {
status: 404,
});
if (!wine) {
throw new Response("Wine not found.", {
status: 404,
});
}
return json({ wine });
}
return json({ wine });
};
export default function WineRoute() {
const data = useLoaderData<typeof loader>();
const data = useLoaderData<typeof loader>();
return (
<div>
<DrinkCard drink={data.wine}></DrinkCard>
</div>
);
return (
<DrinkPage drink={data.wine}></DrinkPage>
);
}
export function ErrorBoundary() {
const { wineid } = useParams();
const error = useRouteError();
const { wineid } = useParams();
const error = useRouteError();
if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
if (error.status === 404) {
return (
<div className="error-container">
Huh? What the heck is "{wineid}"?
</div>
);
}
if (isRouteErrorResponse(error)) {
if (error.status === 400) {
return (
<div className="error-container">
What you're trying to do is not allowed.
</div>
);
}
if (error.status === 404) {
return (
<div className="error-container">
Huh? What the heck is "{wineid}"?
</div>
);
}
}
return (
<div className="error-container">
There was an error loading wine by the id "{wineid}".
Sorry.
</div>
);
}
return (
<div className="error-container">
There was an error loading wine by the id "{wineid}".
Sorry.
</div>
);
}
+11
View File
@@ -0,0 +1,11 @@
import { LoaderFunctionArgs, redirect, json } from "@remix-run/node";
import { enhance } from "~/utils/db.server";
export async function loader({
request,
}: LoaderFunctionArgs) {
const { dbe } = await enhance(request);
const numCheckouts = await dbe.history.count({});
return json({checkouts: numCheckouts});
}
+14 -1
View File
@@ -1,5 +1,6 @@
import { LoaderFunctionArgs } from "@remix-run/node";
import { Outlet, json, redirect, useLoaderData } from "@remix-run/react";
import { Outlet, json, redirect, useLoaderData, useRevalidator } from "@remix-run/react";
import { useEffect } from "react";
import { enhance } from "~/utils/db.server";
export async function loader({
@@ -17,6 +18,18 @@ export async function loader({
export default function InventoryLayout() {
useLoaderData<typeof loader>();
let revalidator = useRevalidator();
useEffect(() => {
const timer = setTimeout(() => {
if (revalidator.state === "idle") {
revalidator.revalidate();
}
}, 10000);
return () => clearTimeout(timer);
});
return (
<>
<header>
+13
View File
@@ -10,6 +10,7 @@
"@remix-run/node": "^2.9.1",
"@remix-run/react": "^2.9.1",
"@remix-run/serve": "^2.9.1",
"@uidotdev/usehooks": "^2.4.1",
"@zenstackhq/runtime": "2.0.3",
"bootstrap": "^5.3.3",
"isbot": "^4.1.0",
@@ -2692,6 +2693,18 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@uidotdev/usehooks": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@uidotdev/usehooks/-/usehooks-2.4.1.tgz",
"integrity": "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==",
"engines": {
"node": ">=16"
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+1
View File
@@ -15,6 +15,7 @@
"@remix-run/node": "^2.9.1",
"@remix-run/react": "^2.9.1",
"@remix-run/serve": "^2.9.1",
"@uidotdev/usehooks": "^2.4.1",
"@zenstackhq/runtime": "2.0.3",
"bootstrap": "^5.3.3",
"isbot": "^4.1.0",
+4
View File
@@ -12,12 +12,16 @@ export default defineConfig({
// Index
route("/", "routes/_index.tsx", { index: true });
// Resources
route("/resource/checkouts", "routes/resource/checkouts.tsx");
// Inventory
route("inventory", "routes/inventory/layout.tsx", () => {
route("", "routes/inventory/route.tsx", { index: true });
route("beer/:beerslug", "routes/inventory/beer.$beerslug.tsx");
route("wine/:wineslug", "routes/inventory/wine.$wineslug.tsx");
route("soda/:sodaslug", "routes/inventory/soda.$sodaslug.tsx");
route("cocktail/:cocktailslug", "routes/inventory/cocktail.$cocktailslug.tsx");
route("stats", "routes/inventory/stats/route.tsx");