UI Consolidation Cont.
This commit is contained in:
@@ -47,3 +47,6 @@ export default function PageHead(props: PageHeadProps) {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Named export for consistency
|
||||||
|
export { PageHead };
|
||||||
|
|||||||
@@ -250,6 +250,9 @@ declare module "@tiptap/core" {
|
|||||||
iframe: {
|
iframe: {
|
||||||
setIframe: (options: { src: string }) => ReturnType;
|
setIframe: (options: { src: string }) => ReturnType;
|
||||||
};
|
};
|
||||||
|
video: {
|
||||||
|
setVideo: (options: { src: string }) => ReturnType;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,24 +301,76 @@ const IframeEmbed = Node.create<IframeOptions>({
|
|||||||
return {
|
return {
|
||||||
setIframe:
|
setIframe:
|
||||||
(options: { src: string }) =>
|
(options: { src: string }) =>
|
||||||
({ tr, dispatch, editor }) => {
|
({ tr, dispatch }) => {
|
||||||
const { selection } = tr;
|
const { selection } = tr;
|
||||||
|
const node = this.type.create(options);
|
||||||
|
|
||||||
// Check if the src is a direct video file
|
if (dispatch) {
|
||||||
const src = options.src || "";
|
tr.replaceRangeWith(selection.from, selection.to, node);
|
||||||
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);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-video URLs, create iframe as normal
|
return true;
|
||||||
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) {
|
if (dispatch) {
|
||||||
tr.replaceRangeWith(selection.from, selection.to, node);
|
tr.replaceRangeWith(selection.from, selection.to, node);
|
||||||
@@ -1352,6 +1407,7 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
}),
|
}),
|
||||||
Image,
|
Image,
|
||||||
IframeEmbed,
|
IframeEmbed,
|
||||||
|
Video,
|
||||||
TaskList,
|
TaskList,
|
||||||
TaskItem.configure({
|
TaskItem.configure({
|
||||||
nested: true,
|
nested: true,
|
||||||
@@ -2454,12 +2510,22 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
const instance = editor();
|
const instance = editor();
|
||||||
if (!instance) return;
|
if (!instance) return;
|
||||||
|
|
||||||
const url = window.prompt("URL");
|
const url = window.prompt("Embed URL (YouTube, etc.)");
|
||||||
if (url) {
|
if (url) {
|
||||||
instance.commands.setIframe({ src: 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 addImage = () => {
|
||||||
const instance = editor();
|
const instance = editor();
|
||||||
if (!instance) return;
|
if (!instance) return;
|
||||||
@@ -3790,13 +3856,21 @@ export default function TextEditor(props: TextEditorProps) {
|
|||||||
>
|
>
|
||||||
🖼 Image
|
🖼 Image
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={addVideo}
|
||||||
|
class="touch-manipulation rounded px-2 py-1 text-xs select-none"
|
||||||
|
title="Add Video"
|
||||||
|
>
|
||||||
|
🎬 Video
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={addIframe}
|
onClick={addIframe}
|
||||||
class="touch-manipulation rounded px-2 py-1 text-xs select-none"
|
class="touch-manipulation rounded px-2 py-1 text-xs select-none"
|
||||||
title="Add Iframe"
|
title="Add Iframe Embed"
|
||||||
>
|
>
|
||||||
📺 Iframe
|
📺 Embed
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function Button(props: ButtonProps) {
|
|||||||
const size = () => local.size || "md";
|
const size = () => local.size || "md";
|
||||||
|
|
||||||
const baseClasses =
|
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 variantClasses = () => {
|
||||||
const isDisabledOrLoading = local.disabled || local.loading;
|
const isDisabledOrLoading = local.disabled || local.loading;
|
||||||
@@ -77,3 +77,6 @@ export default function Button(props: ButtonProps) {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Named export for consistency
|
||||||
|
export { Button };
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default function PasswordInput(props: PasswordInputProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div class="flex flex-col items-center gap-2">
|
||||||
<div class={local.containerClass || "input-group relative mx-4 mb-2"}>
|
<div class={local.containerClass || "input-group relative mx-4 mb-2"}>
|
||||||
<Input
|
<Input
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
@@ -36,7 +36,7 @@ export default function PasswordInput(props: PasswordInputProps) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPassword(!showPassword())}
|
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"}
|
aria-label={showPassword() ? "Hide password" : "Show password"}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
@@ -56,10 +56,8 @@ export default function PasswordInput(props: PasswordInputProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{local.showStrength && local.passwordValue !== undefined && (
|
{local.showStrength && local.passwordValue !== undefined && (
|
||||||
<div class="px-4 pt-1">
|
<PasswordStrengthMeter password={local.passwordValue} />
|
||||||
<PasswordStrengthMeter password={local.passwordValue} />
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
import { useNavigate } from "@solidjs/router";
|
import { useNavigate } from "@solidjs/router";
|
||||||
import { createEffect, createSignal, For } from "solid-js";
|
import { createEffect, createSignal, For } from "solid-js";
|
||||||
@@ -51,10 +51,9 @@ export default function Page_401() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>401 Unauthorized | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="401 Unauthorized"
|
||||||
name="description"
|
description="401 - Unauthorized access. Please log in to access this page."
|
||||||
content="401 - Unauthorized access. Please log in to access this page."
|
|
||||||
/>
|
/>
|
||||||
<HttpStatusCode code={401} />
|
<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">
|
<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">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
import { HttpStatusCode } from "@solidjs/start";
|
||||||
import { useNavigate, useLocation } from "@solidjs/router";
|
import { useNavigate, useLocation } from "@solidjs/router";
|
||||||
import { createSignal, Show } from "solid-js";
|
import { createSignal, Show } from "solid-js";
|
||||||
@@ -101,10 +101,9 @@ export default function NotFound() {
|
|||||||
{/*@ts-ignore (intentional crash)*/}
|
{/*@ts-ignore (intentional crash)*/}
|
||||||
<CrashComponent />
|
<CrashComponent />
|
||||||
</Show>
|
</Show>
|
||||||
<Title>404 Not Found | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="404 Not Found"
|
||||||
name="description"
|
description="404 - Page not found. The page you're looking for doesn't exist."
|
||||||
content="404 - Page not found. The page you're looking for doesn't exist."
|
|
||||||
/>
|
/>
|
||||||
<HttpStatusCode code={404} />
|
<HttpStatusCode code={404} />
|
||||||
<TerminalErrorPage
|
<TerminalErrorPage
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSignal, Show, createEffect } from "solid-js";
|
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 { useNavigate, redirect, query, createAsync } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import XCircle from "~/components/icons/XCircle";
|
import XCircle from "~/components/icons/XCircle";
|
||||||
@@ -486,10 +486,9 @@ export default function AccountPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Account | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Account"
|
||||||
name="description"
|
description="Manage your account settings, update profile information, and configure preferences."
|
||||||
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="bg-base mx-8 min-h-screen md:mx-24 lg:mx-36">
|
||||||
@@ -683,23 +682,17 @@ export default function AccountPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={
|
disabled={
|
||||||
emailButtonLoading() ||
|
userProfile().email !== null &&
|
||||||
(userProfile().email !== null &&
|
!userProfile().emailVerified
|
||||||
!userProfile().emailVerified)
|
|
||||||
}
|
}
|
||||||
class={`${
|
loading={emailButtonLoading()}
|
||||||
emailButtonLoading() ||
|
class="mt-2"
|
||||||
(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`}
|
|
||||||
>
|
>
|
||||||
{emailButtonLoading() ? "Submitting..." : "Submit"}
|
Submit
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Show when={showEmailSuccess()}>
|
<Show when={showEmailSuccess()}>
|
||||||
<div class="text-green mt-2 text-center text-sm">
|
<div class="text-green mt-2 text-center text-sm">
|
||||||
@@ -740,19 +733,13 @@ export default function AccountPage() {
|
|||||||
containerClass="input-group mx-4"
|
containerClass="input-group mx-4"
|
||||||
/>
|
/>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={displayNameButtonLoading()}
|
loading={displayNameButtonLoading()}
|
||||||
class={`${
|
class="mt-2"
|
||||||
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`}
|
|
||||||
>
|
>
|
||||||
{displayNameButtonLoading()
|
Submit
|
||||||
? "Submitting..."
|
</Button>
|
||||||
: "Submit"}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<Show when={showDisplayNameSuccess()}>
|
<Show when={showDisplayNameSuccess()}>
|
||||||
<div class="text-green mt-2 text-center text-sm">
|
<div class="text-green mt-2 text-center text-sm">
|
||||||
@@ -838,17 +825,14 @@ export default function AccountPage() {
|
|||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={passwordChangeLoading() || !passwordsMatch()}
|
disabled={!passwordsMatch()}
|
||||||
class={`${
|
loading={passwordChangeLoading()}
|
||||||
passwordChangeLoading() || !passwordsMatch()
|
class="my-6"
|
||||||
? "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`}
|
|
||||||
>
|
>
|
||||||
{passwordChangeLoading() ? "Setting..." : "Set"}
|
Set
|
||||||
</button>
|
</Button>
|
||||||
|
|
||||||
<Show when={passwordError()}>
|
<Show when={passwordError()}>
|
||||||
<div class="text-red text-center text-sm">
|
<div class="text-red text-center text-sm">
|
||||||
@@ -871,18 +855,15 @@ export default function AccountPage() {
|
|||||||
|
|
||||||
{/* Sign Out Section */}
|
{/* Sign Out Section */}
|
||||||
<div class="mx-auto max-w-md py-4">
|
<div class="mx-auto max-w-md py-4">
|
||||||
<button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleSignOut}
|
onClick={handleSignOut}
|
||||||
disabled={signOutLoading()}
|
loading={signOutLoading()}
|
||||||
class={`${
|
variant="secondary"
|
||||||
signOutLoading()
|
class="w-full"
|
||||||
? "bg-overlay0 cursor-not-allowed opacity-75"
|
|
||||||
: "bg-overlay0 hover:bg-overlay1"
|
|
||||||
} w-full rounded px-4 py-2 transition-all`}
|
|
||||||
>
|
>
|
||||||
{signOutLoading() ? "Signing Out..." : "Sign Out"}
|
Sign Out
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="mt-8 mb-8" />
|
<hr class="mt-8 mb-8" />
|
||||||
@@ -938,19 +919,14 @@ export default function AccountPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={deleteAccountButtonLoading()}
|
loading={deleteAccountButtonLoading()}
|
||||||
class={`${
|
variant="danger"
|
||||||
deleteAccountButtonLoading()
|
class="border-text mx-auto mt-4 border"
|
||||||
? "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`}
|
|
||||||
>
|
>
|
||||||
{deleteAccountButtonLoading()
|
Delete Account
|
||||||
? "Deleting..."
|
</Button>
|
||||||
: "Delete Account"}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Show when={passwordDeletionError()}>
|
<Show when={passwordDeletionError()}>
|
||||||
<div class="text-red mt-2 text-center text-sm">
|
<div class="text-red mt-2 text-center text-sm">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSignal, Show, For, createEffect, ErrorBoundary } from "solid-js";
|
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 { redirect, query, createAsync, useNavigate } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
@@ -128,7 +128,10 @@ export default function AnalyticsPage() {
|
|||||||
|
|
||||||
return (
|
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="bg-base min-h-screen px-4 py-8">
|
||||||
<div class="mx-auto max-w-7xl">
|
<div class="mx-auto max-w-7xl">
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
query,
|
query,
|
||||||
useSearchParams
|
useSearchParams
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
import { getRequestEvent } from "solid-js/web";
|
||||||
import SessionDependantLike from "~/components/blog/SessionDependantLike";
|
import SessionDependantLike from "~/components/blog/SessionDependantLike";
|
||||||
@@ -316,12 +316,9 @@ export default function PostPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>
|
<PageHead
|
||||||
{p().title.replaceAll("_", " ")} | Michael Freno
|
title={p().title.replaceAll("_", " ")}
|
||||||
</Title>
|
description={
|
||||||
<Meta
|
|
||||||
name="description"
|
|
||||||
content={
|
|
||||||
p().subtitle ||
|
p().subtitle ||
|
||||||
`Read ${p().title.replaceAll("_", " ")} by Michael Freno on the freno.me blog.`
|
`Read ${p().title.replaceAll("_", " ")} by Michael Freno on the freno.me blog.`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Show, lazy } from "solid-js";
|
import { Show, lazy } from "solid-js";
|
||||||
import { query, redirect } from "@solidjs/router";
|
import { query, redirect } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import { Spinner } from "~/components/Spinner";
|
import { Spinner } from "~/components/Spinner";
|
||||||
@@ -31,10 +31,9 @@ export default function CreatePost() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Create Blog Post | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Create Blog Post"
|
||||||
name="description"
|
description="Create a new blog post with rich text editing, image uploads, and tag management."
|
||||||
content="Create a new blog post with rich text editing, image uploads, and tag management."
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Show when={authState()?.userID} fallback={<Spinner />}>
|
<Show when={authState()?.userID} fallback={<Spinner />}>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Show, lazy } from "solid-js";
|
import { Show, lazy } from "solid-js";
|
||||||
import { useParams, query } from "@solidjs/router";
|
import { useParams, query, redirect } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import "../post.css";
|
import "../post.css";
|
||||||
@@ -66,10 +66,9 @@ export default function EditPost() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Edit Post | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Edit Post"
|
||||||
name="description"
|
description="Edit your blog post with rich text editing, image management, and tag updates."
|
||||||
content="Edit your blog post with rich text editing, image management, and tag updates."
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Show
|
<Show
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Show } from "solid-js";
|
import { Show } from "solid-js";
|
||||||
import { useSearchParams, A, query } from "@solidjs/router";
|
import { useSearchParams, A, query } from "@solidjs/router";
|
||||||
import { Title } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { createAsync } from "@solidjs/router";
|
import { createAsync } from "@solidjs/router";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
import { getRequestEvent } from "solid-js/web";
|
||||||
import PostSortingSelect from "~/components/blog/PostSortingSelect";
|
import PostSortingSelect from "~/components/blog/PostSortingSelect";
|
||||||
@@ -91,7 +91,10 @@ export default function BlogIndex() {
|
|||||||
|
|
||||||
return (
|
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">
|
<div class="mx-auto py-16 pb-24">
|
||||||
<Show when={data()} fallback={<TerminalSplash />}>
|
<Show when={data()} fallback={<TerminalSplash />}>
|
||||||
|
|||||||
@@ -6,15 +6,16 @@ import {
|
|||||||
query,
|
query,
|
||||||
createAsync
|
createAsync
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
|
||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { action, redirect } from "@solidjs/router";
|
import { action, redirect } from "@solidjs/router";
|
||||||
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import { getClientCookie, setClientCookie } from "~/lib/cookies.client";
|
import { getClientCookie, setClientCookie } from "~/lib/cookies.client";
|
||||||
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
||||||
import LoadingSpinner from "~/components/LoadingSpinner";
|
import LoadingSpinner from "~/components/LoadingSpinner";
|
||||||
import RevealDropDown from "~/components/RevealDropDown";
|
import RevealDropDown from "~/components/RevealDropDown";
|
||||||
import Input from "~/components/ui/Input";
|
import Input from "~/components/ui/Input";
|
||||||
|
import { Button } from "~/components/ui/Button";
|
||||||
import type { UserProfile } from "~/types/user";
|
import type { UserProfile } from "~/types/user";
|
||||||
import { getCookie, setCookie } from "vinxi/http";
|
import { getCookie, setCookie } from "vinxi/http";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -360,8 +361,7 @@ export default function ContactPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Contact | Michael Freno</Title>
|
<PageHead title="Contact" description="Contact Me" />
|
||||||
<Meta name="description" content="Contact Me" />
|
|
||||||
|
|
||||||
<div class="bg-base flex min-h-screen w-full justify-center">
|
<div class="bg-base flex min-h-screen w-full justify-center">
|
||||||
<div class="w-full max-w-4xl px-4 pt-[20vh]">
|
<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
|
countDown() > 0 || (contactData()?.remainingTime ?? 0) > 0
|
||||||
}
|
}
|
||||||
fallback={
|
fallback={
|
||||||
<button
|
<Button type="submit" loading={loading()} class="w-36">
|
||||||
type="submit"
|
Send Message
|
||||||
disabled={loading()}
|
</Button>
|
||||||
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>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Show
|
<Show
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import DeletionForm from "~/components/DeletionForm";
|
import DeletionForm from "~/components/DeletionForm";
|
||||||
|
|
||||||
export default function LifeAndLinageDeletionForm() {
|
export default function LifeAndLinageDeletionForm() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Account Deletion - Life and Lineage | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Account Deletion - Life and Lineage"
|
||||||
name="description"
|
description="Request account deletion for Life and Lineage. Remove all your data from our system with a 24-hour grace period."
|
||||||
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="pt-20">
|
||||||
<div class="mx-auto p-4 md:p-6 lg:p-12">
|
<div class="mx-auto p-4 md:p-6 lg:p-12">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { createSignal, onMount, onCleanup } from "solid-js";
|
import { createSignal, onMount, onCleanup } from "solid-js";
|
||||||
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
|
import DownloadOnAppStore from "~/components/icons/DownloadOnAppStore";
|
||||||
@@ -50,10 +50,9 @@ export default function DownloadsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Downloads | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Downloads"
|
||||||
name="description"
|
description="Download Life and Lineage, Shapes with Abigail, and Cork for macOS. Available on iOS, Android, and macOS."
|
||||||
content="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">
|
<div class="bg-base relative min-h-screen overflow-hidden px-4 pt-[15vh] pb-12 md:px-8">
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { DarkModeToggle } from "~/components/DarkModeToggle";
|
import { DarkModeToggle } from "~/components/DarkModeToggle";
|
||||||
import { Typewriter } from "~/components/Typewriter";
|
import { Typewriter } from "~/components/Typewriter";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Home | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Home"
|
||||||
name="description"
|
description="Michael Freno - Software Engineer based in Brooklyn, NY"
|
||||||
content="Michael Freno - Software Engineer based in Brooklyn, NY"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main class="flex h-full flex-col gap-8 px-4 py-16 text-xl">
|
<main class="flex h-full flex-col gap-8 px-4 py-16 text-xl">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
redirect,
|
redirect,
|
||||||
query
|
query
|
||||||
} from "@solidjs/router";
|
} from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { getEvent } from "vinxi/http";
|
import { getEvent } from "vinxi/http";
|
||||||
import GoogleLogo from "~/components/icons/GoogleLogo";
|
import GoogleLogo from "~/components/icons/GoogleLogo";
|
||||||
import GitHub from "~/components/icons/GitHub";
|
import GitHub from "~/components/icons/GitHub";
|
||||||
@@ -18,6 +18,7 @@ import { env } from "~/env/client";
|
|||||||
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
|
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
|
||||||
import Input from "~/components/ui/Input";
|
import Input from "~/components/ui/Input";
|
||||||
import PasswordInput from "~/components/ui/PasswordInput";
|
import PasswordInput from "~/components/ui/PasswordInput";
|
||||||
|
import { Button } from "~/components/ui/Button";
|
||||||
|
|
||||||
const checkAuth = query(async () => {
|
const checkAuth = query(async () => {
|
||||||
"use server";
|
"use server";
|
||||||
@@ -318,10 +319,9 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Login | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Login"
|
||||||
name="description"
|
description="Sign in to your account or register for a new account to access personalized features and manage your profile."
|
||||||
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">
|
<div class="flex h-dvh flex-row justify-evenly">
|
||||||
<div class="relative pt-12 md:pt-24">
|
<div class="relative pt-12 md:pt-24">
|
||||||
@@ -480,21 +480,13 @@ export default function LoginPage() {
|
|||||||
<Show
|
<Show
|
||||||
when={!register() && !usePassword() && countDown() > 0}
|
when={!register() && !usePassword() && countDown() > 0}
|
||||||
fallback={
|
fallback={
|
||||||
<button
|
<Button type="submit" loading={loading()} class="w-36">
|
||||||
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`}
|
|
||||||
>
|
|
||||||
{register()
|
{register()
|
||||||
? "Sign Up"
|
? "Sign Up"
|
||||||
: usePassword()
|
: usePassword()
|
||||||
? "Sign In"
|
? "Sign In"
|
||||||
: "Get Link"}
|
: "Get Link"}
|
||||||
</button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<CountdownCircleTimer
|
<CountdownCircleTimer
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { createSignal, createEffect, Show } from "solid-js";
|
import { createSignal, createEffect, Show } from "solid-js";
|
||||||
import { A, useNavigate, useSearchParams } from "@solidjs/router";
|
import { A, useNavigate, useSearchParams } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
||||||
import { validatePassword } from "~/lib/validation";
|
import { validatePassword } from "~/lib/validation";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
|
import { VALIDATION_CONFIG, COUNTDOWN_CONFIG } from "~/config";
|
||||||
import PasswordInput from "~/components/ui/PasswordInput";
|
import PasswordInput from "~/components/ui/PasswordInput";
|
||||||
import PasswordStrengthMeter from "~/components/PasswordStrengthMeter";
|
import PasswordStrengthMeter from "~/components/PasswordStrengthMeter";
|
||||||
|
import { Button } from "~/components/ui/Button";
|
||||||
|
|
||||||
export default function PasswordResetPage() {
|
export default function PasswordResetPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -153,10 +154,9 @@ export default function PasswordResetPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Reset Password | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Reset Password"
|
||||||
name="description"
|
description="Set a new password for your account to regain access to your profile and personalized features."
|
||||||
content="Set a new password for your account to regain access to your profile and personalized features."
|
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div class="pt-24 text-center text-xl font-semibold">
|
<div class="pt-24 text-center text-xl font-semibold">
|
||||||
@@ -222,17 +222,14 @@ export default function PasswordResetPage() {
|
|||||||
<Show
|
<Show
|
||||||
when={countDown()}
|
when={countDown()}
|
||||||
fallback={
|
fallback={
|
||||||
<button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={passwordChangeLoading() || !passwordsMatch()}
|
disabled={!passwordsMatch()}
|
||||||
class={`${
|
loading={passwordChangeLoading()}
|
||||||
passwordChangeLoading() || !passwordsMatch()
|
class="my-6"
|
||||||
? "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`}
|
|
||||||
>
|
>
|
||||||
{passwordChangeLoading() ? "Setting..." : "Set New Password"}
|
Set New Password
|
||||||
</button>
|
</Button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="mx-auto pt-4">
|
<div class="mx-auto pt-4">
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
|
import { createSignal, createEffect, onCleanup, Show } from "solid-js";
|
||||||
import { A, useNavigate } from "@solidjs/router";
|
import { A, useNavigate } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
import CountdownCircleTimer from "~/components/CountdownCircleTimer";
|
||||||
import { isValidEmail } from "~/lib/validation";
|
import { isValidEmail } from "~/lib/validation";
|
||||||
import { getClientCookie } from "~/lib/cookies.client";
|
import { getClientCookie } from "~/lib/cookies.client";
|
||||||
import { COUNTDOWN_CONFIG } from "~/config";
|
import { COUNTDOWN_CONFIG } from "~/config";
|
||||||
import Input from "~/components/ui/Input";
|
import Input from "~/components/ui/Input";
|
||||||
|
import { Button } from "~/components/ui/Button";
|
||||||
|
|
||||||
export default function RequestPasswordResetPage() {
|
export default function RequestPasswordResetPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -123,10 +124,9 @@ export default function RequestPasswordResetPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Request Password Reset | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Request Password Reset"
|
||||||
name="description"
|
description="Request a password reset link to regain access to your account. Enter your email to receive reset instructions."
|
||||||
content="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">
|
<div class="pt-24 text-center text-xl font-semibold">
|
||||||
Password Reset Request
|
Password Reset Request
|
||||||
@@ -152,17 +152,9 @@ export default function RequestPasswordResetPage() {
|
|||||||
<Show
|
<Show
|
||||||
when={countDown() > 0}
|
when={countDown() > 0}
|
||||||
fallback={
|
fallback={
|
||||||
<button
|
<Button type="submit" loading={loading()} class="my-6">
|
||||||
type="submit"
|
Request Password Reset
|
||||||
disabled={loading()}
|
</Button>
|
||||||
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>
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div class="mx-auto pt-4">
|
<div class="mx-auto pt-4">
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import SimpleParallax from "~/components/SimpleParallax";
|
import SimpleParallax from "~/components/SimpleParallax";
|
||||||
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
|
import DownloadOnAppStoreDark from "~/components/icons/DownloadOnAppStoreDark";
|
||||||
|
|
||||||
export default function LifeAndLineageMarketing() {
|
export default function LifeAndLineageMarketing() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Life and Lineage | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Life and Lineage"
|
||||||
name="description"
|
description="A dark fantasy adventure mobile game. Download Life and Lineage on the App Store and Google Play."
|
||||||
content="A dark fantasy adventure mobile game. Download Life and Lineage on the App Store and Google Play."
|
|
||||||
/>
|
/>
|
||||||
<SimpleParallax>
|
<SimpleParallax>
|
||||||
<div class="flex h-full flex-col items-center justify-center text-white">
|
<div class="flex h-full flex-col items-center justify-center text-white">
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
|
|
||||||
export default function PrivacyPolicy() {
|
export default function PrivacyPolicy() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Privacy Policy - Life and Lineage | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Privacy Policy - Life and Lineage"
|
||||||
name="description"
|
description="Privacy policy for Life and Lineage mobile game, outlining data collection, usage, and user rights."
|
||||||
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="min-h-screen px-[8vw] py-[10vh]">
|
||||||
<div class="py-4 text-xl">Life and Lineage's Privacy Policy</div>
|
<div class="py-4 text-xl">Life and Lineage's Privacy Policy</div>
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import { A } from "@solidjs/router";
|
import { A } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
|
|
||||||
export default function PrivacyPolicy() {
|
export default function PrivacyPolicy() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Privacy Policy - Shapes with Abigail | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Privacy Policy - Shapes with Abigail"
|
||||||
name="description"
|
description="Privacy policy for Shapes with Abigail app, explaining our commitment to child safety and non-collection of personal data."
|
||||||
content="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="bg-base">
|
||||||
<div class="min-h-screen px-[8vw] py-[8vh]">
|
<div class="min-h-screen px-[8vw] py-[8vh]">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Title, Meta } from "@solidjs/meta";
|
|
||||||
import { onCleanup, onMount } from "solid-js";
|
import { onCleanup, onMount } from "solid-js";
|
||||||
|
import { PageHead } from "~/components/PageHead";
|
||||||
|
|
||||||
export default function Resume() {
|
export default function Resume() {
|
||||||
let iframeRef: HTMLIFrameElement | undefined;
|
let iframeRef: HTMLIFrameElement | undefined;
|
||||||
@@ -25,10 +25,9 @@ export default function Resume() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Resume | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Resume"
|
||||||
name="description"
|
description="View Michael Freno's resume - Software Engineer."
|
||||||
content="View Michael Freno's resume - Software Engineer."
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main class="flex h-screen w-full flex-col">
|
<main class="flex h-screen w-full flex-col">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { createSignal } from "solid-js";
|
import { createSignal } from "solid-js";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import Input from "~/components/ui/Input";
|
import Input from "~/components/ui/Input";
|
||||||
import Button from "~/components/ui/Button";
|
import Button from "~/components/ui/Button";
|
||||||
import {
|
import {
|
||||||
@@ -44,10 +44,9 @@ export default function TestUtilsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>Utility Testing | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="Utility Testing"
|
||||||
name="description"
|
description="Testing page for form components and validation utilities."
|
||||||
content="Testing page for form components and validation utilities."
|
|
||||||
/>
|
/>
|
||||||
<main class="min-h-screen bg-gray-100 p-8">
|
<main class="min-h-screen bg-gray-100 p-8">
|
||||||
<div class="mx-auto max-w-2xl">
|
<div class="mx-auto max-w-2xl">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createSignal, For, Show } from "solid-js";
|
import { createSignal, For, Show } from "solid-js";
|
||||||
import { query, createAsync } from "@solidjs/router";
|
import { query, createAsync } from "@solidjs/router";
|
||||||
import { Title, Meta } from "@solidjs/meta";
|
import { PageHead } from "~/components/PageHead";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
import { getRequestEvent } from "solid-js/web";
|
||||||
import { api } from "~/lib/api";
|
import { api } from "~/lib/api";
|
||||||
|
|
||||||
@@ -914,10 +914,9 @@ export default function TestPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title>API Testing | Michael Freno</Title>
|
<PageHead
|
||||||
<Meta
|
title="API Testing"
|
||||||
name="description"
|
description="tRPC API testing dashboard for developers to test endpoints and verify functionality."
|
||||||
content="tRPC API testing dashboard for developers to test endpoints and verify functionality."
|
|
||||||
/>
|
/>
|
||||||
<Show
|
<Show
|
||||||
when={authState()?.privilegeLevel === "admin"}
|
when={authState()?.privilegeLevel === "admin"}
|
||||||
|
|||||||
Reference in New Issue
Block a user