metadata and titles
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { HttpStatusCode } from "@solidjs/start";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { createEffect, createSignal, For } from "solid-js";
|
||||
@@ -47,7 +47,11 @@ export default function Page_401() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>401 - Unauthorized</Title>
|
||||
<Title>401 Unauthorized | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="401 - Unauthorized access. Please log in to access this page."
|
||||
/>
|
||||
<HttpStatusCode code={401} />
|
||||
<div class="relative min-h-screen w-full overflow-hidden bg-gradient-to-br from-slate-900 via-amber-950/20 to-slate-900 dark:from-black dark:via-amber-950/30 dark:to-black">
|
||||
{/* Animated particle background */}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { HttpStatusCode } from "@solidjs/start";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { createEffect, createSignal, For } from "solid-js";
|
||||
@@ -43,7 +43,11 @@ export default function NotFound() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>404 - Not Found</Title>
|
||||
<Title>404 Not Found | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="404 - Page not found. The page you're looking for doesn't exist."
|
||||
/>
|
||||
<HttpStatusCode code={404} />
|
||||
<div class="relative min-h-screen w-full overflow-hidden bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 dark:from-black dark:via-slate-900 dark:to-black">
|
||||
{/* Animated particle background */}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<>
|
||||
<Title>About | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Learn more about Michael Freno - Software Engineer, game developer, and open source contributor."
|
||||
/>
|
||||
|
||||
<main>
|
||||
<Title>About</Title>
|
||||
<h1>About</h1>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createSignal, createEffect, Show, onMount } from "solid-js";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { useNavigate, cache, redirect } from "@solidjs/router";
|
||||
import { getEvent } from "vinxi/http";
|
||||
import Eye from "~/components/icons/Eye";
|
||||
@@ -453,6 +454,13 @@ export default function AccountPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Account | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Manage your account settings, update profile information, and configure preferences."
|
||||
/>
|
||||
|
||||
<div class="bg-base mx-8 min-h-screen md:mx-24 lg:mx-36">
|
||||
<div class="pt-24">
|
||||
<Show
|
||||
@@ -507,7 +515,9 @@ export default function AccountPage() {
|
||||
: "bg-blue hover:brightness-125 active:scale-90"
|
||||
} mt-2 flex w-full justify-center rounded px-4 py-2 text-base transition-all duration-300 ease-out`}
|
||||
>
|
||||
{profileImageSetLoading() ? "Uploading..." : "Set Image"}
|
||||
{profileImageSetLoading()
|
||||
? "Uploading..."
|
||||
: "Set Image"}
|
||||
</button>
|
||||
</form>
|
||||
<Show when={showImageSuccess()}>
|
||||
@@ -618,7 +628,8 @@ export default function AccountPage() {
|
||||
/>
|
||||
<span class="bar"></span>
|
||||
<label class="underlinedInputLabel">
|
||||
Set {currentUser().displayName ? "New " : ""}Display Name
|
||||
Set {currentUser().displayName ? "New " : ""}Display
|
||||
Name
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
@@ -631,7 +642,9 @@ export default function AccountPage() {
|
||||
: "bg-blue hover:brightness-125 active:scale-90"
|
||||
} mt-2 flex justify-center rounded px-4 py-2 text-base transition-all duration-300 ease-out`}
|
||||
>
|
||||
{displayNameButtonLoading() ? "Submitting..." : "Submit"}
|
||||
{displayNameButtonLoading()
|
||||
? "Submitting..."
|
||||
: "Submit"}
|
||||
</button>
|
||||
</div>
|
||||
<Show when={showDisplayNameSuccess()}>
|
||||
@@ -673,7 +686,10 @@ export default function AccountPage() {
|
||||
}
|
||||
class="text-subtext0 absolute top-2 right-0 transition-all hover:brightness-125"
|
||||
>
|
||||
<Show when={showOldPasswordInput()} fallback={<Eye />}>
|
||||
<Show
|
||||
when={showOldPasswordInput()}
|
||||
fallback={<Eye />}
|
||||
>
|
||||
<EyeSlash />
|
||||
</Show>
|
||||
</button>
|
||||
@@ -695,7 +711,9 @@ export default function AccountPage() {
|
||||
<label class="underlinedInputLabel">New Password</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPasswordInput(!showPasswordInput())}
|
||||
onClick={() =>
|
||||
setShowPasswordInput(!showPasswordInput())
|
||||
}
|
||||
class="text-subtext0 absolute top-2 right-0 transition-all hover:brightness-125"
|
||||
>
|
||||
<Show when={showPasswordInput()} fallback={<Eye />}>
|
||||
@@ -837,5 +855,6 @@ export default function AccountPage() {
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Show, Suspense, For } from "solid-js";
|
||||
import { useParams, A, Navigate, query } from "@solidjs/router";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { createAsync } from "@solidjs/router";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import SessionDependantLike from "~/components/blog/SessionDependantLike";
|
||||
@@ -159,6 +159,13 @@ export default function PostPage() {
|
||||
<Title>
|
||||
{p().title.replaceAll("_", " ")} | Michael Freno
|
||||
</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content={
|
||||
p().subtitle ||
|
||||
`Read ${p().title.replaceAll("_", " ")} by Michael Freno on the freno.me blog.`
|
||||
}
|
||||
/>
|
||||
|
||||
<div class="relative overflow-x-hidden">
|
||||
{/* Fixed banner image background */}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Show, createSignal, createEffect, onCleanup } from "solid-js";
|
||||
import { useNavigate, query } from "@solidjs/router";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { createAsync } from "@solidjs/router";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import { api } from "~/lib/api";
|
||||
@@ -195,6 +195,10 @@ export default function CreatePost() {
|
||||
return (
|
||||
<>
|
||||
<Title>Create Blog Post | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Create a new blog post with rich text editing, image uploads, and tag management."
|
||||
/>
|
||||
|
||||
<Show
|
||||
when={authState()?.privilegeLevel === "admin"}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Show, createSignal, createEffect, onCleanup } from "solid-js";
|
||||
import { useParams, useNavigate, query } from "@solidjs/router";
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { createAsync } from "@solidjs/router";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import { api } from "~/lib/api";
|
||||
@@ -241,6 +241,10 @@ export default function EditPost() {
|
||||
return (
|
||||
<>
|
||||
<Title>Edit Post | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Edit your blog post with rich text editing, image management, and tag updates."
|
||||
/>
|
||||
|
||||
<Show
|
||||
when={data()?.privilegeLevel === "admin"}
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import DeletionForm from "~/components/DeletionForm";
|
||||
|
||||
export default function LifeAndLinageDeletionForm() {
|
||||
return (
|
||||
<>
|
||||
<Title>Account Deletion - Life and Lineage | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Request account deletion for Life and Lineage. Remove all your data from our system with a 24-hour grace period."
|
||||
/>
|
||||
<div class="pt-20">
|
||||
<div class="mx-auto p-4 md:p-6 lg:p-12">
|
||||
<div class="w-full justify-center text-text">
|
||||
<div class="text-text w-full justify-center">
|
||||
<div class="text-xl">
|
||||
<em>What will happen</em>:
|
||||
</div>
|
||||
Once you send, if a match to the email provided is found in our
|
||||
system, a 24hr grace period is started where you can request a
|
||||
cancellation of the account deletion. Once the grace period ends, the
|
||||
account's entry in our central database will be completely removed,
|
||||
and your individual database storing your remote saves will also be
|
||||
deleted. No data related to the account is retained in any way.
|
||||
cancellation of the account deletion. Once the grace period ends,
|
||||
the account's entry in our central database will be completely
|
||||
removed, and your individual database storing your remote saves will
|
||||
also be deleted. No data related to the account is retained in any
|
||||
way.
|
||||
</div>
|
||||
|
||||
<DeletionForm />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { A } from "@solidjs/router";
|
||||
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
|
||||
import GitHub from "~/components/icons/GitHub";
|
||||
@@ -21,6 +22,13 @@ export default function DownloadsPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Downloads | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Download Life and Lineage, Shapes with Abigail, and Cork for macOS. Available on iOS, Android, and macOS."
|
||||
/>
|
||||
|
||||
<div class="bg-base min-h-screen pt-[15vh] pb-12">
|
||||
<div class="text-text text-center text-3xl tracking-widest">
|
||||
Downloads
|
||||
@@ -163,5 +171,6 @@ export default function DownloadsPage() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
|
||||
import { Typewriter } from "~/components/Typewriter";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Title>Home | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Michael Freno - Software Engineer based in Brooklyn, NY. Passionate about dev tooling, game development, and open source software."
|
||||
/>
|
||||
|
||||
<main class="flex h-full flex-col gap-8 p-4 text-xl">
|
||||
<div class="flex-1">
|
||||
<Typewriter speed={30} keepAlive={2000}>
|
||||
@@ -151,5 +159,6 @@ export default function Home() {
|
||||
</Typewriter>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
cache,
|
||||
redirect
|
||||
} from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { getEvent } from "vinxi/http";
|
||||
import GoogleLogo from "~/components/icons/GoogleLogo";
|
||||
import GitHub from "~/components/icons/GitHub";
|
||||
@@ -320,6 +321,12 @@ export default function LoginPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Login | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Sign in to your account or register for a new account to access personalized features and manage your profile."
|
||||
/>
|
||||
<div class="flex h-dvh flex-row justify-evenly">
|
||||
{/* Logo section - hidden on mobile */}
|
||||
{/* <div class="hidden md:flex">
|
||||
@@ -639,5 +646,6 @@ export default function LoginPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createSignal, createEffect, Show } from "solid-js";
|
||||
import { A, useNavigate, useSearchParams } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
||||
import Eye from "~/components/icons/Eye";
|
||||
import EyeSlash from "~/components/icons/EyeSlash";
|
||||
@@ -13,8 +14,10 @@ export default function PasswordResetPage() {
|
||||
const [passwordBlurred, setPasswordBlurred] = createSignal(false);
|
||||
const [passwordChangeLoading, setPasswordChangeLoading] = createSignal(false);
|
||||
const [passwordsMatch, setPasswordsMatch] = createSignal(false);
|
||||
const [showPasswordLengthWarning, setShowPasswordLengthWarning] = createSignal(false);
|
||||
const [passwordLengthSufficient, setPasswordLengthSufficient] = createSignal(false);
|
||||
const [showPasswordLengthWarning, setShowPasswordLengthWarning] =
|
||||
createSignal(false);
|
||||
const [passwordLengthSufficient, setPasswordLengthSufficient] =
|
||||
createSignal(false);
|
||||
const [showRequestNewEmail, setShowRequestNewEmail] = createSignal(false);
|
||||
const [countDown, setCountDown] = createSignal(false);
|
||||
const [error, setError] = createSignal("");
|
||||
@@ -70,8 +73,8 @@ export default function PasswordResetPage() {
|
||||
body: JSON.stringify({
|
||||
token: token,
|
||||
newPassword,
|
||||
newPasswordConfirmation: newPasswordConf,
|
||||
}),
|
||||
newPasswordConfirmation: newPasswordConf
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
@@ -158,14 +161,26 @@ export default function PasswordResetPage() {
|
||||
}
|
||||
return (
|
||||
<div class="timer text-center">
|
||||
<div class="text-sm text-slate-700 dark:text-slate-300">Change Successful!</div>
|
||||
<div class="value py-1 text-3xl text-blue-500 dark:text-blue-400">{timeRemaining}</div>
|
||||
<div class="text-sm text-slate-700 dark:text-slate-300">Redirecting...</div>
|
||||
<div class="text-sm text-slate-700 dark:text-slate-300">
|
||||
Change Successful!
|
||||
</div>
|
||||
<div class="value py-1 text-3xl text-blue-500 dark:text-blue-400">
|
||||
{timeRemaining}
|
||||
</div>
|
||||
<div class="text-sm text-slate-700 dark:text-slate-300">
|
||||
Redirecting...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Reset Password | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Set a new password for your account to regain access to your profile and personalized features."
|
||||
/>
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
|
||||
<div class="pt-24 text-center text-xl font-semibold text-slate-800 dark:text-slate-100">
|
||||
Set New Password
|
||||
@@ -175,9 +190,9 @@ export default function PasswordResetPage() {
|
||||
onSubmit={(e) => setNewPasswordTrigger(e)}
|
||||
class="mt-4 flex w-full justify-center"
|
||||
>
|
||||
<div class="flex flex-col justify-center max-w-md w-full px-4">
|
||||
<div class="flex w-full max-w-md flex-col justify-center px-4">
|
||||
{/* New Password Input */}
|
||||
<div class="input-group mx-4 relative">
|
||||
<div class="input-group relative mx-4">
|
||||
<input
|
||||
ref={newPasswordRef}
|
||||
name="newPassword"
|
||||
@@ -194,7 +209,7 @@ export default function PasswordResetPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPasswordInput(!showPasswordInput())}
|
||||
class="absolute right-0 top-2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
class="absolute top-2 right-0 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
>
|
||||
<Show when={showPasswordInput()} fallback={<Eye />}>
|
||||
<EyeSlash />
|
||||
@@ -205,14 +220,14 @@ export default function PasswordResetPage() {
|
||||
{/* Password Length Warning */}
|
||||
<div
|
||||
class={`${
|
||||
showPasswordLengthWarning() ? "" : "select-none opacity-0"
|
||||
} transition-opacity text-center text-red-500 text-sm duration-200 ease-in-out mt-2`}
|
||||
showPasswordLengthWarning() ? "" : "opacity-0 select-none"
|
||||
} mt-2 text-center text-sm text-red-500 transition-opacity duration-200 ease-in-out`}
|
||||
>
|
||||
Password too short! Min Length: 8
|
||||
</div>
|
||||
|
||||
{/* Password Confirmation Input */}
|
||||
<div class="input-group mx-4 mt-6 relative">
|
||||
<div class="input-group relative mx-4 mt-6">
|
||||
<input
|
||||
ref={newPasswordConfRef}
|
||||
name="newPasswordConf"
|
||||
@@ -227,8 +242,10 @@ export default function PasswordResetPage() {
|
||||
<label class="underlinedInputLabel">Password Confirmation</label>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPasswordConfInput(!showPasswordConfInput())}
|
||||
class="absolute right-0 top-2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
onClick={() =>
|
||||
setShowPasswordConfInput(!showPasswordConfInput())
|
||||
}
|
||||
class="absolute top-2 right-0 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
|
||||
>
|
||||
<Show when={showPasswordConfInput()} fallback={<Eye />}>
|
||||
<EyeSlash />
|
||||
@@ -244,8 +261,8 @@ export default function PasswordResetPage() {
|
||||
newPasswordConfRef &&
|
||||
newPasswordConfRef.value.length >= 6
|
||||
? ""
|
||||
: "select-none opacity-0"
|
||||
} transition-opacity text-center text-red-500 text-sm duration-200 ease-in-out mt-2`}
|
||||
: "opacity-0 select-none"
|
||||
} mt-2 text-center text-sm text-red-500 transition-opacity duration-200 ease-in-out`}
|
||||
>
|
||||
Passwords do not match!
|
||||
</div>
|
||||
@@ -259,9 +276,9 @@ export default function PasswordResetPage() {
|
||||
disabled={passwordChangeLoading() || !passwordsMatch()}
|
||||
class={`${
|
||||
passwordChangeLoading() || !passwordsMatch()
|
||||
? "bg-zinc-400 cursor-not-allowed"
|
||||
? "cursor-not-allowed bg-zinc-400"
|
||||
: "bg-blue-400 hover:bg-blue-500 active:scale-90 dark:bg-blue-600 dark:hover:bg-blue-700"
|
||||
} flex justify-center rounded transition-all duration-300 ease-out my-6 px-4 py-2 text-white font-medium`}
|
||||
} my-6 flex justify-center rounded px-4 py-2 font-medium text-white transition-all duration-300 ease-out`}
|
||||
>
|
||||
{passwordChangeLoading() ? "Setting..." : "Set New Password"}
|
||||
</button>
|
||||
@@ -285,16 +302,16 @@ export default function PasswordResetPage() {
|
||||
|
||||
{/* Error Message */}
|
||||
<Show when={error() && !showRequestNewEmail()}>
|
||||
<div class="flex justify-center mt-4">
|
||||
<div class="text-red-500 text-sm italic">{error()}</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<div class="text-sm text-red-500 italic">{error()}</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Token Expired Message */}
|
||||
<div
|
||||
class={`${
|
||||
showRequestNewEmail() ? "" : "select-none opacity-0"
|
||||
} text-red-500 italic transition-opacity flex justify-center duration-300 ease-in-out px-4`}
|
||||
showRequestNewEmail() ? "" : "opacity-0 select-none"
|
||||
} flex justify-center px-4 text-red-500 italic transition-opacity duration-300 ease-in-out`}
|
||||
>
|
||||
Token has expired, request a new one{" "}
|
||||
<A
|
||||
@@ -307,15 +324,16 @@ export default function PasswordResetPage() {
|
||||
|
||||
{/* Back to Login Link */}
|
||||
<Show when={!countDown()}>
|
||||
<div class="flex justify-center mt-6">
|
||||
<div class="mt-6 flex justify-center">
|
||||
<A
|
||||
href="/login"
|
||||
class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 underline underline-offset-4 transition-colors"
|
||||
class="text-blue-500 underline underline-offset-4 transition-colors hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
Back to Login
|
||||
</A>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
|
||||
import { A, useNavigate } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
||||
import { isValidEmail } from "~/lib/validation";
|
||||
import { getClientCookie } from "~/lib/cookies.client";
|
||||
@@ -37,7 +38,10 @@ export default function RequestPasswordResetPage() {
|
||||
createEffect(() => {
|
||||
const timer = getClientCookie("passwordResetRequested");
|
||||
if (timer) {
|
||||
timerInterval = setInterval(() => calcRemainder(timer), 1000) as unknown as number;
|
||||
timerInterval = setInterval(
|
||||
() => calcRemainder(timer),
|
||||
1000
|
||||
) as unknown as number;
|
||||
onCleanup(() => {
|
||||
if (timerInterval) {
|
||||
clearInterval(timerInterval);
|
||||
@@ -71,7 +75,7 @@ export default function RequestPasswordResetPage() {
|
||||
const response = await fetch("/api/trpc/auth.requestPasswordReset", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email }),
|
||||
body: JSON.stringify({ email })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
@@ -115,6 +119,12 @@ export default function RequestPasswordResetPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Request Password Reset | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Request a password reset link to regain access to your account. Enter your email to receive reset instructions."
|
||||
/>
|
||||
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
|
||||
<div class="pt-24 text-center text-xl font-semibold text-slate-800 dark:text-slate-100">
|
||||
Password Reset Request
|
||||
@@ -151,7 +161,7 @@ export default function RequestPasswordResetPage() {
|
||||
loading()
|
||||
? "bg-zinc-400"
|
||||
: "bg-blue-400 hover:bg-blue-500 active:scale-90 dark:bg-blue-600 dark:hover:bg-blue-700"
|
||||
} flex justify-center rounded transition-all duration-300 ease-out my-6 px-4 py-2 text-white font-medium`}
|
||||
} my-6 flex justify-center rounded px-4 py-2 font-medium text-white transition-all duration-300 ease-out`}
|
||||
>
|
||||
{loading() ? "Sending..." : "Request Password Reset"}
|
||||
</button>
|
||||
@@ -177,28 +187,29 @@ export default function RequestPasswordResetPage() {
|
||||
{/* Success Message */}
|
||||
<div
|
||||
class={`${
|
||||
showSuccessMessage() ? "" : "select-none opacity-0"
|
||||
} text-green-500 italic transition-opacity flex justify-center duration-300 ease-in-out`}
|
||||
showSuccessMessage() ? "" : "opacity-0 select-none"
|
||||
} flex justify-center text-green-500 italic transition-opacity duration-300 ease-in-out`}
|
||||
>
|
||||
If email exists, you will receive an email shortly!
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
<Show when={error()}>
|
||||
<div class="flex justify-center mt-4">
|
||||
<div class="text-red-500 text-sm italic">{error()}</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
<div class="text-sm text-red-500 italic">{error()}</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
{/* Back to Login Link */}
|
||||
<div class="flex justify-center mt-6">
|
||||
<div class="mt-6 flex justify-center">
|
||||
<A
|
||||
href="/login"
|
||||
class="text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 underline underline-offset-4 transition-colors"
|
||||
class="text-blue-500 underline underline-offset-4 transition-colors hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
>
|
||||
Back to Login
|
||||
</A>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import SimpleParallax from "~/components/SimpleParallax";
|
||||
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
|
||||
|
||||
export default function LifeAndLineageMarketing() {
|
||||
return (
|
||||
<>
|
||||
<Title>Life and Lineage | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="A dark fantasy adventure mobile game. Download Life and Lineage on the App Store and Google Play."
|
||||
/>
|
||||
<SimpleParallax>
|
||||
<div class="flex flex-col items-center justify-center h-full text-white">
|
||||
<div class="flex h-full flex-col items-center justify-center text-white">
|
||||
<div>
|
||||
<img
|
||||
src="/LineageIcon.png"
|
||||
@@ -15,10 +22,8 @@ export default function LifeAndLineageMarketing() {
|
||||
class="object-cover object-center"
|
||||
/>
|
||||
</div>
|
||||
<h1 class="text-5xl font-bold mb-4 text-center">
|
||||
Life and Lineage
|
||||
</h1>
|
||||
<p class="text-xl mb-8">A dark fantasy adventure</p>
|
||||
<h1 class="mb-4 text-center text-5xl font-bold">Life and Lineage</h1>
|
||||
<p class="mb-8 text-xl">A dark fantasy adventure</p>
|
||||
<div class="flex space-x-4">
|
||||
<a
|
||||
class="my-auto transition-all duration-200 ease-out active:scale-95"
|
||||
@@ -42,5 +47,6 @@ export default function LifeAndLineageMarketing() {
|
||||
</div>
|
||||
</div>
|
||||
</SimpleParallax>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
|
||||
export default function PrivacyPolicy() {
|
||||
return (
|
||||
<>
|
||||
<Title>Privacy Policy - Life and Lineage | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Privacy policy for Life and Lineage mobile game, outlining data collection, usage, and user rights."
|
||||
/>
|
||||
<div class="min-h-screen px-[8vw] py-[10vh]">
|
||||
<div class="py-4 text-xl">Life and Lineage's Privacy Policy</div>
|
||||
<div class="py-2">Last Updated: October 22, 2024</div>
|
||||
@@ -18,13 +25,13 @@ export default function PrivacyPolicy() {
|
||||
</div>
|
||||
<div class="pl-4">
|
||||
<div class="pb-2">
|
||||
<div class="-ml-6">(a) Collection of Personal Data:</div> Life and
|
||||
Lineage collects and stores personal data only if users opt to use
|
||||
the remote saving feature. The information collected includes
|
||||
email address, and if using an OAuth provider - first name, and
|
||||
last name. This information is used solely for the purpose of
|
||||
providing and managing the remote saving feature. It is and never
|
||||
will be shared with a third party.
|
||||
<div class="-ml-6">(a) Collection of Personal Data:</div> Life
|
||||
and Lineage collects and stores personal data only if users opt
|
||||
to use the remote saving feature. The information collected
|
||||
includes email address, and if using an OAuth provider - first
|
||||
name, and last name. This information is used solely for the
|
||||
purpose of providing and managing the remote saving feature. It
|
||||
is and never will be shared with a third party.
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
<div class="-ml-6">(b) Data Removal:</div> Users can request the
|
||||
@@ -59,11 +66,11 @@ export default function PrivacyPolicy() {
|
||||
<span class="-ml-4 pr-2">3.</span> Security
|
||||
</div>
|
||||
<div class="pb-2 pl-4">
|
||||
<div class="-ml-6">(a) Data Protection:</div>Life and Lineage takes
|
||||
appropriate measures to protect the personal information of users
|
||||
who opt for the remote saving feature. We implement
|
||||
industry-standard security protocols to prevent unauthorized access,
|
||||
disclosure, alteration, or destruction of user data.
|
||||
<div class="-ml-6">(a) Data Protection:</div>Life and Lineage
|
||||
takes appropriate measures to protect the personal information of
|
||||
users who opt for the remote saving feature. We implement
|
||||
industry-standard security protocols to prevent unauthorized
|
||||
access, disclosure, alteration, or destruction of user data.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,8 +92,8 @@ export default function PrivacyPolicy() {
|
||||
</div>
|
||||
<div class="pb-2 pl-4">
|
||||
<div class="-ml-6">(a) Reaching Out:</div> If there are any
|
||||
questions or comments regarding this privacy policy, you can contact
|
||||
us{" "}
|
||||
questions or comments regarding this privacy policy, you can
|
||||
contact us{" "}
|
||||
<A href="/contact" class="text-blue hover-underline-animation">
|
||||
here
|
||||
</A>
|
||||
@@ -95,5 +102,6 @@ export default function PrivacyPolicy() {
|
||||
</div>
|
||||
</ol>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
|
||||
export default function PrivacyPolicy() {
|
||||
return (
|
||||
<>
|
||||
<Title>Privacy Policy - Shapes with Abigail | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Privacy policy for Shapes with Abigail app, explaining our commitment to child safety and non-collection of personal data."
|
||||
/>
|
||||
<div class="bg-zinc-100 dark:bg-zinc-900">
|
||||
<div class="min-h-screen px-[8vw] py-[8vh]">
|
||||
<div class="py-4 text-xl">
|
||||
@@ -24,9 +31,7 @@ export default function PrivacyPolicy() {
|
||||
</div>
|
||||
<div class="pl-4">
|
||||
<div class="pb-2">
|
||||
<div class="-ml-6">
|
||||
(a) Non-Collection of Personal Data:
|
||||
</div>{" "}
|
||||
<div class="-ml-6">(a) Non-Collection of Personal Data:</div>{" "}
|
||||
Shapes with Abigail! does not collect nor store personal data.
|
||||
We respect the privacy of our users, especially considering
|
||||
the age of our users. We believe that no information, whether
|
||||
@@ -41,10 +46,10 @@ export default function PrivacyPolicy() {
|
||||
<span class="-ml-4 pr-2">2.</span> Third-Party Access
|
||||
</div>
|
||||
<div class="pb-2 pl-4">
|
||||
<div class="-ml-6">(a) No Third-Party Access:</div> Since we
|
||||
do not collect or store any user data, there is no possibility
|
||||
of sharing or selling our users' information to third
|
||||
parties. Our priority is the safety and privacy of our users.
|
||||
<div class="-ml-6">(a) No Third-Party Access:</div> Since we do
|
||||
not collect or store any user data, there is no possibility of
|
||||
sharing or selling our users' information to third parties.
|
||||
Our priority is the safety and privacy of our users.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -63,14 +68,13 @@ export default function PrivacyPolicy() {
|
||||
|
||||
<div class="py-2">
|
||||
<div class="pb-2 text-lg">
|
||||
<span class="-ml-4 pr-2">4.</span> Changes to the Privacy
|
||||
Policy
|
||||
<span class="-ml-4 pr-2">4.</span> Changes to the Privacy Policy
|
||||
</div>
|
||||
<div class="pb-2 pl-4">
|
||||
<div class="-ml-6">(a) Updates:</div> We may update this
|
||||
privacy policy periodically. Any changes to this privacy policy
|
||||
will be posted on this page. However, since we do not collect
|
||||
any personal data, these updates are likely to be insignificant.
|
||||
<div class="-ml-6">(a) Updates:</div> We may update this privacy
|
||||
policy periodically. Any changes to this privacy policy will be
|
||||
posted on this page. However, since we do not collect any
|
||||
personal data, these updates are likely to be insignificant.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -94,5 +98,6 @@ export default function PrivacyPolicy() {
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { Title } from "@solidjs/meta";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
|
||||
export default function Resume() {
|
||||
return (
|
||||
<>
|
||||
<Title>Resume | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="View Michael Freno's resume - Software Engineer with expertise in full-stack development, game development, and open source."
|
||||
/>
|
||||
|
||||
<main class="flex h-screen w-full flex-col">
|
||||
<Title>Resume - Freno.dev</Title>
|
||||
<div class="flex h-full w-full items-center justify-center">
|
||||
@@ -12,5 +19,6 @@ export default function Resume() {
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { createSignal } from "solid-js";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import Input from "~/components/ui/Input";
|
||||
import Button from "~/components/ui/Button";
|
||||
import { isValidEmail, validatePassword, passwordsMatch } from "~/lib/validation";
|
||||
import {
|
||||
isValidEmail,
|
||||
validatePassword,
|
||||
passwordsMatch
|
||||
} from "~/lib/validation";
|
||||
|
||||
/**
|
||||
* Test page to validate Task 01 components and utilities
|
||||
@@ -36,24 +41,30 @@ export default function TestUtilsPage() {
|
||||
setLoading(true);
|
||||
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
alert(`Form submitted!\nEmail: ${email()}\nPassword: ${password()}`);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Utility Testing | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="Testing page for form components and validation utilities."
|
||||
/>
|
||||
<main class="min-h-screen bg-gray-100 p-8">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||
<h1 class="text-3xl font-bold mb-2">Task 01 - Utility Testing</h1>
|
||||
<p class="text-gray-600 mb-4">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<div class="mb-6 rounded-lg bg-white p-6 shadow-lg">
|
||||
<h1 class="mb-2 text-3xl font-bold">Task 01 - Utility Testing</h1>
|
||||
<p class="mb-4 text-gray-600">
|
||||
Testing shared utilities, types, and UI components
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6 mb-6">
|
||||
<h2 class="text-xl font-bold mb-4">Form Components & Validation</h2>
|
||||
<div class="mb-6 rounded-lg bg-white p-6 shadow">
|
||||
<h2 class="mb-4 text-xl font-bold">Form Components & Validation</h2>
|
||||
|
||||
<form onSubmit={handleSubmit} class="space-y-4">
|
||||
<Input
|
||||
@@ -130,30 +141,42 @@ export default function TestUtilsPage() {
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow p-6">
|
||||
<h2 class="text-xl font-bold mb-4">Validation Status</h2>
|
||||
<div class="rounded-lg bg-white p-6 shadow">
|
||||
<h2 class="mb-4 text-xl font-bold">Validation Status</h2>
|
||||
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={`w-3 h-3 rounded-full ${isValidEmail(email()) ? "bg-green-500" : "bg-gray-300"}`} />
|
||||
<span
|
||||
class={`h-3 w-3 rounded-full ${isValidEmail(email()) ? "bg-green-500" : "bg-gray-300"}`}
|
||||
/>
|
||||
<span>Email Valid: {isValidEmail(email()) ? "✓" : "✗"}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={`w-3 h-3 rounded-full ${validatePassword(password()).isValid ? "bg-green-500" : "bg-gray-300"}`} />
|
||||
<span>Password Valid: {validatePassword(password()).isValid ? "✓" : "✗"}</span>
|
||||
<span
|
||||
class={`h-3 w-3 rounded-full ${validatePassword(password()).isValid ? "bg-green-500" : "bg-gray-300"}`}
|
||||
/>
|
||||
<span>
|
||||
Password Valid:{" "}
|
||||
{validatePassword(password()).isValid ? "✓" : "✗"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={`w-3 h-3 rounded-full ${passwordsMatch(password(), passwordConf()) ? "bg-green-500" : "bg-gray-300"}`} />
|
||||
<span>Passwords Match: {passwordsMatch(password(), passwordConf()) ? "✓" : "✗"}</span>
|
||||
<span
|
||||
class={`h-3 w-3 rounded-full ${passwordsMatch(password(), passwordConf()) ? "bg-green-500" : "bg-gray-300"}`}
|
||||
/>
|
||||
<span>
|
||||
Passwords Match:{" "}
|
||||
{passwordsMatch(password(), passwordConf()) ? "✓" : "✗"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-50 border border-blue-200 rounded p-4 mt-6">
|
||||
<h3 class="font-bold text-blue-800 mb-2">✅ Task 01 Complete</h3>
|
||||
<ul class="text-sm text-blue-700 space-y-1">
|
||||
<div class="mt-6 rounded border border-blue-200 bg-blue-50 p-4">
|
||||
<h3 class="mb-2 font-bold text-blue-800">✅ Task 01 Complete</h3>
|
||||
<ul class="space-y-1 text-sm text-blue-700">
|
||||
<li>✓ User types created</li>
|
||||
<li>✓ Cookie utilities created</li>
|
||||
<li>✓ Validation helpers created</li>
|
||||
@@ -165,5 +188,6 @@ export default function TestUtilsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createSignal, For, Show } from "solid-js";
|
||||
import { query, createAsync } from "@solidjs/router";
|
||||
import { Title, Meta } from "@solidjs/meta";
|
||||
import { getRequestEvent } from "solid-js/web";
|
||||
import { api } from "~/lib/api";
|
||||
|
||||
@@ -932,6 +933,12 @@ export default function TestPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>API Testing | Michael Freno</Title>
|
||||
<Meta
|
||||
name="description"
|
||||
content="tRPC API testing dashboard for developers to test endpoints and verify functionality."
|
||||
/>
|
||||
<Show
|
||||
when={authState()?.privilegeLevel === "admin"}
|
||||
fallback={
|
||||
@@ -946,7 +953,9 @@ export default function TestPage() {
|
||||
<main class="min-h-screen p-8">
|
||||
<div class="mx-auto max-w-6xl">
|
||||
<div class="bg-surface0 mb-6 rounded-lg p-6 shadow-lg">
|
||||
<h1 class="mb-2 text-3xl font-bold">tRPC API Testing Dashboard</h1>
|
||||
<h1 class="mb-2 text-3xl font-bold">
|
||||
tRPC API Testing Dashboard
|
||||
</h1>
|
||||
<p class="text-text mb-4">
|
||||
Complete API coverage: Example, Auth, Database, User, Misc, and
|
||||
Lineage routers
|
||||
@@ -994,7 +1003,8 @@ export default function TestPage() {
|
||||
<For each={section.endpoints}>
|
||||
{(endpoint) => {
|
||||
const key = `${endpoint.router}.${endpoint.procedure}`;
|
||||
const hasInput = endpoint.sampleInput !== undefined;
|
||||
const hasInput =
|
||||
endpoint.sampleInput !== undefined;
|
||||
const displayInput = () => {
|
||||
if (inputEdits()[key]) {
|
||||
return inputEdits()[key];
|
||||
@@ -1069,7 +1079,10 @@ export default function TestPage() {
|
||||
<textarea
|
||||
value={displayInput()}
|
||||
onInput={(e) =>
|
||||
updateInput(key, e.currentTarget.value)
|
||||
updateInput(
|
||||
key,
|
||||
e.currentTarget.value
|
||||
)
|
||||
}
|
||||
class="border-lavender bg-crust min-h-[100px] w-full rounded border p-2 font-mono text-xs"
|
||||
spellcheck={false}
|
||||
@@ -1096,7 +1109,11 @@ export default function TestPage() {
|
||||
✓ Response:
|
||||
</p>
|
||||
<pre class="max-h-60 overflow-auto text-xs text-green-400">
|
||||
{JSON.stringify(results()[key], null, 2)}
|
||||
{JSON.stringify(
|
||||
results()[key],
|
||||
null,
|
||||
2
|
||||
)}
|
||||
</pre>
|
||||
</div>
|
||||
</Show>
|
||||
@@ -1118,18 +1135,20 @@ export default function TestPage() {
|
||||
|
||||
<div class="space-y-4 text-base">
|
||||
<div>
|
||||
<h3 class="mb-2 text-lg font-semibold">🟢 No Auth Required</h3>
|
||||
<h3 class="mb-2 text-lg font-semibold">
|
||||
🟢 No Auth Required
|
||||
</h3>
|
||||
<ul class="ml-6 list-disc space-y-1 text-sm">
|
||||
<li>
|
||||
<strong>Example Router</strong> - Hello endpoint
|
||||
</li>
|
||||
<li>
|
||||
<strong>Lineage JSON Service</strong> - All 6 endpoints work
|
||||
immediately
|
||||
<strong>Lineage JSON Service</strong> - All 6 endpoints
|
||||
work immediately
|
||||
</li>
|
||||
<li>
|
||||
<strong>Database</strong> - All endpoints (comments, posts,
|
||||
users, reactions, likes)
|
||||
<strong>Database</strong> - All endpoints (comments,
|
||||
posts, users, reactions, likes)
|
||||
</li>
|
||||
<li>
|
||||
<strong>Misc</strong> - Downloads, S3 operations, password
|
||||
@@ -1179,30 +1198,32 @@ export default function TestPage() {
|
||||
<strong>Example Router</strong> - Admin Dashboard
|
||||
</li>
|
||||
<li>
|
||||
<strong>Lineage Maintenance</strong> - Find Loose Databases,
|
||||
Cleanup Expired
|
||||
<strong>Lineage Maintenance</strong> - Find Loose
|
||||
Databases, Cleanup Expired
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="mb-2 text-lg font-semibold">📝 Typical Workflows</h3>
|
||||
<h3 class="mb-2 text-lg font-semibold">
|
||||
📝 Typical Workflows
|
||||
</h3>
|
||||
<ol class="ml-6 list-decimal space-y-2 text-sm">
|
||||
<li>
|
||||
<strong>Test public endpoints:</strong> Start with Example
|
||||
Hello, Lineage JSON Service, or Database queries
|
||||
</li>
|
||||
<li>
|
||||
<strong>OAuth flow:</strong> Use Auth router callbacks with
|
||||
OAuth codes from GitHub/Google
|
||||
<strong>OAuth flow:</strong> Use Auth router callbacks
|
||||
with OAuth codes from GitHub/Google
|
||||
</li>
|
||||
<li>
|
||||
<strong>Email auth flow:</strong> Register → verify email →
|
||||
login → use JWT
|
||||
<strong>Email auth flow:</strong> Register → verify email
|
||||
→ login → use JWT
|
||||
</li>
|
||||
<li>
|
||||
<strong>Blog/Project management:</strong> Create posts → add
|
||||
comments/likes → upload images via S3
|
||||
<strong>Blog/Project management:</strong> Create posts →
|
||||
add comments/likes → upload images via S3
|
||||
</li>
|
||||
<li>
|
||||
<strong>Lineage game data:</strong> Fetch JSON data →
|
||||
@@ -1214,9 +1235,9 @@ export default function TestPage() {
|
||||
<div class="border-rosewater bg-rosewater mt-4 rounded border p-4">
|
||||
<p class="text-crust text-sm">
|
||||
<strong>Note:</strong> Some endpoints require specific setup
|
||||
(e.g., OAuth codes, existing database records, valid S3 keys).
|
||||
Check the sample input to understand what data each endpoint
|
||||
expects.
|
||||
(e.g., OAuth codes, existing database records, valid S3
|
||||
keys). Check the sample input to understand what data each
|
||||
endpoint expects.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1224,5 +1245,6 @@ export default function TestPage() {
|
||||
</div>
|
||||
</main>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user