Compare commits
12 Commits
136c0e61db
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d9c0014ed | |||
| 3caf1e30b0 | |||
| b4ef9fd2e0 | |||
| 66f63523e2 | |||
| 9a84d4b78c | |||
| f559a28b74 | |||
| 4e82497640 | |||
|
|
39507d4bd8 | ||
|
|
0fb0877a92 | ||
|
|
6f7efed5e5 | ||
| 594aa96dc7 | |||
| d5a2914915 |
25
.gitea/workflows/deploy.yaml
Normal file
25
.gitea/workflows/deploy.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Deploy Jody's App
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: vps-host
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build with Vite
|
||||
|
||||
run: npx vite build
|
||||
|
||||
- name: Sync Files
|
||||
run: |
|
||||
mkdir -p /var/www/jody/dist
|
||||
rm -rf /var/www/jody/dist/*
|
||||
cp -r dist/* /var/www/jody/dist/
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,3 +30,4 @@ Thumbs.db
|
||||
# yarn.lock
|
||||
# package-lock.json
|
||||
# pnpm-lock.yamlw
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -47,25 +47,9 @@ src/
|
||||
These themes are applied via html[data-theme="x"] and are used across the site for all gradients, tints, accents, etc.
|
||||
|
||||
|
||||
## DEV NOTES as of 10/29/2025
|
||||
|
||||
- All social links are dummy values as of now.
|
||||
- Section layout is controlled via <Section id="...."></Section> wrappers.
|
||||
- Images are outdated and will be replaced.
|
||||
- UI is mobile-oriented, but device friendly.
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- Add links to projects within cards
|
||||
- Change out experience tab for resume/skills
|
||||
- Add animations
|
||||
- more ways to contact
|
||||
- Deploy site via personal service
|
||||
|
||||
|
||||
## View Progress
|
||||
|
||||
## To run on local client:
|
||||
```bash
|
||||
npm i
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export function Footer({
|
||||
}) {
|
||||
return (
|
||||
<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-xl md:text-2xl font-extrabold font-name tracking-wide text-text">
|
||||
Jody Holt
|
||||
@@ -36,7 +36,7 @@ export function Footer({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav className="flex items-center gap-5">
|
||||
<nav className="flex items-center gap-5 md:justify-center">
|
||||
<button
|
||||
className="text-text hover:text-primary anim-base"
|
||||
onClick={() => document.getElementById("home")?.scrollIntoView({ behavior: "smooth" })}
|
||||
@@ -57,7 +57,7 @@ export function Footer({
|
||||
</button>
|
||||
</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) => (
|
||||
<a
|
||||
key={s.label}
|
||||
|
||||
@@ -71,8 +71,10 @@ export function Navbar({ onNav }: { onNav: (id: string) => void }) {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`md:hidden transition-[max-height] duration-300 ${
|
||||
open ? "max-h-96 overflow-visible" : "max-h-0 overflow-hidden"
|
||||
className={`md:hidden transition-[max-height,opacity,transform] duration-300 ease-out ${
|
||||
open
|
||||
? "max-h-96 overflow-visible opacity-100 translate-y-0"
|
||||
: "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">
|
||||
|
||||
@@ -18,13 +18,15 @@ type Project = {
|
||||
videoMobile: string;
|
||||
techStack: string[];
|
||||
liveUrl?: string;
|
||||
beta?: boolean;
|
||||
note?: string;
|
||||
comingSoon?: boolean;
|
||||
};
|
||||
|
||||
const projects: Project[] = [
|
||||
{
|
||||
id: "skymoney",
|
||||
title: "Skymoney",
|
||||
title: "SkyMoney",
|
||||
description:
|
||||
"A budgeting app that simulates your bank account to ensure financial discipline.",
|
||||
coverImage: skymoneycover,
|
||||
@@ -32,7 +34,9 @@ const projects: Project[] = [
|
||||
video: skymoneyvideo,
|
||||
videoMobile: skymoneyvideoMobile,
|
||||
techStack: ["React", "TypeScript", "Node.js", "PostgreSQL"],
|
||||
comingSoon: true,
|
||||
liveUrl: "https://skymoneybudget.com",
|
||||
beta: true,
|
||||
note: "Contact Jody for beta access.",
|
||||
},
|
||||
{
|
||||
id: "miller-building",
|
||||
@@ -154,10 +158,10 @@ function ProjectCard({
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
{/* Coming Soon Badge */}
|
||||
{project.comingSoon && (
|
||||
{/* Beta Badge */}
|
||||
{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">
|
||||
Coming Soon
|
||||
Beta
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -187,10 +191,10 @@ function ProjectCard({
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
{/* Coming Soon Badge */}
|
||||
{project.comingSoon && (
|
||||
{/* Beta Badge */}
|
||||
{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">
|
||||
Coming Soon
|
||||
Beta
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -201,6 +205,9 @@ function ProjectCard({
|
||||
<p className="text-text/70 text-sm mb-4 line-clamp-2">
|
||||
{project.description}
|
||||
</p>
|
||||
{project.note && (
|
||||
<p className="text-text/60 text-xs mb-4">{project.note}</p>
|
||||
)}
|
||||
|
||||
{/* Tech Stack */}
|
||||
<div className="flex flex-wrap gap-2 mb-4">
|
||||
|
||||
@@ -1,57 +1,8 @@
|
||||
import React from "react";
|
||||
|
||||
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" },
|
||||
];
|
||||
import { resumeData } from "../data/resume";
|
||||
|
||||
export function Resume() {
|
||||
const { contactInfo, summary, skills, certifications, projects, workHistory, education } = resumeData;
|
||||
return (
|
||||
<div className="mx-auto max-w-5xl px-4 py-16 md:py-24">
|
||||
{/* Header */}
|
||||
@@ -67,6 +18,22 @@ export function Resume() {
|
||||
<a href={`mailto:${contactInfo.email}`} className="hover:text-primary anim-base">
|
||||
{contactInfo.email}
|
||||
</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>
|
||||
|
||||
@@ -101,18 +68,47 @@ export function Resume() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Accomplishments */}
|
||||
<section className="mb-10 anim-fade-in">
|
||||
<SectionTitle>Professional Accomplishments</SectionTitle>
|
||||
<ul className="space-y-3">
|
||||
{accomplishments.map((item, 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>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
{/* Certifications */}
|
||||
{certifications && certifications.length > 0 && (
|
||||
<section className="mb-10 anim-fade-in">
|
||||
<SectionTitle>Certifications</SectionTitle>
|
||||
<ul className="space-y-3">
|
||||
{certifications.map((item, 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>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</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 */}
|
||||
<section className="mb-10 anim-fade-in">
|
||||
@@ -121,15 +117,27 @@ export function Resume() {
|
||||
{workHistory.map((job, i) => (
|
||||
<div
|
||||
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>
|
||||
<h4 className="font-semibold text-text">{job.title}</h4>
|
||||
<p className="text-text/70 text-sm">
|
||||
{job.company} — {job.location}
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold text-text">{job.title}</h4>
|
||||
<p className="text-text/70 text-sm">
|
||||
{job.company} — {job.location}
|
||||
</p>
|
||||
</div>
|
||||
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{job.dates}</span>
|
||||
</div>
|
||||
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{job.dates}</span>
|
||||
{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>
|
||||
@@ -146,7 +154,10 @@ export function Resume() {
|
||||
>
|
||||
<div>
|
||||
<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>
|
||||
<span className="mt-2 sm:mt-0 text-sm text-primary font-medium">{edu.date}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ThemeToggle.tsx
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { useTheme } from "../hooks/useTheme";
|
||||
|
||||
// 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 }) {
|
||||
const { theme, setTheme } = useTheme();
|
||||
const [open, setOpen] = useState(false);
|
||||
const themes = ["a", "b", "c", "d", "e"] as const;
|
||||
|
||||
const crossfadeTo = (next: typeof themes[number]) => {
|
||||
@@ -27,6 +28,7 @@ export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
||||
|
||||
// 3) switch theme (your existing logic)
|
||||
setTheme(next as any);
|
||||
setOpen(false);
|
||||
|
||||
// 4) remove crossfade flag after the animation
|
||||
window.setTimeout(() => {
|
||||
@@ -37,42 +39,46 @@ export function ThemeToggle({ compact = false }: { compact?: boolean }) {
|
||||
|
||||
return (
|
||||
<div className="relative inline-block text-text">
|
||||
<details className="group">
|
||||
<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">
|
||||
<span className="font-medium">{compact ? "Theme" : "Toggle Theme"}</span>
|
||||
<span aria-hidden>▾</span>
|
||||
</summary>
|
||||
<button
|
||||
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 aria-hidden>{open ? "▴" : "▾"}</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className="
|
||||
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]
|
||||
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]
|
||||
pointer-events-none transition-all duration-300 ease-out
|
||||
group-open:opacity-100 group-open:scale-y-100 group-open:translate-y-0 group-open:pointer-events-auto
|
||||
"
|
||||
>
|
||||
<ul className="space-y-1">
|
||||
{themes.map((t) => (
|
||||
<li key={t}>
|
||||
<button
|
||||
onClick={() => crossfadeTo(t)}
|
||||
className={`w-full rounded px-3 py-2 text-left hover:bg-secondary/60 anim-base ${
|
||||
theme === t ? "outline outline-1 outline-primary" : ""
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="mr-2 inline-block h-3 w-3 rounded-full align-middle"
|
||||
style={{ background: themeColors[t].primary }}
|
||||
/>
|
||||
{themeColors[t].label}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
<div
|
||||
className={`
|
||||
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]
|
||||
md:left-auto md:right-0 md:mx-0 md:w-44 md:max-w-none
|
||||
origin-top transition-[opacity,transform] duration-200 ease-out
|
||||
${open ? "opacity-100 scale-100 translate-y-0 pointer-events-auto" : "opacity-0 scale-95 -translate-y-1 pointer-events-none"}
|
||||
`}
|
||||
>
|
||||
<ul className="space-y-1" role="listbox">
|
||||
{themes.map((t) => (
|
||||
<li key={t}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => crossfadeTo(t)}
|
||||
className={`w-full rounded px-3 py-2 text-left hover:bg-secondary/60 anim-base ${
|
||||
theme === t ? "outline outline-1 outline-primary" : ""
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="mr-2 inline-block h-3 w-3 rounded-full align-middle"
|
||||
style={{ background: themeColors[t].primary }}
|
||||
/>
|
||||
{themeColors[t].label}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</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