code dump frontend

This commit is contained in:
2024-12-02 18:29:45 -05:00
parent 44f1f35caa
commit 9e8cf4b147
19 changed files with 681 additions and 184 deletions

View File

@ -0,0 +1,42 @@
import { PhotoCircle } from "./PhotoCircle.tsx";
export default function AuthorCard({
author,
isIdentified,
}: {
author: Author;
isIdentified?: boolean;
}) {
return (
<div
class={isIdentified
? "p-6 bg-[#45475a] rounded-lg shadow-md"
: "p-6 bg-[#45475a] rounded-lg shadow-md transition-all duration-300 ease-in-out hover:shadow-xl hover:scale-105"}
>
<div class="min-w-screen flex flex-col items-center justify-between bg-[#313244] sm:min-h-screen">
<div class="sm:mt-14 sm:mb-14 mt-12 mb-4 flex flex-col items-center gap-y-5 gap-x-10 md:flex-row">
<PhotoCircle
src={author.image ?? "/logo.svg"}
alt="Author's profile photo"
/>
<div class="space-y-2 text-center md:text-left">
<p class="text-2xl text-[#f5e0dc] font-bold sm:text-4xl">
{author.first_name} {author.last_name}
</p>
<p class="text-md font-medium text-[#f2cdcd] sm:text-xl">
{author.bio}
</p>
</div>
</div>
</div>
</div>
);
}
export type Author = {
author_id: number;
first_name: string;
last_name: string;
bio: string;
image?: string;
};

View File

@ -1,3 +1,63 @@
export default function Footer() {
return <div>THIS IS A FOOTER</div>;
return (
<footer class="bg-[#313244] text-[#cdd6f4] py-8">
<div class="container mx-auto px-4">
{/* Grid layout that switches from 2 to 1 column on small screens */}
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="space-y-2">
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="/Resume_Wyatt_miller.png"
>
RSS
</a>
<br />
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="/Resume_Wyatt_miller.png"
>
Sitemap
</a>
</div>
<div>
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="/Resume_Wyatt_miller.png"
>
Resume
</a>
<br />
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="mailto:wjmiller2016@gmail.com"
>
Email me
</a>
<br />
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="https://x.com/wymillerlinux"
>
X
</a>
<br />
<a
class="mb-8 text-[#cdd6f4] transition-all duration-300 ease-in-out hover:text-[#cba6f7] hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer visited:text-[#bac2de]"
href="https://github.com/wymillerlinux"
>
GitHub
</a>
</div>
</div>
<div class="border-t border-gray-700 mt-8 pt-4 text-center">
<p class="text-sm text-gray-400">
© {new Date().getFullYear()}{" "}
Miller Web Soultions. All Rights Reserved.
</p>
</div>
</div>
</footer>
);
}

View File

@ -1,6 +1,10 @@
import * as hi from "jsr:@preact-icons/hi2";
interface HeaderLink {
name: string;
linkTo: string;
// deno-lint-ignore no-explicit-any
icon: any;
newTab?: boolean;
}
@ -8,34 +12,53 @@ const headerLinks: Array<HeaderLink> = [
{
name: "Home",
linkTo: "/",
icon: <hi.HiOutlineHome />,
},
{
name: "Blog",
linkTo: "posts/",
icon: <hi.HiOutlineBookmarkSquare />,
},
{
name: "Projects",
linkTo: "projects",
linkTo: "projects/",
icon: <hi.HiOutlineWrenchScrewdriver />,
},
{
name: "Contact",
linkTo: "contact/",
icon: <hi.HiOutlinePencilSquare />,
},
];
export default function Header() {
return (
<div>
{headerLinks.map((l) => {
const newTab = l.newTab ? "_blank" : "_self";
return (
<div class="">
<a href={l.linkTo} target={newTab} class="">
{l.name}
<nav>
<div class="bg-[#313244] flex justify-center space-x-6 p-4">
{headerLinks.map((l) => {
const newTab = l.newTab ? "_blank" : "_self";
return (
<a
href={l.linkTo}
target={newTab}
class="text-[#cdd6f4]
text-lg
font-medium
transition-all
duration-300
ease-in-out
hover:text-[#cba6f7]
hover:drop-shadow-[0_0_20px_rgba(96,165,250,0.7)]
hover:scale-110
cursor-pointer"
>
<div class="flex items-center gap-2">
{l.icon} {l.name}
</div>
</a>
</div>
);
})}
</div>
);
})}
</div>
</nav>
);
}

View File

@ -0,0 +1,29 @@
export const PhotoCircle = function PhotoCircle({
src,
alt,
size = "w-48 h-48",
width = "256",
height = "256",
}: PhotoCircleOpts) {
return (
<div
className={`${size} rounded-full border-4 border-white shadow-md overflow-hidden`}
>
<img
src={src}
alt={alt ?? "A photo in circle"}
width={width}
height={height}
className="w-full h-full object-cover"
/>
</div>
);
};
type PhotoCircleOpts = {
src: string;
alt?: string;
size?: string;
width?: string;
height?: string;
};

View File

@ -0,0 +1,33 @@
import { convertUtc } from "../lib/convertUtc.ts";
import { truncateString } from "../lib/truncate.ts";
export const PostCard = function PostCard({ post }: { post: Post }) {
return (
<div class="p-6 bg-[#45475a] rounded-lg shadow-md transition-all duration-300 ease-in-out hover:shadow-xl hover:scale-105">
<a href={`${Deno.env.get("BASE_URI_WEB")}/posts/${post.post_id}`}>
<h2 class="text-white text-lg font-bold mb-2">{post.title}</h2>
<p class="text-white">
Written by{" "}
<a
class="text-white transition-all duration-300 ease-in-out hover:text-[#74c7ec] hover:drop-shadow-[0_0_10px_rgba(96,165,250,0.7)] hover:scale-110 cursor-pointer"
href={`${Deno.env.get("BASE_URI_WEB")}/authors/${post.author_id}`}
>
{post.first_name} {post.last_name}
</a>{" "}
at {convertUtc(post.created_at)}
</p>
<p class="text-gray-400">{truncateString(post.body, 15)}</p>
</a>
</div>
);
};
export type Post = {
post_id: number;
author_id: number;
first_name: string;
last_name: string;
title: string;
body: string;
created_at: string;
};

View File

@ -0,0 +1,19 @@
import { Post, PostCard } from "./PostCard.tsx";
interface PostOpts {
posts: Post[];
}
export const PostCarousel = function PostCarousel({ posts }: PostOpts) {
return (
<div class="post-carousel flex flex-col items-center justify-between bg-[#313244]">
<div class="flex items-center justify-center">
<div class="flex space-x-4">
{posts.map((post: Post) => {
return <PostCard post={post} />;
})}
</div>
</div>
</div>
);
};