You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
8.5 KiB
TypeScript

import { isoDateStr } from "../util";
type Info = {
name: string;
emailAddress: string;
website: string;
};
type Education = {
university: string;
college: string;
graduationYear: number;
degree: string;
};
type Timeframe = { start: Date; end?: Date } | string;
type Skill =
| "TypeScript"
| "React"
| "Python"
| "Django"
| "CSS"
| "SCSS"
| "PostgreSQL"
| "Java"
| "Go"
| "MongoDB"
| "WordPress"
| "PHP"
| "Kotlin"
| "HTML"
| "Elixir"
| "Phoenix"
| "JavaScript"
| "Docker"
| "Rust"
| "C++"
| "C"
| "Haskell"
| "GraphQL";
type Experience = {
companyName: string;
title: string;
skills: Skill[];
timeframe: Timeframe;
highlights: string[];
};
type Project = {
name: string;
skills: Skill[];
description: string;
link?: string;
};
type Resume = {
info: Info;
education: Education;
experiences: Experience[];
selectedProjects: Project[];
skills: String[];
};
const RESUME: Resume = {
info: {
name: "Nick",
emailAddress: "hello@idylls.net",
website: "https://idylls.net",
},
education: {
university: "Northeastern University",
college: "Khoury College of Computer Sciences",
degree: "Bachelor of Science in Computer Science with Honors, Cum Laude",
graduationYear: 2019,
},
experiences: [
{
companyName: "Massachusetts Bay Transportation Authority",
title: "Full Stack Software Engineer",
highlights: [
"Led maintenance and development of critical, high-availability service delivering transit alerts to riders via both SMS and email",
"Developed and deployed various improvements to public-facing rider website",
],
skills: [
"Elixir",
"Phoenix",
"TypeScript",
"JavaScript",
"SCSS",
"Docker",
"PostgreSQL",
],
timeframe: "2022",
},
{
companyName: "KickUp",
title: "Full Stack Software Engineer",
highlights: [
"Designed and implemented several features improving client abilities to visualize, analyze, and action teacher growth data",
"Led effort to integrate TypeScript into existing JavaScript codebase, as well as provided mentorship to other team members",
],
skills: [
"TypeScript",
"JavaScript",
"React",
"Python",
"Django",
"CSS",
"PostgreSQL",
"GraphQL",
],
timeframe: {
start: new Date("2020-10-01"),
end: new Date("2022-02-01"),
},
},
{
companyName: "TripAdvisor",
title: "Full Stack Software Engineer",
highlights: [
"Led design and implementation of project surfacing location outliers to suppliers, increasing sales and click-through rate on 10% of products",
"Implemented improved online photo editor, increasing the percentage of listings with owner-provided photos by 5%",
],
timeframe: {
start: new Date("2019-09-01"),
end: new Date("2020-06-01"),
},
skills: ["Java", "JavaScript", "React", "CSS", "PostgreSQL", "GraphQL"],
},
{
companyName: "TripAdvisor, Rentals",
title: "Full Stack Engineering Co-op",
highlights: [
"Assisted in redesigning the external calendar syncing system, reducing the time to update hundreds of thousands of external calendars from hours to fifteen minutes",
"Designed, developed, and tested endpoints for internal and external REST APIs in a large Java codebase, as well as modified user-facing web pages",
],
timeframe: "Spring/Summer 2018",
skills: ["Java", "JavaScript", "CSS"],
},
{
companyName: "clypd",
title: "Backend Engineering Co-op",
highlights: [
"Acted as feature lead on redesign of large portions of existing API to surface additional data and improve ergonomics for major analytics project",
"Organized meetups originally related to Rust but later expanding to general code jams",
],
skills: ["Go", "PostgreSQL"],
timeframe: "Spring/Summer 2017",
},
{
companyName: "I'm From the Future",
title: "Junior Full Stack Software Engineer",
highlights: [
"Led development on an internal marketing tool using React, D3.js, PhantomJS, and MongoDB",
],
skills: ["PHP", "JavaScript", "React", "MongoDB", "CSS", "WordPress"],
timeframe: "Summer/Fall 2016",
},
{
companyName: "ProTech Internet Group",
title: "Junior Web Developer",
skills: ["PHP", "JavaScript", "CSS", "WordPress"],
timeframe: {
start: new Date("2015-06-01"),
end: new Date("2016-06-01"),
},
highlights: [],
},
{
companyName: "SiteTechs",
title: "Web Content Admin",
skills: ["JavaScript", "HTML", "CSS", "WordPress"],
timeframe: {
start: new Date("2012-06-01"),
end: new Date("2015-09-01"),
},
highlights: [],
},
],
selectedProjects: [
{
name: "Pathos",
description:
"An Old School RuneScape plugin for displaying pathing information",
skills: ["Kotlin"],
link: "https://git.idylls.net/idylls/pathos",
},
{
name: "Statue",
description: "A lightweight web host / service status monitor",
skills: ["Rust", "TypeScript"],
link: "https://status.idylls.net",
},
],
skills: [
"C++",
"C",
"Haskell",
"Low-level Programming",
"Performance Analysis / Optimization",
"Realtime Graphics",
],
};
const SKILL_PRIO = {
TypeScript: 100,
Go: 99,
Rust: 101,
JavaScript: 98,
Java: 97,
React: 96,
Elixir: 90,
Python: 90,
} as const satisfies Partial<Record<Skill, number>>;
const skillPrio = (s: Skill) => SKILL_PRIO[s] ?? null;
const sortSkill = (a: Skill, b: Skill) => {
const aPrio = skillPrio(a);
const bPrio = skillPrio(b);
if (!aPrio) {
if (bPrio) {
return bPrio;
}
return a.localeCompare(b);
}
const prio = bPrio - aPrio;
if (prio == 0) {
return a.localeCompare(b);
}
return prio;
};
for (const exp of RESUME.experiences) {
exp.skills.sort(sortSkill);
}
export const renderResume = () => {
const renderInfo = () => `
<section class="info">
<div class="name">${RESUME.info.name}</div>
<div class="email">${RESUME.info.emailAddress}</div>
<div class="website">
<a href="${RESUME.info.website}">
${RESUME.info.website}
</a>
</div>
</section>
`;
const renderExperiences = () => {
let out = `<section class="experiences"><header>Experience</header>`;
for (const e of RESUME.experiences) {
out += `<div class="experience">`;
out += `<div class="title">${e.title}</div>`;
out += `<div class="company">${e.companyName}</div>`;
out += `<div class="timeframe">${renderTimeframe(e.timeframe)}</div>`;
out += `<div class="skills">${e.skills.join(", ")}</div>`;
out += `<ul class="highlights">`;
for (const h of e.highlights) {
out += `<li>${h}</li>`;
}
out += `</ul>`;
out += `</div>`;
}
out += `</section>`;
return out;
};
const renderProjects = () => {
let out = `<section class="projects"><header>Selected Projects</header>`;
out += `<aside>See more at&nbsp;<a href="https://git.idylls.net/idylls">https://git.idylls.net/idylls</a></aside>`;
for (const p of RESUME.selectedProjects) {
out += `<div class="project">`;
out += `<div class="name">${p.name}</div>`;
out += `<div class="skills">${p.skills.join(", ")}</div>`;
out += `<div class="description">${p.description}</div>`;
out += `</div>`;
}
out += "</section>";
return out;
};
const renderEducation = () => `
<section class="education">
<header>Education</header>
<div class="university">${RESUME.education.university}</div>
<div class="college">${RESUME.education.college} Class of ${RESUME.education.graduationYear}</div>
<div class="degree">${RESUME.education.degree}</div>
</section>
`;
const renderSkills = () => `
<section class="skills">
<header>Additional Skills</header>
<div>${RESUME.skills.join(", ")}</div>
</section>
`;
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />
<link rel="stylesheet" href="/resume.css" />
<title>Résumé</title>
</head>
<body>
${renderInfo()}
${renderExperiences()}
${renderProjects()}
${renderEducation()}
${renderSkills()}
</body>
</html>
`;
};
const renderTimeframe = (t: Timeframe) => {
if (typeof t == "string") {
return t;
}
return `${t.start.getFullYear()}${
t.end ? t.end.getFullYear() : "Present"
}`;
};
export const render = () => {
let out = `
<div class="full-bleed">
<table class="full-bleed work">
<thead>
<tr>
<th>Company</th>
<th>Title</th>
<th>Timeframe</th>
</tr>
</thead>
<tbody>
`;
for (const e of RESUME.experiences) {
out += `
<tr>
<td>${e.companyName}</td>
<td>${e.title}</td>
<td>${renderTimeframe(e.timeframe)}</td>
</tr>
`;
}
out += `</tbody></table></div>`;
return out;
};