Major Rework

This commit is contained in:
Thomas Cole 2023-07-25 16:16:36 -04:00
parent 29abd4a688
commit 316e2c3c69
10 changed files with 707 additions and 1426 deletions

View File

@ -0,0 +1,18 @@
name: Build Site
run-name: ${{ github.actor }} is building Resume
on: [push]
jobs:
Build-Site:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
steps:
- run: echo "Starting build job"
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm run build

View File

@ -1,12 +1,16 @@
const { src, dest, watch, series } = require("gulp"); const { src, dest, watch, series, pipe } = require("gulp");
const bs = require("browser-sync").create(); const bs = require("browser-sync").create();
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
const index = require("./index");
const resumePath = path.join(__dirname, "resume.json"); const resumePath = path.join(__dirname, "resume.json");
const templateFile = path.join(__dirname, "theme/template.ejs");
//const sass = require("gulp-sass")(require("sass"));
const sass = require("sass");
const ejs = require('ejs');
function setup(cb) { function setup(cb) {
let dir = path.join(__dirname, 'dev'); let dir = path.join(__dirname, "dev");
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir); fs.mkdirSync(dir);
} }
@ -14,14 +18,25 @@ function setup(cb){
} }
function css(cb) { function css(cb) {
// src("./theme/**/*.scss")
// .pipe(sass().on("error", sass.logError))
// .pipe(dest("./dev"));
cb(); cb();
} }
function html(cb) { function html(cb) {
const resume = JSON.parse(fs.readFileSync("./resume.json", "utf-8")); const resume = JSON.parse(fs.readFileSync("./resume.json", "utf-8"));
let css = sass.compile("./theme/style.scss");
let devBKG = sass.compile("./theme/devbkg.scss");
css.tagOpen = "<style>"
css.tagClose = "</style>"
css.devBKG = devBKG.css;
fs.writeFileSync(path.join(__dirname, 'dev/template.html'), index.render(resume)); fs.writeFileSync(
path.join(__dirname, "dev/template.html"),
ejs.render(fs.readFileSync(templateFile, {encoding: "utf-8"}), {resume: resume, css: css})
);
cb(); cb();
} }
@ -36,8 +51,10 @@ function serve() {
open: false, open: false,
}); });
watch(['./theme/**/*.html', './resume.json'], html) watch(["./theme/**/*.ejs", "./resume.json"], html);
watch(["./theme/**/*.scss"], css);
bs.watch("./dev/*.html").on("change", bs.reload); bs.watch("./dev/*.html").on("change", bs.reload);
bs.watch("./dev/*.css").on("change", bs.reload);
} }
exports.default = series(setup, css, html, serve); exports.default = series(setup, css, html, serve);

View File

@ -1,27 +1,16 @@
const fs = require("fs"); const fs = require("fs");
const ejs = require('ejs');
const path = require("path"); const path = require("path");
const templateFile = path.join(__dirname, "theme/template.html"); const sass = (require("sass"));
const templateFile = path.join(__dirname, "theme/template.ejs");
function replaceTokens(str, data) { const sassFile = path.join(__dirname, "theme/style.scss");
const tokenRegex = /{(.*?)}/g;
str = str.replace(tokenRegex, (match, token) => {
let value = data;
console.log("Token, " , token)
for (let key of token.split(".")) {
value = value[key];
}
console.log(match, value)
return value;
});
return str;
}
const render = (resume) => { const render = (resume) => {
let template = fs.readFileSync(templateFile, { encoding: "utf-8" }); let css = sass.compile(sassFile);
return replaceTokens(template, resume); css.tagOpen = "<style>"
css.tagClose = "</style>"
let redneredTemplate = ejs.render(fs.readFileSync(templateFile, {encoding: "utf-8"}), {resume: resume, css: css});
return redneredTemplate
}; };
module.exports = { module.exports = {

1486
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,21 @@
{ {
"name": "resume", "name": "jsonresume-theme-thomaspcole",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"dev": "gulp", "dev": "gulp",
"resume": "resume", "validate": "resume validate",
"build": "mkdir -p build && resume export build/resume.pdf --theme . && resume export build/resume.html --theme ." "build": "mkdir -p build && resume export build/resume.pdf --theme . && resume export build/resume.html --theme ."
}, },
"author": "Thomas Cole", "author": "Thomas Cole",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browser-sync": "^2.29.1", "browser-sync": "^2.29.1",
"ejs": "^3.1.9",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"resume-cli": "^3.0.8" "gulp-sass": "^5.1.0",
"resume-cli": "^3.0.8",
"sass": "^1.64.1"
} }
} }

View File

@ -2,7 +2,7 @@
"$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json", "$schema": "https://raw.githubusercontent.com/jsonresume/resume-schema/v1.0.0/schema.json",
"basics": { "basics": {
"name": "Thomas Cole", "name": "Thomas Cole",
"label": "", "label": "System & Network Administrator",
"image": "https://avatars.githubusercontent.com/u/4421560", "image": "https://avatars.githubusercontent.com/u/4421560",
"email": "thomas.patrick.cole@gmail.com", "email": "thomas.patrick.cole@gmail.com",
"phone": "704-771-2453", "phone": "704-771-2453",
@ -37,7 +37,7 @@
"url": "https://christelca.org", "url": "https://christelca.org",
"startDate": "2020-12-02", "startDate": "2020-12-02",
"endDate": "", "endDate": "",
"summary": "", "summary": "Responsible for overseeing and enhancing the essential network infrastructure across multiple campuses. Managed backups to ensure data integrity and collaborated with action teams to create effective technology plans. Implemented cost-efficient software and hardware solutions, resulting in an annual cost reduction of $50,000.",
"highlights": [ "highlights": [
"Maintain and upgrade critical network infrastructure for a multi-campus environment", "Maintain and upgrade critical network infrastructure for a multi-campus environment",
"Facilitate backups and ensure their integrity", "Facilitate backups and ensure their integrity",
@ -53,7 +53,7 @@
"url": "https://christelca.org", "url": "https://christelca.org",
"startDate": "2019-08-02", "startDate": "2019-08-02",
"endDate": "2020-12-02", "endDate": "2020-12-02",
"summary": "", "summary": "Spearheaded the development and implementation of innovative strategies aimed at enhancing live stream services and expanding audience outreach. Oversaw the building and installation of advanced computer systems, significantly improving the recording and streaming capabilities of worship services. Managed the graphics for both in-house worship and live-streamed services to create engaging and visually appealing experiences.",
"highlights": [ "highlights": [
"Developed and implemented new strategies in collaboration with the Christ Providence Tech team to improve live stream services and reach a broader audience", "Developed and implemented new strategies in collaboration with the Christ Providence Tech team to improve live stream services and reach a broader audience",
"Facilitated building and installation of new computer systems to improve recording and streaming capabilities of worship services", "Facilitated building and installation of new computer systems to improve recording and streaming capabilities of worship services",
@ -68,7 +68,7 @@
"url": "https://scanonline.com/", "url": "https://scanonline.com/",
"startDate": "2019-06-02", "startDate": "2019-06-02",
"endDate": "2020-12-02", "endDate": "2020-12-02",
"summary": "", "summary": "Responsible for administering the Office 365 and Windows Active Directory infrastructure. Managed the VOIP phone system and maintained extension listings. Deployed and configured virtual machines to meet specific business requirements and maintained legacy Windows Mobile applications for existing clientele. Performed extensive configuration and maintenance of both customer hardware and software to ensure optimal performance.",
"highlights": [ "highlights": [
"Administer Office 365 and Windows Active Directory infrastructure", "Administer Office 365 and Windows Active Directory infrastructure",
"Manage company VOIP phone system and extension listings", "Manage company VOIP phone system and extension listings",
@ -86,7 +86,7 @@
"url": "https://www.uncg.edu/", "url": "https://www.uncg.edu/",
"startDate": "2018-02-02", "startDate": "2018-02-02",
"endDate": "2019-05-02", "endDate": "2019-05-02",
"summary": "", "summary": "Conducted assessments of helpdesk tickets, ensuring timely resolution of issues while maintaining accurate documentation. Maintained and troubleshot enterprise network systems. Actively collaborated with other network engineers to support the University's goals and objectives.",
"highlights": [ "highlights": [
"Assessed helpdesk tickets to ensure timely resolution of issues and proper documentation", "Assessed helpdesk tickets to ensure timely resolution of issues and proper documentation",
"Assisted in maintenance and troubleshooting of enterprise network systems", "Assisted in maintenance and troubleshooting of enterprise network systems",
@ -113,8 +113,8 @@
{ {
"institution": "University of North Carolina at Greensboro", "institution": "University of North Carolina at Greensboro",
"url": "https://www.uncg.edu/", "url": "https://www.uncg.edu/",
"area": "Bachelor of Science Information Systems and Supply Chain Management", "area": "Information Systems and Supply Chain Management",
"studyType": "", "studyType": "Bachelor of Science",
"startDate": "2015-08-02", "startDate": "2015-08-02",
"endDate": "2019-05-02", "endDate": "2019-05-02",
"score": "" "score": ""
@ -132,12 +132,12 @@
{ {
"name": "Web Development", "name": "Web Development",
"level": "", "level": "",
"keywords": ["HTML", "CSS", "Javascript", "Svelte", "Tailwind", "Static Site Generation", "REST", "Back end as a service", "Typescript", "JQuery"] "keywords": ["HTML", "CSS", "Javascript", "Svelte", "Tailwind", "Static Site Generation", "REST", "Back End As A Service", "Typescript", "JQuery"]
}, },
{ {
"name": "Systems Administration", "name": "Systems Administration",
"level": "", "level": "",
"keywords": ["Linux", "Microsoft Active Directory", "Office 365", "Azure AD", "VMware ESXI", "Microsoft Exchange", "Docker", "Windows Server"] "keywords": ["Linux", "Office 365", "Azure AD", "Microsoft Active Directory", "VMware ESXI", "Microsoft Exchange", "Docker", "Windows Server"]
}, },
{ {
"name": "Network Administration", "name": "Network Administration",
@ -149,6 +149,6 @@
"canonical": "https://raw.githubusercontent.com/jsonresume/resume-schema/master/resume.json", "canonical": "https://raw.githubusercontent.com/jsonresume/resume-schema/master/resume.json",
"version": "v1.0.0", "version": "v1.0.0",
"lastModified": "2017-12-24T15:53:00", "lastModified": "2017-12-24T15:53:00",
"theme": "elegant" "theme": ""
} }
} }

15
theme/devbkg.scss Normal file
View File

@ -0,0 +1,15 @@
body {
/* 'Danger' zone. Highlights content that would not print on the page */
width: 8.5in;
height: 11in;
margin: 0;
padding: 0;
background: repeating-linear-gradient(
45deg,
var(--error-container-alt),
var(--error-container-alt) 10px,
var(--error-container) 10px,
var(--error-container) 20px
)
fixed !important;
}

231
theme/style.scss Normal file
View File

@ -0,0 +1,231 @@
:root {
font-family: "Noto Sans", sans-serif;
--primary: #006494;
--on-primary: #ffffff;
--primary-container: #c8e6ff;
--on-primary-container: #001e31;
--secondary: #50606e;
--on-secondary: #ffffff;
--secondary-container: #d3e4f5;
--on-secondary-container: #0c1d29;
--tertiary: #65597b;
--on-tertiary: #ffffff;
--tertiary-container: #ecdcff;
--on-tertiary-container: #201634;
--error: #ba1b1b;
--error-container: #ffdad4;
--on-error: #ffffff;
--on-error-container: #410001;
--background: #fcfcff;
--on-background: #1a1c1e;
--surface: #fcfcff;
--on-surface: #1a1c1e;
--surface-variant: #dee3ea;
--on-surface-variant: #41474d;
--outline: #72787e;
--inverse-on-surface: #f0f0f3;
--inverse-surface: #2f3032;
--inverse-primary: #8bceff;
--error-container-alt: #ffd3cc;
}
body {
width: 8.5in;
height: 11in;
margin: 0;
padding: 0;
}
p {
margin: 0;
}
.page {
page-break-after: always;
position: relative;
width: 8.5in;
height: 11in;
}
.page-content {
position: absolute;
width: 8.125in;
height: 10.625in;
left: 0.1875in;
top: 0.1875in;
color: var(--on-background);
background-color: var(--background);
}
.resumehead {
width: 100%;
padding: 16px;
h1 {
font-weight: 500;
text-transform: uppercase;
margin: 0;
font-size: 2.5rem;
line-height: 2.5rem;
}
h5 {
font-weight: 300;
text-transform: uppercase;
margin: 0;
}
.divider {
display: flex;
background-color: var(--on-background);
width: 100%;
height: 1px;
}
.resumehead-name {
float: right;
text-align: right;
}
.resumehead-contactinfo {
float: right;
text-align: right;
display: flex;
padding-top: 4px;
font-size: 13px;
.material-symbols-outlined {
font-variation-settings: "FILL" 1, "wght" 400, "GRAD" 0, "opsz" 24;
font-size: 16px;
}
:last-child {
margin-right: 0;
}
}
// Weird values but the picture midline is the divider line
.profile-image {
width: 17%;
border-radius: 50%;
position: absolute;
transform: translate(0, -5.75%);
margin-left: 2em;
border: 8px solid var(--background);
}
}
.contactinfo {
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
margin-right: 16px;
p {
font-weight: 300;
}
}
.resumebody {
margin-top: 3rem;
width: 100%;
display: grid;
grid-auto-columns: 1fr;
grid-template-columns: 30% 1fr;
grid-template-rows: 1fr;
gap: 0px 2em;
grid-template-areas: ". .";
h1,h2,h3,h4,h5,h6{
margin: 0;
margin-bottom: .5em;
}
p{
font-size: 12px;
}
.l{
margin-left: 16px;
}
.r{
margin-right: 16px;
}
}
.space{
height: 1em;
&-small{
height: .25em;
}
}
.timelineitem{
border-bottom: 1px solid var(--on-background);
border-left: 1px solid var(--on-background);
margin-bottom: .75rem;
margin-left: .25rem;
padding-left: 1rem;
padding-bottom: .25rem;
position: relative;
h1,h2,h3,h4,h5,h6{
margin: 0;
}
.date{
background-color: var(--background);
position: absolute;
padding-right: 5px;
padding-left: 2px;
top: 0;
left: 0;
transform-origin: 0 0;
transform: rotate(-90deg) translate(-99%, -55%);
}
}
.skill-container{
margin-bottom: .75rem;
border-bottom: 1px solid var(--on-background);
border-left: 1px solid var(--on-background);
padding-left: .5rem;
padding-bottom: .5rem;
}
.chip{
color: var(--background);
background-color: var(--on-background);
display: inline-flex;
flex-direction: row;
border: none;
outline: none;
padding: 0;
white-space: nowrap;
align-items: center;
border-radius: 16px;
vertical-align: middle;
text-decoration: none;
justify-content: center;
font-size: .75em;
}
.chip-content{
display: flex;
align-items: center;
user-select: none;
white-space: nowrap;
padding-left: .5rem;
padding-right: .5rem;
}
@media print {
body {
background: unset;
background-color: var(--background);
}
}

136
theme/template.ejs Normal file
View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Resume - <%= resume.basics.name %></title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
<style>
.material-symbols-outlined {
font-variation-settings: "FILL"0, "wght"400, "GRAD"0, "opsz"48;
}
*,
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
</style>
<% if (css.devBKG) { %>
<%- css.tagOpen + css.css + css.devBKG + css.tagClose %>
<% } else { %>
<%- css.tagOpen + css.css + css.tagClose %>
<% } %>
</head>
<body>
<div class="page">
<div class="page-content">
<div class="resumehead">
<img class="profile-image" src=<%= resume.basics.image %> alt="">
<div class="resumehead-name">
<h5><%= resume.basics.label %></h5>
<h1><%= resume.basics.name %></h1>
</div>
<span class="divider"></span>
<div class="resumehead-contactinfo">
<div class="contactinfo">
<span class="material-symbols-outlined">
phone
</span>
<p><%= resume.basics.phone %></p>
</div>
<div class="contactinfo">
<span class="material-symbols-outlined">
mail
</span>
<p><%= resume.basics.email %></p>
</div>
<div class="contactinfo">
<span class="material-symbols-outlined">
pin_drop
</span>
<p><%= resume.basics.location.address %> Matthews, NC</p>
</div>
</div>
</div>
<div class="resumebody">
<div class="l">
<h3>Profile</h3>
<p><%= resume.basics.summary %></p>
<br>
<h3>Skills</h3>
<% resume.skills.forEach(element => { %>
<!-- Timeline component -->
<div class="skill-container">
<h5><%= element.name %></h5>
<div class="chip-container">
<% element.keywords.forEach(word => { %>
<div class="chip"><div class="chip-content"><%= word %></div></div>
<% }) %>
</div>
</div>
<% }) %>
</div>
<div class="r">
<h3>Experience</h3>
<% resume.work.forEach(element => { %>
<!-- Timeline component -->
<div class="timelineitem">
<h4><%= element.position %></h4>
<p><i><%= element.name %></i></p>
<div class="space-small"></div>
<p><%= element.summary %></p>
<% if (element.endDate) { %>
<p class="date"><%= new Date(element.startDate).getFullYear() %> - <%= new Date(element.endDate).getFullYear() %></p>
<% } else { %>
<p class="date"><%= new Date(element.startDate).getFullYear() %> - Now</p>
<% } %>
</div>
<% }) %>
<div class="space"></div>
<h3>Education</h3>
<% resume.education.forEach(element => { %>
<!-- Timeline component -->
<div class="timelineitem">
<h4><%= element.area %></h4>
<p><i><%= element.studyType %></i></p>
<div class="space-small"></div>
<p><%= element.institution %></p>
<p class="date"><%= new Date(element.endDate).getFullYear() %> </p>
</div>
<% }) %>
<div class="space"></div>
<h3>Certifications</h3>
<% resume.certificates.forEach(element => { %>
<!-- Timeline component -->
<div class="timelineitem">
<h4><%= element.name %></h4>
<p><i><%= element.issuer %></i></p>
<p class="date"><%= new Date(element.date).getFullYear() %> </p>
</div>
<% }) %>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,150 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Resume - {basics.name}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200"
/>
<style>
.material-symbols-outlined {
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 48;
}
*,
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
:root {
font-family: "Open Sans", sans-serif;
--primary: #006494;
--on-primary: #ffffff;
--primary-container: #c8e6ff;
--on-primary-container: #001e31;
--secondary: #50606e;
--on-secondary: #ffffff;
--secondary-container: #d3e4f5;
--on-secondary-container: #0c1d29;
--tertiary: #65597b;
--on-tertiary: #ffffff;
--tertiary-container: #ecdcff;
--on-tertiary-container: #201634;
--error: #ba1b1b;
--error-container: #ffdad4;
--on-error: #ffffff;
--on-error-container: #410001;
--background: #fcfcff;
--on-background: #1a1c1e;
--surface: #fcfcff;
--on-surface: #1a1c1e;
--surface-variant: #dee3ea;
--on-surface-variant: #41474d;
--outline: #72787e;
--inverse-on-surface: #f0f0f3;
--inverse-surface: #2f3032;
--inverse-primary: #8bceff;
--error-container-alt: #ffd3cc;
}
body {
width: 8.5in;
height: 11in;
margin: 0;
padding: 0;
/* 'Danger' zone. Highlights content that would not print on the page */
background: repeating-linear-gradient(
45deg,
var(--error-container-alt),
var(--error-container-alt) 10px,
var(--error-container) 10px,
var(--error-container) 20px
)
fixed;
}
p {
margin: 0;
}
.page {
page-break-after: always;
position: relative;
width: 8.5in;
height: 11in;
}
.page-content {
position: absolute;
width: 8.125in;
height: 10.625in;
left: 0.1875in;
top: 0.1875in;
color: var(--on-background);
background-color: var(--background);
}
.resume-wrapper {
display: grid;
grid-template-columns: 30% 1fr;
grid-template-rows: 1fr;
grid-auto-columns: 1fr;
gap: 0px 0px;
grid-auto-flow: row;
grid-template-areas: ". .";
height: 100%;
}
.l-section{
background-color: var(--secondary);
color: var(--on-secondary);
text-align: center;
}
.r-section{
}
.profile-image{
width: 60%;
border-radius: 50%;
margin-top: 16px;
}
@media print {
body {
background: unset;
background-color: var(--background);
}
}
</style>
</head>
<body>
<div class="page">
<div class="page-content">
<div class="resume-wrapper">
<div class="l-section">
<img class="profile-image" src={basics.image} alt="" >
<h1 style="font-weight: normal">{basics.name}</h1>
</div>
<div class="r-section">
</div>
</div>
</div>
</div>
</body>
</html>