UI Consolidation Cont.

This commit is contained in:
Michael Freno
2026-01-06 10:40:15 -05:00
parent 609932a55b
commit 021a2871c1
25 changed files with 235 additions and 220 deletions

View File

@@ -47,3 +47,6 @@ export default function PageHead(props: PageHeadProps) {
</>
);
}
// Named export for consistency
export { PageHead };

View File

@@ -250,6 +250,9 @@ declare module "@tiptap/core" {
iframe: {
setIframe: (options: { src: string }) => ReturnType;
};
video: {
setVideo: (options: { src: string }) => ReturnType;
};
}
}
@@ -298,24 +301,76 @@ const IframeEmbed = Node.create<IframeOptions>({
return {
setIframe:
(options: { src: string }) =>
({ tr, dispatch, editor }) => {
({ tr, dispatch }) => {
const { selection } = tr;
const node = this.type.create(options);
// Check if the src is a direct video file
const src = options.src || "";
const isVideoFile = /\.(mp4|mov|webm|ogg)(\?.*)?$/i.test(src);
if (isVideoFile) {
// Insert a proper video tag instead of iframe
if (dispatch) {
const videoHTML = `<video src="${src}" controls playsinline style="max-width: 100%; height: auto;"></video>`;
editor.commands.insertContent(videoHTML);
tr.replaceRangeWith(selection.from, selection.to, node);
}
return true;
}
};
}
});
// For non-video URLs, create iframe as normal
const node = this.type.create(options);
interface VideoOptions {
HTMLAttributes: {
[key: string]: any;
};
}
const Video = Node.create<VideoOptions>({
name: "video",
group: "block",
atom: true,
addOptions() {
return {
HTMLAttributes: {
class: "video-wrapper"
}
};
},
addAttributes() {
return {
src: {
default: null
},
controls: {
default: true
},
playsinline: {
default: true
}
};
},
parseHTML() {
return [
{
tag: "video[src]"
}
];
},
renderHTML({ HTMLAttributes }) {
return ["video", HTMLAttributes];
},
addCommands() {
return {
setVideo:
(options: { src: string }) =>
({ tr, dispatch }) => {
const { selection } = tr;
const node = this.type.create({
src: options.src,
controls: true,
playsinline: true
});
if (dispatch) {
tr.replaceRangeWith(selection.from, selection.to, node);
@@ -1352,6 +1407,7 @@ export default function TextEditor(props: TextEditorProps) {
}),
Image,
IframeEmbed,
Video,
TaskList,
TaskItem.configure({
nested: true,
@@ -2454,12 +2510,22 @@ export default function TextEditor(props: TextEditorProps) {
const instance = editor();
if (!instance) return;
const url = window.prompt("URL");
const url = window.prompt("Embed URL (YouTube, etc.)");
if (url) {
instance.commands.setIframe({ src: url });
}
};
const addVideo = () => {
const instance = editor();
if (!instance) return;
const url = window.prompt("Video URL (direct link to .mp4, .webm, etc.)");
if (url) {
instance.commands.setVideo({ src: url });
}
};
const addImage = () => {
const instance = editor();
if (!instance) return;
@@ -3790,13 +3856,21 @@ export default function TextEditor(props: TextEditorProps) {
>
🖼 Image
</button>
<button
type="button"
onClick={addVideo}
class="touch-manipulation rounded px-2 py-1 text-xs select-none"
title="Add Video"
>
🎬 Video
</button>
<button
type="button"
onClick={addIframe}
class="touch-manipulation rounded px-2 py-1 text-xs select-none"
title="Add Iframe"
title="Add Iframe Embed"
>
📺 Iframe
📺 Embed
</button>
<button
type="button"

View File

@@ -23,7 +23,7 @@ export default function Button(props: ButtonProps) {
const size = () => local.size || "md";
const baseClasses =
"flex justify-center items-center rounded transition-all duration-300 ease-out";
"flex justify-center cursor-pointer items-center rounded transition-all duration-300 ease-out";
const variantClasses = () => {
const isDisabledOrLoading = local.disabled || local.loading;
@@ -77,3 +77,6 @@ export default function Button(props: ButtonProps) {
</button>
);
}
// Named export for consistency
export { Button };

View File

@@ -24,7 +24,7 @@ export default function PasswordInput(props: PasswordInputProps) {
);
return (
<>
<div class="flex flex-col items-center gap-2">
<div class={local.containerClass || "input-group relative mx-4 mb-2"}>
<Input
{...inputProps}
@@ -36,7 +36,7 @@ export default function PasswordInput(props: PasswordInputProps) {
<button
type="button"
onClick={() => setShowPassword(!showPassword())}
class="text-subtext0 absolute top-2 right-0 transition-all hover:brightness-125"
class="text-subtext0 absolute right-0 bottom-2 transition-all hover:brightness-125"
aria-label={showPassword() ? "Hide password" : "Show password"}
>
<Show
@@ -56,10 +56,8 @@ export default function PasswordInput(props: PasswordInputProps) {
</div>
{local.showStrength && local.passwordValue !== undefined && (
<div class="px-4 pt-1">
<PasswordStrengthMeter password={local.passwordValue} />
</div>
)}
</>
</div>
);
}

View File

@@ -1,4 +1,4 @@
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { HttpStatusCode } from "@solidjs/start";
import { useNavigate } from "@solidjs/router";
import { createEffect, createSignal, For } from "solid-js";
@@ -51,10 +51,9 @@ export default function Page_401() {
return (
<>
<Title>401 Unauthorized | Michael Freno</Title>
<Meta
name="description"
content="401 - Unauthorized access. Please log in to access this page."
<PageHead
title="401 Unauthorized"
description="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">

View File

@@ -1,4 +1,4 @@
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { HttpStatusCode } from "@solidjs/start";
import { useNavigate, useLocation } from "@solidjs/router";
import { createSignal, Show } from "solid-js";
@@ -101,10 +101,9 @@ export default function NotFound() {
{/*@ts-ignore (intentional crash)*/}
<CrashComponent />
</Show>
<Title>404 Not Found | Michael Freno</Title>
<Meta
name="description"
content="404 - Page not found. The page you're looking for doesn't exist."
<PageHead
title="404 Not Found"
description="404 - Page not found. The page you're looking for doesn't exist."
/>
<HttpStatusCode code={404} />
<TerminalErrorPage

View File

@@ -1,5 +1,5 @@
import { createSignal, Show, createEffect } from "solid-js";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { useNavigate, redirect, query, createAsync } from "@solidjs/router";
import { getEvent } from "vinxi/http";
import XCircle from "~/components/icons/XCircle";
@@ -486,10 +486,9 @@ export default function AccountPage() {
return (
<>
<Title>Account | Michael Freno</Title>
<Meta
name="description"
content="Manage your account settings, update profile information, and configure preferences."
<PageHead
title="Account"
description="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">
@@ -683,23 +682,17 @@ export default function AccountPage() {
</div>
</Show>
<div class="flex justify-end">
<button
<Button
type="submit"
disabled={
emailButtonLoading() ||
(userProfile().email !== null &&
!userProfile().emailVerified)
userProfile().email !== null &&
!userProfile().emailVerified
}
class={`${
emailButtonLoading() ||
(userProfile().email !== null &&
!userProfile().emailVerified)
? "bg-blue cursor-not-allowed brightness-75"
: "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`}
loading={emailButtonLoading()}
class="mt-2"
>
{emailButtonLoading() ? "Submitting..." : "Submit"}
</button>
Submit
</Button>
</div>
<Show when={showEmailSuccess()}>
<div class="text-green mt-2 text-center text-sm">
@@ -740,19 +733,13 @@ export default function AccountPage() {
containerClass="input-group mx-4"
/>
<div class="flex justify-end">
<button
<Button
type="submit"
disabled={displayNameButtonLoading()}
class={`${
displayNameButtonLoading()
? "bg-blue cursor-not-allowed brightness-75"
: "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`}
loading={displayNameButtonLoading()}
class="mt-2"
>
{displayNameButtonLoading()
? "Submitting..."
: "Submit"}
</button>
Submit
</Button>
</div>
<Show when={showDisplayNameSuccess()}>
<div class="text-green mt-2 text-center text-sm">
@@ -838,17 +825,14 @@ export default function AccountPage() {
</div>
</Show>
<button
<Button
type="submit"
disabled={passwordChangeLoading() || !passwordsMatch()}
class={`${
passwordChangeLoading() || !passwordsMatch()
? "bg-blue cursor-not-allowed brightness-75"
: "bg-blue hover:brightness-125 active:scale-90"
} my-6 flex justify-center rounded px-4 py-2 text-base transition-all duration-300 ease-out`}
disabled={!passwordsMatch()}
loading={passwordChangeLoading()}
class="my-6"
>
{passwordChangeLoading() ? "Setting..." : "Set"}
</button>
Set
</Button>
<Show when={passwordError()}>
<div class="text-red text-center text-sm">
@@ -871,18 +855,15 @@ export default function AccountPage() {
{/* Sign Out Section */}
<div class="mx-auto max-w-md py-4">
<button
<Button
type="button"
onClick={handleSignOut}
disabled={signOutLoading()}
class={`${
signOutLoading()
? "bg-overlay0 cursor-not-allowed opacity-75"
: "bg-overlay0 hover:bg-overlay1"
} w-full rounded px-4 py-2 transition-all`}
loading={signOutLoading()}
variant="secondary"
class="w-full"
>
{signOutLoading() ? "Signing Out..." : "Sign Out"}
</button>
Sign Out
</Button>
</div>
<hr class="mt-8 mb-8" />
@@ -938,19 +919,14 @@ export default function AccountPage() {
/>
</div>
<button
<Button
type="submit"
disabled={deleteAccountButtonLoading()}
class={`${
deleteAccountButtonLoading()
? "bg-red cursor-not-allowed brightness-75"
: "bg-red hover:brightness-125 active:scale-90"
} border-text mx-auto mt-4 flex justify-center rounded border px-4 py-2 text-base transition-all duration-300 ease-out`}
loading={deleteAccountButtonLoading()}
variant="danger"
class="border-text mx-auto mt-4 border"
>
{deleteAccountButtonLoading()
? "Deleting..."
: "Delete Account"}
</button>
Delete Account
</Button>
<Show when={passwordDeletionError()}>
<div class="text-red mt-2 text-center text-sm">

View File

@@ -1,5 +1,5 @@
import { createSignal, Show, For, createEffect, ErrorBoundary } from "solid-js";
import { Title } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { redirect, query, createAsync, useNavigate } from "@solidjs/router";
import { getEvent } from "vinxi/http";
import { api } from "~/lib/api";
@@ -128,7 +128,10 @@ export default function AnalyticsPage() {
return (
<>
<Title>Analytics Dashboard - Admin</Title>
<PageHead
title="Analytics Dashboard - Admin"
description="Visitor analytics and performance metrics"
/>
<div class="bg-base min-h-screen px-4 py-8">
<div class="mx-auto max-w-7xl">
<div class="mb-8">

View File

@@ -6,7 +6,7 @@ import {
query,
useSearchParams
} from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { createAsync } from "@solidjs/router";
import { getRequestEvent } from "solid-js/web";
import SessionDependantLike from "~/components/blog/SessionDependantLike";
@@ -316,12 +316,9 @@ export default function PostPage() {
return (
<>
<Title>
{p().title.replaceAll("_", " ")} | Michael Freno
</Title>
<Meta
name="description"
content={
<PageHead
title={p().title.replaceAll("_", " ")}
description={
p().subtitle ||
`Read ${p().title.replaceAll("_", " ")} by Michael Freno on the freno.me blog.`
}

View File

@@ -1,6 +1,6 @@
import { Show, lazy } from "solid-js";
import { query, redirect } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { createAsync } from "@solidjs/router";
import { getEvent } from "vinxi/http";
import { Spinner } from "~/components/Spinner";
@@ -31,10 +31,9 @@ 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."
<PageHead
title="Create Blog Post"
description="Create a new blog post with rich text editing, image uploads, and tag management."
/>
<Show when={authState()?.userID} fallback={<Spinner />}>

View File

@@ -1,6 +1,6 @@
import { Show, lazy } from "solid-js";
import { useParams, query } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { useParams, query, redirect } from "@solidjs/router";
import { PageHead } from "~/components/PageHead";
import { createAsync } from "@solidjs/router";
import { getEvent } from "vinxi/http";
import "../post.css";
@@ -66,10 +66,9 @@ 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."
<PageHead
title="Edit Post"
description="Edit your blog post with rich text editing, image management, and tag updates."
/>
<Show

View File

@@ -1,6 +1,6 @@
import { Show } from "solid-js";
import { useSearchParams, A, query } from "@solidjs/router";
import { Title } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { createAsync } from "@solidjs/router";
import { getRequestEvent } from "solid-js/web";
import PostSortingSelect from "~/components/blog/PostSortingSelect";
@@ -91,7 +91,10 @@ export default function BlogIndex() {
return (
<>
<Title>Blog | Michael Freno</Title>
<PageHead
title="Blog"
description="Technical blog posts about web development, programming, and software engineering."
/>
<div class="mx-auto py-16 pb-24">
<Show when={data()} fallback={<TerminalSplash />}>

View File

@@ -6,15 +6,16 @@ import {
query,
createAsync
} from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { A } from "@solidjs/router";
import { action, redirect } from "@solidjs/router";
import { PageHead } from "~/components/PageHead";
import { api } from "~/lib/api";
import { getClientCookie, setClientCookie } from "~/lib/cookies.client";
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import LoadingSpinner from "~/components/LoadingSpinner";
import RevealDropDown from "~/components/RevealDropDown";
import Input from "~/components/ui/Input";
import { Button } from "~/components/ui/Button";
import type { UserProfile } from "~/types/user";
import { getCookie, setCookie } from "vinxi/http";
import { z } from "zod";
@@ -360,8 +361,7 @@ export default function ContactPage() {
return (
<>
<Title>Contact | Michael Freno</Title>
<Meta name="description" content="Contact Me" />
<PageHead title="Contact" description="Contact Me" />
<div class="bg-base flex min-h-screen w-full justify-center">
<div class="w-full max-w-4xl px-4 pt-[20vh]">
@@ -422,19 +422,9 @@ export default function ContactPage() {
countDown() > 0 || (contactData()?.remainingTime ?? 0) > 0
}
fallback={
<button
type="submit"
disabled={loading()}
class={`${
loading()
? "bg-zinc-400"
: "bg-blue hover:brightness-125 active:scale-90"
} flex w-36 justify-center rounded py-3 text-base font-light transition-all duration-300 ease-out`}
>
<Show when={loading()} fallback="Send Message">
<LoadingSpinner height={24} width={24} />
</Show>
</button>
<Button type="submit" loading={loading()} class="w-36">
Send Message
</Button>
}
>
<Show

View File

@@ -1,13 +1,12 @@
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
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."
<PageHead
title="Account Deletion - Life and Lineage"
description="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">

View File

@@ -1,4 +1,4 @@
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { A } from "@solidjs/router";
import { createSignal, onMount, onCleanup } from "solid-js";
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
@@ -50,10 +50,9 @@ 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."
<PageHead
title="Downloads"
description="Download Life and Lineage, Shapes with Abigail, and Cork for macOS. Available on iOS, Android, and macOS."
/>
<div class="bg-base relative min-h-screen overflow-hidden px-4 pt-[15vh] pb-12 md:px-8">

View File

@@ -1,14 +1,13 @@
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { DarkModeToggle } from "~/components/DarkModeToggle";
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"
<PageHead
title="Home"
description="Michael Freno - Software Engineer based in Brooklyn, NY"
/>
<main class="flex h-full flex-col gap-8 px-4 py-16 text-xl">

View File

@@ -6,7 +6,7 @@ import {
redirect,
query
} from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { getEvent } from "vinxi/http";
import GoogleLogo from "~/components/icons/GoogleLogo";
import GitHub from "~/components/icons/GitHub";
@@ -18,6 +18,7 @@ import { env } from "~/env/client";
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
import Input from "~/components/ui/Input";
import PasswordInput from "~/components/ui/PasswordInput";
import { Button } from "~/components/ui/Button";
const checkAuth = query(async () => {
"use server";
@@ -318,10 +319,9 @@ 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."
<PageHead
title="Login"
description="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">
<div class="relative pt-12 md:pt-24">
@@ -480,21 +480,13 @@ export default function LoginPage() {
<Show
when={!register() && !usePassword() && countDown() > 0}
fallback={
<button
type="submit"
disabled={loading()}
class={`${
loading()
? "bg-zinc-400"
: "bg-blue hover:brightness-125 active:scale-90"
} flex w-36 justify-center rounded py-3 text-white transition-all duration-300 ease-out`}
>
<Button type="submit" loading={loading()} class="w-36">
{register()
? "Sign Up"
: usePassword()
? "Sign In"
: "Get Link"}
</button>
</Button>
}
>
<CountdownCircleTimer

View File

@@ -1,12 +1,13 @@
import { createSignal, createEffect, Show } from "solid-js";
import { A, useNavigate, useSearchParams } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import { validatePassword } from "~/lib/validation";
import { api } from "~/lib/api";
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
import PasswordInput from "~/components/ui/PasswordInput";
import PasswordStrengthMeter from "~/components/PasswordStrengthMeter";
import { Button } from "~/components/ui/Button";
export default function PasswordResetPage() {
const navigate = useNavigate();
@@ -153,10 +154,9 @@ export default function PasswordResetPage() {
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."
<PageHead
title="Reset Password"
description="Set a new password for your account to regain access to your profile and personalized features."
/>
<div>
<div class="pt-24 text-center text-xl font-semibold">
@@ -222,17 +222,14 @@ export default function PasswordResetPage() {
<Show
when={countDown()}
fallback={
<button
<Button
type="submit"
disabled={passwordChangeLoading() || !passwordsMatch()}
class={`${
passwordChangeLoading() || !passwordsMatch()
? "cursor-not-allowed bg-zinc-400"
: "bg-blue hover:brightness-125 active:scale-90"
} my-6 flex justify-center rounded px-4 py-2 text-base font-medium transition-all duration-300 ease-out`}
disabled={!passwordsMatch()}
loading={passwordChangeLoading()}
class="my-6"
>
{passwordChangeLoading() ? "Setting..." : "Set New Password"}
</button>
Set New Password
</Button>
}
>
<div class="mx-auto pt-4">

View File

@@ -1,11 +1,12 @@
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
import { A, useNavigate } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
import { isValidEmail } from "~/lib/validation";
import { getClientCookie } from "~/lib/cookies.client";
import { COUNTDOWN_CONFIG } from "~/config";
import Input from "~/components/ui/Input";
import { Button } from "~/components/ui/Button";
export default function RequestPasswordResetPage() {
const navigate = useNavigate();
@@ -123,10 +124,9 @@ 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."
<PageHead
title="Request Password Reset"
description="Request a password reset link to regain access to your account. Enter your email to receive reset instructions."
/>
<div class="pt-24 text-center text-xl font-semibold">
Password Reset Request
@@ -152,17 +152,9 @@ export default function RequestPasswordResetPage() {
<Show
when={countDown() > 0}
fallback={
<button
type="submit"
disabled={loading()}
class={`${
loading()
? "bg-zinc-400"
: "bg-blue hover:brightness-125 active:scale-90"
} 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>
<Button type="submit" loading={loading()} class="my-6">
Request Password Reset
</Button>
}
>
<div class="mx-auto pt-4">

View File

@@ -1,15 +1,14 @@
import { A } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
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."
<PageHead
title="Life and Lineage"
description="A dark fantasy adventure mobile game. Download Life and Lineage on the App Store and Google Play."
/>
<SimpleParallax>
<div class="flex h-full flex-col items-center justify-center text-white">

View File

@@ -1,13 +1,12 @@
import { A } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
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."
<PageHead
title="Privacy Policy - Life and Lineage"
description="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&apos;s Privacy Policy</div>

View File

@@ -1,13 +1,12 @@
import { A } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
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."
<PageHead
title="Privacy Policy - Shapes with Abigail"
description="Privacy policy for Shapes with Abigail app, explaining our commitment to child safety and non-collection of personal data."
/>
<div class="bg-base">
<div class="min-h-screen px-[8vw] py-[8vh]">

View File

@@ -1,5 +1,5 @@
import { Title, Meta } from "@solidjs/meta";
import { onCleanup, onMount } from "solid-js";
import { PageHead } from "~/components/PageHead";
export default function Resume() {
let iframeRef: HTMLIFrameElement | undefined;
@@ -25,10 +25,9 @@ export default function Resume() {
return (
<>
<Title>Resume | Michael Freno</Title>
<Meta
name="description"
content="View Michael Freno's resume - Software Engineer."
<PageHead
title="Resume"
description="View Michael Freno's resume - Software Engineer."
/>
<main class="flex h-screen w-full flex-col">

View File

@@ -1,5 +1,5 @@
import { createSignal } from "solid-js";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import Input from "~/components/ui/Input";
import Button from "~/components/ui/Button";
import {
@@ -44,10 +44,9 @@ export default function TestUtilsPage() {
return (
<>
<Title>Utility Testing | Michael Freno</Title>
<Meta
name="description"
content="Testing page for form components and validation utilities."
<PageHead
title="Utility Testing"
description="Testing page for form components and validation utilities."
/>
<main class="min-h-screen bg-gray-100 p-8">
<div class="mx-auto max-w-2xl">

View File

@@ -1,6 +1,6 @@
import { createSignal, For, Show } from "solid-js";
import { query, createAsync } from "@solidjs/router";
import { Title, Meta } from "@solidjs/meta";
import { PageHead } from "~/components/PageHead";
import { getRequestEvent } from "solid-js/web";
import { api } from "~/lib/api";
@@ -914,10 +914,9 @@ 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."
<PageHead
title="API Testing"
description="tRPC API testing dashboard for developers to test endpoints and verify functionality."
/>
<Show
when={authState()?.privilegeLevel === "admin"}