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.
328 lines
7.8 KiB
TypeScript
328 lines
7.8 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: Skill[];
|
|
};
|
|
|
|
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: ["Rust", "TypeScript", "C++", "C", "Haskell", "Java"],
|
|
};
|
|
|
|
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 <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>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;
|
|
};
|