Merge pull request #7 from derekslenk/amplify-updates

Fix Amplify environment variable configuration
This commit is contained in:
Derek Slenk 2025-06-27 14:58:38 -04:00 committed by GitHub
commit c26f2e3d98
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 47206 additions and 47183 deletions

View file

@ -1,9 +1,9 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(find:*)", "Bash(find:*)",
"Bash(ls:*)" "Bash(ls:*)"
], ],
"deny": [] "deny": []
} }
} }

100
.gitignore vendored
View file

@ -1,50 +1,50 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp
.pnp.* .pnp.*
.yarn/* .yarn/*
!.yarn/patches !.yarn/patches
!.yarn/plugins !.yarn/plugins
!.yarn/releases !.yarn/releases
!.yarn/versions !.yarn/versions
# testing # testing
/coverage /coverage
# next.js # next.js
/.next/ /.next/
/out/ /out/
# production # production
/build /build
# misc # misc
.DS_Store .DS_Store
*.pem *.pem
# debug # debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
.pnpm-debug.log* .pnpm-debug.log*
# vercel # vercel
.vercel .vercel
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
.genkit/* .genkit/*
.env* .env*
# firebase # firebase
firebase-debug.log firebase-debug.log
firestore-debug.log firestore-debug.log
# amplify # amplify
.amplify .amplify
amplify_outputs* amplify_outputs*
amplifyconfiguration* amplifyconfiguration*

20
amplify.yml Normal file
View file

@ -0,0 +1,20 @@
version: 1
frontend:
phases:
preBuild:
commands:
- npm ci
build:
commands:
- env | grep YOUTUBE_API_KEY || echo "YOUTUBE_API_KEY not found in environment"
- npm run build
artifacts:
baseDirectory: .next
files:
- '**/*'
cache:
paths:
- node_modules/**/*
- .next/cache/**/*
buildPath: /
appRoot: /

View file

@ -1,11 +1,11 @@
import { defineAuth } from '@aws-amplify/backend'; import { defineAuth } from '@aws-amplify/backend';
/** /**
* Define and configure your auth resource * Define and configure your auth resource
* @see https://docs.amplify.aws/gen2/build-a-backend/auth * @see https://docs.amplify.aws/gen2/build-a-backend/auth
*/ */
export const auth = defineAuth({ export const auth = defineAuth({
loginWith: { loginWith: {
email: true, email: true,
}, },
}); });

View file

@ -1,11 +1,11 @@
import { defineBackend } from '@aws-amplify/backend'; import { defineBackend } from '@aws-amplify/backend';
import { auth } from './auth/resource'; import { auth } from './auth/resource';
import { data } from './data/resource'; import { data } from './data/resource';
/** /**
* @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more * @see https://docs.amplify.aws/react/build-a-backend/ to add storage, functions, and more
*/ */
defineBackend({ defineBackend({
auth, auth,
data, data,
}); });

View file

@ -1,53 +1,53 @@
import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
/*== STEP 1 =============================================================== /*== STEP 1 ===============================================================
The section below creates a Todo database table with a "content" field. Try The section below creates a Todo database table with a "content" field. Try
adding a new "isDone" field as a boolean. The authorization rule below adding a new "isDone" field as a boolean. The authorization rule below
specifies that any unauthenticated user can "create", "read", "update", specifies that any unauthenticated user can "create", "read", "update",
and "delete" any "Todo" records. and "delete" any "Todo" records.
=========================================================================*/ =========================================================================*/
const schema = a.schema({ const schema = a.schema({
Todo: a Todo: a
.model({ .model({
content: a.string(), content: a.string(),
}) })
.authorization((allow) => [allow.guest()]), .authorization((allow) => [allow.guest()]),
}); });
export type Schema = ClientSchema<typeof schema>; export type Schema = ClientSchema<typeof schema>;
export const data = defineData({ export const data = defineData({
schema, schema,
authorizationModes: { authorizationModes: {
defaultAuthorizationMode: 'identityPool', defaultAuthorizationMode: 'identityPool',
}, },
}); });
/*== STEP 2 =============================================================== /*== STEP 2 ===============================================================
Go to your frontend source code. From your client-side code, generate a Go to your frontend source code. From your client-side code, generate a
Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY Data client to make CRUDL requests to your table. (THIS SNIPPET WILL ONLY
WORK IN THE FRONTEND CODE FILE.) WORK IN THE FRONTEND CODE FILE.)
Using JavaScript or Next.js React Server Components, Middleware, Server Using JavaScript or Next.js React Server Components, Middleware, Server
Actions or Pages Router? Review how to generate Data clients for those use Actions or Pages Router? Review how to generate Data clients for those use
cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/ cases: https://docs.amplify.aws/gen2/build-a-backend/data/connect-to-API/
=========================================================================*/ =========================================================================*/
/* /*
"use client" "use client"
import { generateClient } from "aws-amplify/data"; import { generateClient } from "aws-amplify/data";
import type { Schema } from "@/amplify/data/resource"; import type { Schema } from "@/amplify/data/resource";
const client = generateClient<Schema>() // use this Data client for CRUDL requests const client = generateClient<Schema>() // use this Data client for CRUDL requests
*/ */
/*== STEP 3 =============================================================== /*== STEP 3 ===============================================================
Fetch records from the database and use them in your frontend component. Fetch records from the database and use them in your frontend component.
(THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.) (THIS SNIPPET WILL ONLY WORK IN THE FRONTEND CODE FILE.)
=========================================================================*/ =========================================================================*/
/* For example, in a React component, you can use this snippet in your /* For example, in a React component, you can use this snippet in your
function's RETURN statement */ function's RETURN statement */
// const { data: todos } = await client.models.Todo.list() // const { data: todos } = await client.models.Todo.list()
// return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul> // return <ul>{todos.map(todo => <li key={todo.id}>{todo.content}</li>)}</ul>

View file

@ -1,3 +1,3 @@
{ {
"type": "module" "type": "module"
} }

View file

@ -1,17 +1,17 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es2022", "target": "es2022",
"module": "es2022", "module": "es2022",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"esModuleInterop": true, "esModuleInterop": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "strict": true,
"skipLibCheck": true, "skipLibCheck": true,
"paths": { "paths": {
"$amplify/*": [ "$amplify/*": [
"../.amplify/generated/*" "../.amplify/generated/*"
] ]
} }
} }
} }

View file

@ -24,6 +24,9 @@ const nextConfig: NextConfig = {
}, },
], ],
}, },
env: {
YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY || '',
},
}; };
export default nextConfig; export default nextConfig;

93556
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,75 +1,75 @@
{ {
"name": "nextn", "name": "nextn",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack -p 9002", "dev": "next dev --turbopack -p 9002",
"genkit:dev": "genkit start -- tsx src/ai/dev.ts", "genkit:dev": "genkit start -- tsx src/ai/dev.ts",
"genkit:watch": "genkit start -- tsx --watch src/ai/dev.ts", "genkit:watch": "genkit start -- tsx --watch src/ai/dev.ts",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@genkit-ai/googleai": "^1.13.0", "@genkit-ai/googleai": "^1.13.0",
"@genkit-ai/next": "^1.13.0", "@genkit-ai/next": "^1.13.0",
"@hookform/resolvers": "^4.1.3", "@hookform/resolvers": "^4.1.3",
"@radix-ui/react-accordion": "^1.2.3", "@radix-ui/react-accordion": "^1.2.3",
"@radix-ui/react-alert-dialog": "^1.1.6", "@radix-ui/react-alert-dialog": "^1.1.6",
"@radix-ui/react-avatar": "^1.1.3", "@radix-ui/react-avatar": "^1.1.3",
"@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6", "@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2", "@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-menubar": "^1.1.6", "@radix-ui/react-menubar": "^1.1.6",
"@radix-ui/react-popover": "^1.1.6", "@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-progress": "^1.1.2",
"@radix-ui/react-radio-group": "^1.2.3", "@radix-ui/react-radio-group": "^1.2.3",
"@radix-ui/react-scroll-area": "^1.2.3", "@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-select": "^2.1.6", "@radix-ui/react-select": "^2.1.6",
"@radix-ui/react-separator": "^1.1.2", "@radix-ui/react-separator": "^1.1.2",
"@radix-ui/react-slider": "^1.2.3", "@radix-ui/react-slider": "^1.2.3",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.1.3", "@radix-ui/react-switch": "^1.1.3",
"@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-toast": "^1.2.6",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"aws-amplify": "^6.15.1", "aws-amplify": "^6.15.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"firebase": "^11.9.1", "firebase": "^11.9.1",
"genkit": "^1.13.0", "genkit": "^1.13.0",
"lucide-react": "^0.475.0", "lucide-react": "^0.475.0",
"next": "15.3.3", "next": "15.3.3",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"patch-package": "^8.0.0", "patch-package": "^8.0.0",
"react": "^18.3.1", "react": "^18.3.1",
"react-day-picker": "^8.10.1", "react-day-picker": "^8.10.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.54.2", "react-hook-form": "^7.54.2",
"recharts": "^2.15.1", "recharts": "^2.15.1",
"tailwind-merge": "^3.0.1", "tailwind-merge": "^3.0.1",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2" "zod": "^3.24.2"
}, },
"devDependencies": { "devDependencies": {
"@aws-amplify/backend": "^1.16.1", "@aws-amplify/backend": "^1.16.1",
"@aws-amplify/backend-cli": "^1.8.0", "@aws-amplify/backend-cli": "^1.8.0",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"aws-cdk-lib": "^2.189.1", "aws-cdk-lib": "^2.189.1",
"constructs": "^10.4.2", "constructs": "^10.4.2",
"esbuild": "^0.25.5", "esbuild": "^0.25.5",
"genkit-cli": "^1.13.0", "genkit-cli": "^1.13.0",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.20.3", "tsx": "^4.20.3",
"typescript": "^5.8.3" "typescript": "^5.8.3"
} }
} }

View file

@ -1,151 +1,151 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import Link from 'next/link'; import Link from 'next/link';
import { ExternalLink } from 'lucide-react'; import { ExternalLink } from 'lucide-react';
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Video Gallery - Community Coverage of Chelsea Smallwood", title: "Video Gallery - Community Coverage of Chelsea Smallwood",
description: "Watch YouTube commentary and analysis on Chelsea Smallwood, The Other Woman and the Wife, and the ongoing controversy.", description: "Watch YouTube commentary and analysis on Chelsea Smallwood, The Other Woman and the Wife, and the ongoing controversy.",
}; };
interface Video { interface Video {
id: string; id: string;
title: string; title: string;
} }
// Replaced broken video IDs with working ones. // Replaced broken video IDs with working ones.
const videoIds = [ const videoIds = [
'MdTWPuNQ1B8', // Authentic Observer 'MdTWPuNQ1B8', // Authentic Observer
'e6rHHtq5K1k', 'e6rHHtq5K1k',
'-6Zftd8C7NE', // Coop '-6Zftd8C7NE', // Coop
'AbVsR7XzNBc', // clout 'AbVsR7XzNBc', // clout
'lJ8zHiwfrqs', 'lJ8zHiwfrqs',
'q8zevCJ6TKw' 'q8zevCJ6TKw'
// 'DK14VZ4Fyl4', // Lauren // 'DK14VZ4Fyl4', // Lauren
]; ];
// Updated fallback data to be a reliable source of working videos. // Updated fallback data to be a reliable source of working videos.
const fallbackData: Video[] = [ const fallbackData: Video[] = [
{ id: 'DK14VZ4Fyl4', title: 'Life Coach CHELSEA SMALLWOOD Is SUING Her HUSBANDS Ex Wife... It Gets WORSE' }, { id: 'DK14VZ4Fyl4', title: 'Life Coach CHELSEA SMALLWOOD Is SUING Her HUSBANDS Ex Wife... It Gets WORSE' },
{ id: '-6Zftd8C7NE', title: "The Husband Stealing, Cheating, \"TikTok Life Coach\"" }, { id: '-6Zftd8C7NE', title: "The Husband Stealing, Cheating, \"TikTok Life Coach\"" },
]; ];
async function getYouTubeVideos(ids: string[]): Promise<Video[]> { async function getYouTubeVideos(ids: string[]): Promise<Video[]> {
const apiKey = process.env.YOUTUBE_API_KEY; const apiKey = process.env.YOUTUBE_API_KEY;
if (!apiKey) { if (!apiKey) {
console.warn("YOUTUBE_API_KEY environment variable not set. Using hardcoded video titles as fallback."); console.warn("YOUTUBE_API_KEY environment variable not set. Using hardcoded video titles as fallback.");
return fallbackData; return fallbackData;
} }
const url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${ids.join(',')}&key=${apiKey}`; const url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${ids.join(',')}&key=${apiKey}`;
try { try {
const response = await fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hour const response = await fetch(url, { next: { revalidate: 3600 } }); // Revalidate every hour
if (!response.ok) { if (!response.ok) {
const errorData = await response.json(); const errorData = await response.json();
console.error("YouTube API Error:", errorData.error.message); console.error("YouTube API Error:", errorData.error.message);
console.log("Falling back to hardcoded video data."); console.log("Falling back to hardcoded video data.");
return fallbackData; return fallbackData;
} }
const data = await response.json(); const data = await response.json();
if (!data.items || data.items.length === 0) { if (!data.items || data.items.length === 0) {
console.warn("YouTube API returned no items for the given video IDs. Falling back to hardcoded data."); console.warn("YouTube API returned no items for the given video IDs. Falling back to hardcoded data.");
return fallbackData; return fallbackData;
} }
const fetchedVideos = data.items.map((item: any) => ({ const fetchedVideos = data.items.map((item: any) => ({
id: item.id, id: item.id,
title: item.snippet.title, title: item.snippet.title,
})); }));
if (fetchedVideos.length < ids.length) { if (fetchedVideos.length < ids.length) {
console.warn(`YouTube API only returned ${fetchedVideos.length} videos out of ${ids.length} requested. Some videos may be private or deleted.`); console.warn(`YouTube API only returned ${fetchedVideos.length} videos out of ${ids.length} requested. Some videos may be private or deleted.`);
} }
return fetchedVideos; return fetchedVideos;
} catch (error) { } catch (error) {
console.error("Failed to fetch from YouTube API:", error); console.error("Failed to fetch from YouTube API:", error);
console.log("Falling back to hardcoded video data."); console.log("Falling back to hardcoded video data.");
return fallbackData; return fallbackData;
} }
} }
export default async function GalleryPage() { export default async function GalleryPage() {
const videos = await getYouTubeVideos(videoIds); const videos = await getYouTubeVideos(videoIds);
return ( return (
<div className="flex flex-col min-h-screen bg-background text-foreground"> <div className="flex flex-col min-h-screen bg-background text-foreground">
<main className="flex-grow"> <main className="flex-grow">
<div className="container mx-auto max-w-5xl py-12 px-4 sm:px-6 lg:px-8"> <div className="container mx-auto max-w-5xl py-12 px-4 sm:px-6 lg:px-8">
<header className="text-center mb-12"> <header className="text-center mb-12">
<h1 className="text-4xl sm:text-5xl font-extrabold tracking-tight text-primary font-headline"> <h1 className="text-4xl sm:text-5xl font-extrabold tracking-tight text-primary font-headline">
YouTube Community Coverage YouTube Community Coverage
</h1> </h1>
<p className="mt-4 text-xl text-muted-foreground"> <p className="mt-4 text-xl text-muted-foreground">
Commentary and analysis from creators across the platform. Commentary and analysis from creators across the platform.
</p> </p>
</header> </header>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{videos.length > 0 ? videos.map((video) => ( {videos.length > 0 ? videos.map((video) => (
<div key={video.id} className="bg-card rounded-lg shadow-sm border overflow-hidden flex flex-col"> <div key={video.id} className="bg-card rounded-lg shadow-sm border overflow-hidden flex flex-col">
<div className="relative w-full pt-[56.25%]"> {/* 16:9 Aspect Ratio */} <div className="relative w-full pt-[56.25%]"> {/* 16:9 Aspect Ratio */}
<iframe <iframe
className="absolute top-0 left-0 w-full h-full" className="absolute top-0 left-0 w-full h-full"
src={`https://www.youtube.com/embed/${video.id}`} src={`https://www.youtube.com/embed/${video.id}`}
title={video.title} title={video.title}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowFullScreen allowFullScreen
></iframe> ></iframe>
</div> </div>
<div className="p-4 flex-grow"> <div className="p-4 flex-grow">
<h2 className="text-lg font-semibold text-card-foreground"> <h2 className="text-lg font-semibold text-card-foreground">
{video.title} {video.title}
</h2> </h2>
<a href={`https://www.youtube.com/watch?v=${video.id}`} target="_blank" rel="noopener noreferrer" className="text-sm text-primary hover:underline flex items-center gap-1 mt-2"> <a href={`https://www.youtube.com/watch?v=${video.id}`} target="_blank" rel="noopener noreferrer" className="text-sm text-primary hover:underline flex items-center gap-1 mt-2">
Watch on YouTube <ExternalLink className="w-4 h-4" /> Watch on YouTube <ExternalLink className="w-4 h-4" />
</a> </a>
</div> </div>
</div> </div>
)) : ( )) : (
<p className="text-center col-span-full">Could not load videos. Please try again later.</p> <p className="text-center col-span-full">Could not load videos. Please try again later.</p>
)} )}
</div> </div>
<div className="text-center mt-12"> <div className="text-center mt-12">
<Button asChild> <Button asChild>
<Link href="/">Back to Home</Link> <Link href="/">Back to Home</Link>
</Button> </Button>
</div> </div>
</div> </div>
</main> </main>
<footer className="text-center py-6 text-sm text-muted-foreground"> <footer className="text-center py-6 text-sm text-muted-foreground">
<div className="container mx-auto"> <div className="container mx-auto">
<p> <p>
This website, cheatingchelsea.com, is dedicated to raising This website, cheatingchelsea.com, is dedicated to raising
awareness and supporting the victims. awareness and supporting the victims.
</p> </p>
<p> <p>
For questions, comments, or concerns, please email:{' '} For questions, comments, or concerns, please email:{' '}
<a <a
href="mailto:notacheater&#64;cheatingchelsea.com" href="mailto:notacheater&#64;cheatingchelsea.com"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
notacheater&#64;cheatingchelsea.com notacheater&#64;cheatingchelsea.com
</a> </a>
</p> </p>
<p className="mt-4 italic"> <p className="mt-4 italic">
Disclaimer: This website is independently operated by a snarky Disclaimer: This website is independently operated by a snarky
Michigander and is not affiliated with or endorsed by Kristen Michigander and is not affiliated with or endorsed by Kristen
Jacobs. Jacobs.
</p> </p>
<p className="mt-2">&copy; 2025 Cheating Chelsea Exposed. All Rights Reserved.</p> <p className="mt-2">&copy; 2025 Cheating Chelsea Exposed. All Rights Reserved.</p>
</div> </div>
</footer> </footer>
</div> </div>
); );
} }

View file

@ -1,28 +1,28 @@
import Link from 'next/link'; import Link from 'next/link';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import Image from 'next/image'; import Image from 'next/image';
export default function NotFound() { export default function NotFound() {
return ( return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground"> <div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground">
<main className="text-center p-8 max-w-2xl mx-auto"> <main className="text-center p-8 max-w-2xl mx-auto">
<div className="bg-black p-4 inline-block rounded-lg border-2 border-gray-700 shadow-2xl"> <div className="bg-black p-4 inline-block rounded-lg border-2 border-gray-700 shadow-2xl">
<Image <Image
src="/404-cat.jpg" src="/404-cat.jpg"
alt="A calico cat's tail is sticking out from under a pile of papers, resembling the '404 Not Found' cat meme." alt="A calico cat's tail is sticking out from under a pile of papers, resembling the '404 Not Found' cat meme."
width={750} width={750}
height={600} height={600}
className="object-cover" className="object-cover"
/> />
</div> </div>
<p className="text-muted-foreground mt-8 mb-8 text-xl"> <p className="text-muted-foreground mt-8 mb-8 text-xl">
Oops! It looks like the page you're looking for has gone into hiding. Oops! It looks like the page you're looking for has gone into hiding.
</p> </p>
<Button asChild size="lg"> <Button asChild size="lg">
<Link href="/">Go Back to Home</Link> <Link href="/">Go Back to Home</Link>
</Button> </Button>
</main> </main>
</div> </div>
); );
} }