Compare commits
4 Commits
9a84d4b78c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d9c0014ed | |||
| 3caf1e30b0 | |||
| b4ef9fd2e0 | |||
| 66f63523e2 |
@@ -26,7 +26,7 @@ export function Footer({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<footer className="border-t border-secondary bg-bg px-4 py-10">
|
<footer className="border-t border-secondary bg-bg px-4 py-10">
|
||||||
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-6 md:flex-row">
|
<div className="mx-auto max-w-7xl flex flex-col gap-6 md:grid md:grid-cols-3 md:items-end">
|
||||||
<div className="text-center md:text-left">
|
<div className="text-center md:text-left">
|
||||||
<div className="text-xl md:text-2xl font-extrabold font-name tracking-wide text-text">
|
<div className="text-xl md:text-2xl font-extrabold font-name tracking-wide text-text">
|
||||||
Jody Holt
|
Jody Holt
|
||||||
@@ -36,7 +36,7 @@ export function Footer({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className="flex items-center gap-5">
|
<nav className="flex items-center gap-5 md:justify-center">
|
||||||
<button
|
<button
|
||||||
className="text-text hover:text-primary anim-base"
|
className="text-text hover:text-primary anim-base"
|
||||||
onClick={() => document.getElementById("home")?.scrollIntoView({ behavior: "smooth" })}
|
onClick={() => document.getElementById("home")?.scrollIntoView({ behavior: "smooth" })}
|
||||||
@@ -57,7 +57,7 @@ export function Footer({
|
|||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex items-center gap-4 text-text">
|
<div className="flex items-center gap-4 text-text md:justify-end">
|
||||||
{socials.map((s) => (
|
{socials.map((s) => (
|
||||||
<a
|
<a
|
||||||
key={s.label}
|
key={s.label}
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ export function Navbar({ onNav }: { onNav: (id: string) => void }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`md:hidden overflow-hidden transition-[max-height,opacity,transform] duration-300 ease-out ${
|
className={`md:hidden transition-[max-height,opacity,transform] duration-300 ease-out ${
|
||||||
open
|
open
|
||||||
? "max-h-96 opacity-100 translate-y-0"
|
? "max-h-96 overflow-visible opacity-100 translate-y-0"
|
||||||
: "max-h-0 opacity-0 -translate-y-2 pointer-events-none"
|
: "max-h-0 overflow-hidden opacity-0 -translate-y-2 pointer-events-none"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="space-y-2 border-t border-secondary bg-bg px-4 py-3">
|
<div className="space-y-2 border-t border-secondary bg-bg px-4 py-3">
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ type Project = {
|
|||||||
videoMobile: string;
|
videoMobile: string;
|
||||||
techStack: string[];
|
techStack: string[];
|
||||||
liveUrl?: string;
|
liveUrl?: string;
|
||||||
|
beta?: boolean;
|
||||||
|
note?: string;
|
||||||
comingSoon?: boolean;
|
comingSoon?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const projects: Project[] = [
|
const projects: Project[] = [
|
||||||
{
|
{
|
||||||
id: "skymoney",
|
id: "skymoney",
|
||||||
title: "Skymoney",
|
title: "SkyMoney",
|
||||||
description:
|
description:
|
||||||
"A budgeting app that simulates your bank account to ensure financial discipline.",
|
"A budgeting app that simulates your bank account to ensure financial discipline.",
|
||||||
coverImage: skymoneycover,
|
coverImage: skymoneycover,
|
||||||
@@ -32,7 +34,9 @@ const projects: Project[] = [
|
|||||||
video: skymoneyvideo,
|
video: skymoneyvideo,
|
||||||
videoMobile: skymoneyvideoMobile,
|
videoMobile: skymoneyvideoMobile,
|
||||||
techStack: ["React", "TypeScript", "Node.js", "PostgreSQL"],
|
techStack: ["React", "TypeScript", "Node.js", "PostgreSQL"],
|
||||||
comingSoon: true,
|
liveUrl: "https://skymoneybudget.com",
|
||||||
|
beta: true,
|
||||||
|
note: "Contact Jody for beta access.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "miller-building",
|
id: "miller-building",
|
||||||
@@ -154,10 +158,10 @@ function ProjectCard({
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{/* Coming Soon Badge */}
|
{/* Beta Badge */}
|
||||||
{project.comingSoon && (
|
{project.beta && (
|
||||||
<div className="absolute top-3 right-3 px-3 py-1 rounded-full bg-primary text-white text-xs font-bold uppercase tracking-wider">
|
<div className="absolute top-3 right-3 px-3 py-1 rounded-full bg-primary text-white text-xs font-bold uppercase tracking-wider">
|
||||||
Coming Soon
|
Beta
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -187,10 +191,10 @@ function ProjectCard({
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
{/* Coming Soon Badge */}
|
{/* Beta Badge */}
|
||||||
{project.comingSoon && (
|
{project.beta && (
|
||||||
<div className="absolute top-3 right-3 px-3 py-1 rounded-full bg-primary text-white text-xs font-bold uppercase tracking-wider">
|
<div className="absolute top-3 right-3 px-3 py-1 rounded-full bg-primary text-white text-xs font-bold uppercase tracking-wider">
|
||||||
Coming Soon
|
Beta
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -201,6 +205,9 @@ function ProjectCard({
|
|||||||
<p className="text-text/70 text-sm mb-4 line-clamp-2">
|
<p className="text-text/70 text-sm mb-4 line-clamp-2">
|
||||||
{project.description}
|
{project.description}
|
||||||
</p>
|
</p>
|
||||||
|
{project.note && (
|
||||||
|
<p className="text-text/60 text-xs mb-4">{project.note}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Tech Stack */}
|
{/* Tech Stack */}
|
||||||
<div className="flex flex-wrap gap-2 mb-4">
|
<div className="flex flex-wrap gap-2 mb-4">
|
||||||
|
|||||||
@@ -1,57 +1,8 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { resumeData } from "../data/resume";
|
||||||
const contactInfo = {
|
|
||||||
location: "Amarillo, TX",
|
|
||||||
phone: "806.654.2813",
|
|
||||||
email: "jholt1008@gmail.com",
|
|
||||||
linkedin: "https://www.linkedin.com/in/jody-holt-9b19b0256",
|
|
||||||
};
|
|
||||||
|
|
||||||
const summary = `Detail-oriented software developer skilled in building full-stack applications using React, TypeScript, Node/Express, SQL, and Docker. Experienced in designing responsive user interfaces, structuring maintainable front-end architectures, and developing reliable, modular APIs. Strong communicator with proven ability to solve problems quickly, learn new technologies efficiently, and deliver clean, scalable code across multiple projects.`;
|
|
||||||
|
|
||||||
const skills = [
|
|
||||||
{
|
|
||||||
category: "Front-End Development",
|
|
||||||
items: ["React", "TypeScript", "Responsive UI/UX", "Component Architecture", "Entity Framework Core", "TailwindCSS"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: "Back-End & APIs",
|
|
||||||
items: ["Node.js", "Express.js", "RESTful API", "Authentication Flows", "Data Validation", "C#", ".NET Core"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: "Database & Data Modeling",
|
|
||||||
items: ["SQL", "PostgreSQL", "CRUD Operations", "Query Optimization", "Object-Oriented Analysis & Design"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: "DevOps & Tools",
|
|
||||||
items: ["Docker Compose", "Git/GitHub", "Software Migration", "Multi-Container Setups"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: "Software Engineering",
|
|
||||||
items: ["Clear Communication", "Modular Code Design", "Collaboration", "Rapid Learning", "Problem-Solving"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const accomplishments = [
|
|
||||||
"Meta's Front-End Web Development and Data Engineering certificate programs",
|
|
||||||
"Built responsive React applications featuring structured component trees & dynamic routing",
|
|
||||||
"Designed SQL databases with optimal CRUD operations & well-structured queries",
|
|
||||||
"Containerized full-stack apps with Docker Compose for optimal scaling, resolved network, environment, version control, and dependency issues",
|
|
||||||
"Created reusable UI components and interactive features that improved consistency and flow, user-friendly animations and enticing UX",
|
|
||||||
];
|
|
||||||
|
|
||||||
const workHistory = [
|
|
||||||
{ title: "Training Specialist", company: "Subway", location: "Canyon, TX", dates: "2024–Present" },
|
|
||||||
{ title: "Head Lifeguard", company: "Johnson Park Youth Center", location: "Borger, TX", dates: "Seasonal 2022–2025" },
|
|
||||||
{ title: "Sacker/Grocery Stocker", company: "United Supermarkets", location: "Canyon, TX", dates: "2023–2024" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const education = [
|
|
||||||
{ degree: "M.S. in Computer Information Systems and Business Analytics", school: "West Texas A&M University", date: "May 2027" },
|
|
||||||
{ degree: "B.S. in Computer Information Systems", school: "West Texas A&M University", date: "May 2026" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function Resume() {
|
export function Resume() {
|
||||||
|
const { contactInfo, summary, skills, certifications, projects, workHistory, education } = resumeData;
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-5xl px-4 py-16 md:py-24">
|
<div className="mx-auto max-w-5xl px-4 py-16 md:py-24">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -67,6 +18,22 @@ export function Resume() {
|
|||||||
<a href={`mailto:${contactInfo.email}`} className="hover:text-primary anim-base">
|
<a href={`mailto:${contactInfo.email}`} className="hover:text-primary anim-base">
|
||||||
{contactInfo.email}
|
{contactInfo.email}
|
||||||
</a>
|
</a>
|
||||||
|
{contactInfo.website && (
|
||||||
|
<>
|
||||||
|
<span className="hidden sm:inline text-primary">•</span>
|
||||||
|
<a href={contactInfo.website} className="hover:text-primary anim-base">
|
||||||
|
{new URL(contactInfo.website).hostname}
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{contactInfo.linkedin && (
|
||||||
|
<>
|
||||||
|
<span className="hidden sm:inline text-primary">•</span>
|
||||||
|
<a href={contactInfo.linkedin} className="hover:text-primary anim-base">
|
||||||
|
LinkedIn
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -101,11 +68,12 @@ export function Resume() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Accomplishments */}
|
{/* Certifications */}
|
||||||
|
{certifications && certifications.length > 0 && (
|
||||||
<section className="mb-10 anim-fade-in">
|
<section className="mb-10 anim-fade-in">
|
||||||
<SectionTitle>Professional Accomplishments</SectionTitle>
|
<SectionTitle>Certifications</SectionTitle>
|
||||||
<ul className="space-y-3">
|
<ul className="space-y-3">
|
||||||
{accomplishments.map((item, i) => (
|
{certifications.map((item, i) => (
|
||||||
<li key={i} className="flex gap-3 text-text/85">
|
<li key={i} className="flex gap-3 text-text/85">
|
||||||
<span className="mt-2 h-2 w-2 shrink-0 rounded-full bg-primary" />
|
<span className="mt-2 h-2 w-2 shrink-0 rounded-full bg-primary" />
|
||||||
<span>{item}</span>
|
<span>{item}</span>
|
||||||
@@ -113,6 +81,34 @@ export function Resume() {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Projects */}
|
||||||
|
{projects && projects.length > 0 && (
|
||||||
|
<section className="mb-10 anim-fade-in">
|
||||||
|
<SectionTitle>Projects</SectionTitle>
|
||||||
|
<div className="space-y-4">
|
||||||
|
{projects.map((project) => (
|
||||||
|
<div
|
||||||
|
key={project.name}
|
||||||
|
className="rounded-lg border border-secondary bg-secondary/20 p-4 anim-base hover:border-primary/50"
|
||||||
|
>
|
||||||
|
<h4 className="font-semibold text-text">
|
||||||
|
{project.name} <span className="text-text/70">| {project.stack}</span>
|
||||||
|
</h4>
|
||||||
|
<ul className="mt-2 space-y-2">
|
||||||
|
{project.bullets.map((b, i) => (
|
||||||
|
<li key={i} className="flex gap-3 text-text/85">
|
||||||
|
<span className="mt-2 h-2 w-2 shrink-0 rounded-full bg-primary" />
|
||||||
|
<span>{b}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Work History */}
|
{/* Work History */}
|
||||||
<section className="mb-10 anim-fade-in">
|
<section className="mb-10 anim-fade-in">
|
||||||
@@ -121,8 +117,9 @@ export function Resume() {
|
|||||||
{workHistory.map((job, i) => (
|
{workHistory.map((job, i) => (
|
||||||
<div
|
<div
|
||||||
key={i}
|
key={i}
|
||||||
className="flex flex-col sm:flex-row sm:items-center sm:justify-between rounded-lg border border-secondary bg-secondary/20 p-4 anim-base hover:border-primary/50"
|
className="flex flex-col rounded-lg border border-secondary bg-secondary/20 p-4 anim-base hover:border-primary/50"
|
||||||
>
|
>
|
||||||
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-text">{job.title}</h4>
|
<h4 className="font-semibold text-text">{job.title}</h4>
|
||||||
<p className="text-text/70 text-sm">
|
<p className="text-text/70 text-sm">
|
||||||
@@ -131,6 +128,17 @@ export function Resume() {
|
|||||||
</div>
|
</div>
|
||||||
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{job.dates}</span>
|
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{job.dates}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{job.bullets && job.bullets.length > 0 && (
|
||||||
|
<ul className="mt-2 space-y-2">
|
||||||
|
{job.bullets.map((b, idx) => (
|
||||||
|
<li key={idx} className="flex gap-3 text-text/85">
|
||||||
|
<span className="mt-2 h-2 w-2 shrink-0 rounded-full bg-primary" />
|
||||||
|
<span>{b}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -146,7 +154,10 @@ export function Resume() {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold text-text">{edu.degree}</h4>
|
<h4 className="font-semibold text-text">{edu.degree}</h4>
|
||||||
<p className="text-text/70 text-sm">{edu.school}</p>
|
<p className="text-text/70 text-sm">
|
||||||
|
{edu.school}
|
||||||
|
{edu.details ? ` | ${edu.details}` : ""}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{edu.date}</span>
|
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{edu.date}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// ThemeToggle.tsx
|
// ThemeToggle.tsx
|
||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
import { useTheme } from "../hooks/useTheme";
|
import { useTheme } from "../hooks/useTheme";
|
||||||
|
|
||||||
// Actual primary colors for each theme
|
// Actual primary colors for each theme
|
||||||
@@ -13,6 +13,7 @@ const themeColors: Record<string, { primary: string; label: string }> = {
|
|||||||
|
|
||||||
export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
const themes = ["a", "b", "c", "d", "e"] as const;
|
const themes = ["a", "b", "c", "d", "e"] as const;
|
||||||
|
|
||||||
const crossfadeTo = (next: typeof themes[number]) => {
|
const crossfadeTo = (next: typeof themes[number]) => {
|
||||||
@@ -27,6 +28,7 @@ export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
|||||||
|
|
||||||
// 3) switch theme (your existing logic)
|
// 3) switch theme (your existing logic)
|
||||||
setTheme(next as any);
|
setTheme(next as any);
|
||||||
|
setOpen(false);
|
||||||
|
|
||||||
// 4) remove crossfade flag after the animation
|
// 4) remove crossfade flag after the animation
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
@@ -37,26 +39,31 @@ export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative inline-block text-text">
|
<div className="relative inline-block text-text">
|
||||||
<details className="group">
|
<button
|
||||||
<summary className="cursor-pointer select-none list-none inline-flex items-center gap-2 rounded px-3 py-1.5 bg-secondary/70 hover:bg-secondary focus:outline-none anim-base hover-pop">
|
type="button"
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
className="cursor-pointer select-none inline-flex items-center gap-2 rounded px-3 py-1.5 bg-secondary/70 hover:bg-secondary focus:outline-none anim-base hover-pop"
|
||||||
|
onClick={() => setOpen((v) => !v)}
|
||||||
|
>
|
||||||
<span className="font-medium">{compact ? "Theme" : "Toggle Theme"}</span>
|
<span className="font-medium">{compact ? "Theme" : "Toggle Theme"}</span>
|
||||||
<span aria-hidden>▾</span>
|
<span aria-hidden>{open ? "▴" : "▾"}</span>
|
||||||
</summary>
|
</button>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="
|
className={`
|
||||||
absolute top-full mt-2 z-[70] rounded-lg border border-secondary bg-bg/95 p-2 shadow-xl backdrop-blur
|
absolute top-full mt-2 z-[70] rounded-lg border border-secondary bg-bg/95 p-2 shadow-xl backdrop-blur
|
||||||
left-4 right-4 mx-auto w-[calc(100vw-2rem)] max-w-[18rem]
|
left-4 right-4 mx-auto w-[calc(100vw-2rem)] max-w-[18rem]
|
||||||
md:left-auto md:right-0 md:mx-0 md:w-44 md:max-w-none
|
md:left-auto md:right-0 md:mx-0 md:w-44 md:max-w-none
|
||||||
origin-top scale-y-95 opacity-0 translate-y-[-4px]
|
origin-top transition-[opacity,transform] duration-200 ease-out
|
||||||
pointer-events-none transition-all duration-300 ease-out
|
${open ? "opacity-100 scale-100 translate-y-0 pointer-events-auto" : "opacity-0 scale-95 -translate-y-1 pointer-events-none"}
|
||||||
group-open:opacity-100 group-open:scale-y-100 group-open:translate-y-0 group-open:pointer-events-auto
|
`}
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<ul className="space-y-1">
|
<ul className="space-y-1" role="listbox">
|
||||||
{themes.map((t) => (
|
{themes.map((t) => (
|
||||||
<li key={t}>
|
<li key={t}>
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
onClick={() => crossfadeTo(t)}
|
onClick={() => crossfadeTo(t)}
|
||||||
className={`w-full rounded px-3 py-2 text-left hover:bg-secondary/60 anim-base ${
|
className={`w-full rounded px-3 py-2 text-left hover:bg-secondary/60 anim-base ${
|
||||||
theme === t ? "outline outline-1 outline-primary" : ""
|
theme === t ? "outline outline-1 outline-primary" : ""
|
||||||
@@ -72,7 +79,6 @@ export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/data/resume.ts
Normal file
81
src/data/resume.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
export type ResumeData = {
|
||||||
|
contactInfo: {
|
||||||
|
name?: string;
|
||||||
|
location: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
website?: string;
|
||||||
|
linkedin?: string;
|
||||||
|
};
|
||||||
|
summary: string;
|
||||||
|
skills: Array<{ category: string; items: string[] }>;
|
||||||
|
certifications?: string[];
|
||||||
|
projects?: Array<{ name: string; stack: string; bullets: string[] }>;
|
||||||
|
accomplishments?: string[];
|
||||||
|
workHistory: Array<{ title: string; company: string; location: string; dates: string; bullets?: string[] }>;
|
||||||
|
education: Array<{ degree: string; school: string; date: string; details?: string }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const resumeData: ResumeData = {
|
||||||
|
contactInfo: {
|
||||||
|
name: "Jody Holt",
|
||||||
|
location: "Amarillo, TX",
|
||||||
|
phone: "806.654.2813",
|
||||||
|
email: "jholt1008@gmail.com",
|
||||||
|
website: "https://www.jodyholt.com",
|
||||||
|
linkedin: "https://www.linkedin.com/in/jody-holt-cis",
|
||||||
|
},
|
||||||
|
summary:
|
||||||
|
"Detail-oriented software developer with experience building full-stack applications using React, TypeScript, SQL, Express, and Docker. Skilled in responsive UI, modular API design, and writing scalable code. Strong communicator known for learning new technologies quickly and solving problems efficiently.",
|
||||||
|
skills: [
|
||||||
|
{ category: "Front End", items: ["React", "TypeScript", "JavaScript", "Responsive Design", "Component Architecture", "Vite"] },
|
||||||
|
{ category: "Back End", items: ["Node.js", "Express", "REST APIs", "Authentication", "Input Validation", "JWT"] },
|
||||||
|
{ category: "Database", items: ["SQL", "Prisma", "CRUD Operations", "Query Optimization", "Database Schema Design", "Data Modeling"] },
|
||||||
|
{ category: "Tools", items: ["Git/GitHub", "Docker", "Docker Compose", "Postman"] },
|
||||||
|
],
|
||||||
|
certifications: [
|
||||||
|
"Meta Front-End Developer Certificate (Coursera)",
|
||||||
|
"Meta Data Engineering Certificate (Coursera)",
|
||||||
|
],
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: "SkyMoney Budgeting App (Beta)",
|
||||||
|
stack: "React, TypeScript, Node.js, Prisma, PostgreSQL",
|
||||||
|
bullets: [
|
||||||
|
"Built a full-stack budgeting platform with 17 screens, reusable UI components, and 47 REST endpoints for income, transactions, variable categories, and payment plan automation.",
|
||||||
|
"Implemented core budgeting logic including auto-funding, overdue prioritization, partial payments, and bill reconciliation workflows.",
|
||||||
|
"Designed a relational database schema using 7 Prisma models with all writes scoped to user ID.",
|
||||||
|
"Containerized the API, PostgreSQL, Caddy reverse proxy, and scheduled workers using Docker Compose for production-ready deployment.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "React Portfolio Website",
|
||||||
|
stack: "React, TypeScript, Vite, Responsive UI",
|
||||||
|
bullets: [
|
||||||
|
"Built a single-page portfolio with 3 core sections and 7 reusable components, structured for expansion.",
|
||||||
|
"Implemented interactive UI including a 5-theme color system, navigation, and mobile responsiveness.",
|
||||||
|
"Organized code using TypeScript, reusable component patterns, and a custom theme hook.",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
workHistory: [
|
||||||
|
{
|
||||||
|
title: "Sandwich Artist",
|
||||||
|
company: "Subway",
|
||||||
|
location: "Canyon, TX",
|
||||||
|
dates: "Sep 2024 - Present",
|
||||||
|
bullets: ["Maintained fast and accurate customer service by completing orders in a high-volume environment."],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Head Lifeguard (Seasonal)",
|
||||||
|
company: "Johnson Park Youth Center",
|
||||||
|
location: "Borger, TX",
|
||||||
|
dates: "May 2022 - Aug 2025",
|
||||||
|
bullets: ["Led safety operations by monitoring swimmers and enforcing policies during high-traffic shifts."],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
education: [
|
||||||
|
{ degree: "B.B.A. Computer Information Systems", school: "West Texas A&M University", date: "May 2026", details: "GPA: 3.20/4.0" },
|
||||||
|
{ degree: "M.S. Computer Information Systems & Business Analytics", school: "West Texas A&M University", date: "May 2027" },
|
||||||
|
],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user