code dump frontend
This commit is contained in:
42
frontend/components/AuthorCard.tsx
Normal file
42
frontend/components/AuthorCard.tsx
Normal 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;
|
||||
};
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
29
frontend/components/PhotoCircle.tsx
Normal file
29
frontend/components/PhotoCircle.tsx
Normal 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;
|
||||
};
|
33
frontend/components/PostCard.tsx
Normal file
33
frontend/components/PostCard.tsx
Normal 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;
|
||||
};
|
19
frontend/components/PostCarousel.tsx
Normal file
19
frontend/components/PostCarousel.tsx
Normal 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>
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user