229 lines
9.6 KiB
TypeScript
229 lines
9.6 KiB
TypeScript
|
|
export type MoreSection = {
|
|
title?: string;
|
|
text: string;
|
|
}
|
|
|
|
export type SocialLink = {
|
|
label: string;
|
|
url: string;
|
|
icon: "gh" | "li" | "x" | "dev";
|
|
};
|
|
|
|
export type ContactMethod = {
|
|
masked: boolean;
|
|
label: string;
|
|
icon: string;
|
|
}
|
|
|
|
export type Project = {
|
|
id: number;
|
|
slug: string;
|
|
title: string;
|
|
description: string;
|
|
details?: string;
|
|
tags: string[];
|
|
images?: string[];
|
|
videos?: string[];
|
|
link: string;
|
|
year: string;
|
|
};
|
|
|
|
|
|
export const PROFILE = {
|
|
name: "Hunter W",
|
|
title: "Software Engineer",
|
|
email: "contact@hwilliams.dev",
|
|
bio: `I build user experiences before I write code.
|
|
Experience with full-stack development across a variety of frameworks and languages including Vue,
|
|
React, Next.JS, Nuxt, Node.JS, Express, Flask, FastAPI, Gunicorn, and more.`,
|
|
moreSections: [
|
|
{
|
|
title:"A little more about me",
|
|
text: `I graduated with a B.S. in Computer Science from Georgia Southern University in May 2025.
|
|
I have been working with AWS building analytics and monitoring dashboards with a sprinkle of prompt
|
|
engineering on major production projects.`
|
|
},
|
|
{
|
|
text: "I make mods for video games such as Space Engineers and Rimworld in my free time."
|
|
},
|
|
{
|
|
title:"My goals",
|
|
text: `User experience. \n \n Nothing makes me happier than seeing someone excited to use the software I make, and the way that happens is by practicing
|
|
development techniques that make a good user experience. I especially enjoy peeling back the layers of old systems to explore how an existing
|
|
user experience can be improved. I love the words 'Wow, it used to be difficult to do this!'.
|
|
`
|
|
},
|
|
{
|
|
title:"My dreams",
|
|
text: `Stop me if you've heard this one before: I left college with a burning passion for machine learning and artificial intelligence. It's still there, and despite
|
|
the trouble going on in the world right now I see a bright future for how these incredibly advanced statistical models can still be applied in new ways.
|
|
\n
|
|
`
|
|
},
|
|
{
|
|
title:"Wow, you're still here?",
|
|
text: `I appreciate you, but I am totally out of things to talk about.`
|
|
}
|
|
] satisfies MoreSection[],
|
|
moreSectionsStart: 1,
|
|
links: [
|
|
{ label: "GitHub", url: "https://github.com/FerrenF", icon: "gh" },
|
|
{ label: "LinkedIn", url: "https://www.linkedin.com/in/hwilliamsf/", icon: "li" }
|
|
] satisfies SocialLink[],
|
|
contactMethods: [{
|
|
masked: true,
|
|
label: "contact@hwilliams.dev",
|
|
icon: "email"
|
|
}
|
|
] satisfies ContactMethod[]
|
|
};
|
|
|
|
export const SITE = {
|
|
title: "Hunter W. - Software Engineer",
|
|
description: "SWE Looking for work",
|
|
source: {
|
|
label: "Git",
|
|
link: "https://git.hwilliams.dev/hwilliams/hwilliams-dev"
|
|
}
|
|
};
|
|
|
|
export const PROJECTS: Project[] = [{
|
|
id: 1,
|
|
slug: "flowbased-agent-management",
|
|
title: "Flow-Based Agent Management platform",
|
|
description: "The Flow-based Agent Managemment (FAM) platform is a real-world example of an Agent support tool designed to streamline high-volume in-person Agent to Guest interactions in various scenarios.",
|
|
details: `# Flow-based Agent Management platform (FAM)
|
|
|
|
The Flow-based Agent Managemment (FAM) platform is designed to streamline high-volume in-person Agent to Guest interactions using the concept of workflows.
|
|
|
|
<blockquote>A welcome screen using the default theme.
|
|
<a target="_blank" href="/img/projects/fam/example-welcome.png">
|
|
<img src="/img/projects/fam/example-welcome.png" alt="Alt text" width="600"/>
|
|
</a>
|
|
</blockquote>
|
|
|
|
|
|
## Private Repository
|
|
This project was tailored to the needs of a large organization. As such, and naturally so — the source for this project is private.
|
|
|
|
I am happy to discuss the architecture and design decisions made for this project in detail during an interview.
|
|
|
|
<blockquote>A short video of the platform's operation.
|
|
<video width="640" height="360" controls>
|
|
<source src="/img/projects/fam/runthrough.mp4" type="video/mp4">
|
|
Your browser does not support the video tag.
|
|
</video>
|
|
</blockquote>
|
|
|
|
## Summary
|
|
|
|
In the context of <b>FAM</b>, an \`interaction\` is the behavior that occurs between a guest and an agent.
|
|
An \`agent\` is a real-person (and most often a representative of some business) whom is interacting with a \`guest\` in order to exchange information or complete some task.
|
|
|
|
A front-desk employee at a hotel checking in a guest is an easy to reach example. The ticketing/admission agents at an event might be another.
|
|
These agent to guest interactions are what drive an organization on the ground.
|
|
|
|
These interactions might occur at high volumes and might be sustained throughout normal business operation. In such cases,
|
|
the amount of time spent completing tasks and exchanging information might have a very high impact on a business' operations.
|
|
|
|
FAM supports breaking these interactions down into streamlined, consistent, efficient, repeatable, and flexible
|
|
workflows.
|
|
|
|
<blockquote>Not every interaction will be the same. Some guests might have additional service needs.
|
|
<a target="_blank" href="/img/projects/fam/reference-1.png">
|
|
<img src="/img/projects/fam/reference-1.png" alt="A workflow, and a workload." width="800"/>
|
|
</a>
|
|
</blockquote>
|
|
|
|
|
|
|
|
There is a minimum amount of time that it takes an agent to complete a task in any software, and there is a reasonable maximum amount of time that might be spent on
|
|
unusual circumstances or perhaps lost to the 'growing pains' of inexperienced staff or imperfect systems.
|
|
|
|
FAM tries to shorten the duration of agent to guest interactions without reducing the quality of the interaction through purpose-built UX-first guided workflows. Task identification from business analysis are made into functional workflows that support reducing the time taken to complete tasks while also addressing the variability in
|
|
how much time that it takes to complete these tasks.
|
|
|
|
|
|
<blockquote>The FAM platform runs on a vue/nuxt stack. Here's a part of the app structure in a diagram, without exposing too much.
|
|
<a target="_blank" href="/img/projects/fam/reference-1.png">
|
|
<img src="/img/projects/fam/workflow.png" alt="Workflow architecture" width="800"/>
|
|
</a>
|
|
</blockquote>
|
|
|
|
|
|
A step can contain any type of work, and can be used in multiple workflows. An identification step for a check-in process using the default template might look like such:
|
|
|
|
<blockquote>The general look and feel of a step in a workflow.
|
|
<a target="_blank" href="/img/projects/fam/example-step.png">
|
|
<img src="/img/projects/fam/example-step.png" alt="Example Step" width="800"/>
|
|
</a>
|
|
</blockquote>
|
|
|
|
In this picture, a guest has had their ID card scanned and their profile information is displayed. The area with a marching border on the right side integrates an external barcode scanner in order to
|
|
read information on the back of US driver's license. This information is compared with that on the left, and discrepencies are highlighted in yellow. Checking the acknowledgement box at the bottom moves
|
|
the workflow into the next step.
|
|
|
|
## Implementation
|
|
|
|
This platform was implemented as a solution for in-person registration, VIP registration, pre-event mailouts, and entitlements fulfillment for an Atlanta-based organization's yearly convention for May 2026, where it achieved significant success:
|
|
The setup consists of a Vue 3 frontend and a configurable backend interface. This particular implementation utilized a Perl catalyst backend. Standard 2D barcode scanning devices and RFID tag readers were used
|
|
in the guest identification system and have out-of-the box modular support in flows created on the platform.
|
|
|
|
<pre>Some Stats:</pre>
|
|
|
|
- Single day registration of over 10,000 guests with an average processing time of about 45 seconds.
|
|
- Previous year: ~2 minutes.
|
|
- No single guest wait time greater than 10 minutes.
|
|
- Previous year had lines that wrapped around the entire building and there were stories of four hour waits.
|
|
|
|
<blockquote>A testament
|
|
<a target="_blank" href="/img/projects/fam/stats-2026-1.png">
|
|
<img src="/img/projects/fam/stats-2026-1.png" alt="Example Step" width="800"/>
|
|
</a>
|
|
</blockquote>
|
|
|
|
|
|
A real-world solution for event staff and volunteer management.
|
|
`,
|
|
tags: ["Vue", "Node.JS", "Event-Management", "Real-Time", "Full-Stack"],
|
|
link: "private",
|
|
year: "2026",
|
|
images: [ "img/projects/fam/example-welcome.png", "img/projects/fam/example-step-in-flow.png"],
|
|
videos: [ "img/projects/fam/runthrough.mp4" ]
|
|
},{
|
|
id: 2,
|
|
slug: "clean-space",
|
|
title: "Clean Space",
|
|
description: "Clean Space is an architecture, proof of concept, and an in-development tool for Space Engineers and Space Engineers 2 server owners featuring client and server side .",
|
|
tags: ["C#", ".NET", "WPF", "Netcode", "Security"],
|
|
link: "https://github.com/FerrenF/CleanSpace",
|
|
year: "2025",
|
|
}, {
|
|
id: 3,
|
|
slug: "personal-portfolio",
|
|
title: "Personal Portfolio Website",
|
|
description: "This very website! Built with Next.JS, React, and TypeScript. Featuring a custom CMS and a lot of custom-built components and hooks.",
|
|
tags: ["Next.JS", "React", "TypeScript", "Vercel"],
|
|
link: "https://git.hwilliams.dev/hwilliams/hwilliams-dev",
|
|
year: "2026",
|
|
}];
|
|
|
|
|
|
import { remark } from "remark";
|
|
import remarkParse from "remark-parse";
|
|
import rehypeRaw from "rehype-raw";
|
|
import rehypeStringify from "rehype-stringify";
|
|
import remarkRehype from "remark-rehype";
|
|
|
|
|
|
export async function getProjectDetails(slug: string) {
|
|
const p = PROJECTS.find((v)=>v.slug == slug);
|
|
if (!p || !p.details) return null;
|
|
const re = await remark()
|
|
.use(remarkParse)
|
|
.use(remarkRehype, { allowDangerousHtml: true })
|
|
.use(rehypeRaw)
|
|
.use(rehypeStringify).process(p.details);
|
|
return re?.toString();
|
|
} |