Compare commits
5 commits
cce0e0d08b
...
49b7a59c05
Author | SHA1 | Date | |
---|---|---|---|
|
49b7a59c05 | ||
|
3edaa49ce7 | ||
|
9cce6dbf0a | ||
|
ff3beac8ff | ||
|
139d80f2fe |
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
images/thumbnails
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
max-width: unset;
|
width: unset;
|
||||||
width: 90vw;
|
max-width: unset;
|
||||||
margin-left: -0.5rem;
|
margin-left: -0.5rem;
|
||||||
margin-right: -0.5rem;
|
margin-right: -0.5rem;
|
||||||
margin-top: 0.8rem;
|
margin-top: 0.8rem;
|
||||||
|
@ -21,8 +21,7 @@
|
||||||
img {}
|
img {}
|
||||||
|
|
||||||
.project-image > img {
|
.project-image > img {
|
||||||
max-width: 90%;
|
margin: 0 0.5rem;
|
||||||
margin: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-links img {
|
.social-links img {
|
||||||
|
|
40
css/main.css
|
@ -104,3 +104,43 @@ section {
|
||||||
.project-info {
|
.project-info {
|
||||||
margin: 1.5em 0 5em 0;
|
margin: 1.5em 0 5em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-x {
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: end;
|
||||||
|
padding: 10px 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-modal-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
background: rgba(0,0,0,0.25);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-modal-flex {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-modal {
|
||||||
|
flex-grow: 0;
|
||||||
|
max-width: 90vh;
|
||||||
|
background: #FFF;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#image-modal-contents img {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 25px 25px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
css/
|
css/
|
||||||
images/
|
images/
|
||||||
|
js/
|
||||||
index.html
|
index.html
|
||||||
|
|
BIN
images/curricular-skills-api.png
Normal file
After Width: | Height: | Size: 184 KiB |
BIN
images/skillabi-1-login.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
images/skillabi-2.gif
Normal file
After Width: | Height: | Size: 417 KiB |
BIN
images/skillabi-3.webp
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
images/skills-match-2.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
images/skills-match-img-1.webp
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
images/skills-match-img-3.webp
Normal file
After Width: | Height: | Size: 31 KiB |
139
index.html
|
@ -3,7 +3,7 @@
|
||||||
<link rel="stylesheet" href="css/main.css" />
|
<link rel="stylesheet" href="css/main.css" />
|
||||||
<link rel="stylesheet" media="screen and (max-width: 700px)" href="css/700.css" />
|
<link rel="stylesheet" media="screen and (max-width: 700px)" href="css/700.css" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,400i,500,500i" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Roboto:400,400i,500,500i" rel="stylesheet">
|
||||||
<title>Javascipt Developer - Portfolio</title>
|
<title>Fullstack Developer - Portfolio</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="flex-container">
|
<div class="flex-container">
|
||||||
|
@ -33,29 +33,110 @@
|
||||||
<section class="section about">
|
<section class="section about">
|
||||||
<h2>About Me</h2>
|
<h2>About Me</h2>
|
||||||
<p class="attention">I'm a full-stack engineer with experience building
|
<p class="attention">I'm a full-stack engineer with experience building
|
||||||
and deploying web based applications from the ground up as well as
|
and deploying web based applications, building out CI/CD pipelines and
|
||||||
debugging and maintaining legacy systems. I am looking for a remote
|
maintaining legacy applications. I am looking for a remote position
|
||||||
full-stack/frontend position with a team that embraces remote work.</p>
|
building web based software with a team that embraces remote work.</p>
|
||||||
|
|
||||||
<p>I have been working with websites and code since I was a kid and then
|
<p>I have been working with websites and code since I was a kid and then
|
||||||
worked 7 years in technical support where I gained a solid
|
worked 7 years in technical support where I gained a solid understanding
|
||||||
understanding of the web and how it works under the hood. I have spent
|
of the web and how it works under the hood. I have spent the last 10 years
|
||||||
the last 4 years designing, building, deploying and maintaining
|
designing, building, deploying and maintaining javascript and typescript
|
||||||
javascript applications. I've worked with iOS, PHP, NodeJS, and
|
applications. I've worked with NodeJS, React, and Serverless.js. I
|
||||||
React/Redux. I enjoy learning better ways to do things, solving
|
enjoy learning better ways to do things, solving difficult problems and
|
||||||
difficult problems and building software that assists my clients/users
|
building software that helps people. I live in Moscow Idaho and my
|
||||||
in achieving success. I live in Hayden Idaho and my hobbies include
|
hobbies include Devops, listening` to podcasts, playing ukulele and,
|
||||||
Devops, listening to podcasts, playing ukulele and, especially,
|
especially, spending time with my wife and 4 kids.</p>
|
||||||
spending time with my wife and 3 kids.</p>
|
|
||||||
</section>
|
</section>
|
||||||
<section class="section projects">
|
<section class="section projects">
|
||||||
<h2>Recent Software Projects</h2>
|
<h2>Recent Software Projects</h2>
|
||||||
<h3>Dashdrop</h3>
|
<h3><a href="https://www.economicmodeling.com/skillabi/">Skillabi</a></h3>
|
||||||
<div class="project">
|
<div class="project">
|
||||||
<div class="project-image">
|
<div class="project-image">
|
||||||
<img class="main-image" src="images/dashdrop-1.png"/>
|
<img class="main-image" src="images/thumbnails/th_skillabi-1-login.png"/>
|
||||||
<img class="main-image" src="images/dashdrop-2.png"/>
|
<img class="main-image" src="images/thumbnails/th_skillabi-2.gif"/>
|
||||||
<img class="main-image" src="images/dashdrop-3.png"/>
|
<img class="main-image" src="images/thumbnails/th_skillabi-3.webp"/>
|
||||||
|
<img class="main-image" src="images/thumbnails/th_curricular-skills-api.png"/>
|
||||||
|
</div>
|
||||||
|
<div class="project-info">
|
||||||
|
<h4>Project Goals</h4>
|
||||||
|
<p>Skillabi is a tool to help Higher Education institutions to find
|
||||||
|
skills that are relivant to their curriculum and in-demand in the
|
||||||
|
market place so they can adjust they're curriculum to be more
|
||||||
|
relevant to the developing job market. Skills are parsed from
|
||||||
|
course data and then compared with skills job postings data for
|
||||||
|
relevant occupations.</p>
|
||||||
|
<h4>My Contributions</h4>
|
||||||
|
|
||||||
|
<p>I worked on a 2-3 man team to build a prototype using netlify. We then
|
||||||
|
itterated on it in 3 phases. The prototype was successful enough
|
||||||
|
that customers requested to pay to use it (although it chock full
|
||||||
|
of technical debt on the inside.) When it came time to build a
|
||||||
|
production version, I took over as the technical lead on the
|
||||||
|
project. This included designing a new version of the backend APIs
|
||||||
|
and leading the reimplementation of the frontend.</p>
|
||||||
|
|
||||||
|
<p>The new APIs for storing curricular data and user profiles
|
||||||
|
needed to be secure, easy to use, well tested, well documented,
|
||||||
|
scalable and reliable. We ensured that security was implemeted in
|
||||||
|
the back end first. We used cognito tokens and JWT claims to scope
|
||||||
|
user access by role. We designed api search queries for the a
|
||||||
|
curricular data that ensured the API could be easily expanded and
|
||||||
|
would be consistant across endpoints, while still being powerful
|
||||||
|
enought to do all the searching/filtering that would be needed in
|
||||||
|
the frontend. We implemented the API on AWS serverless lambdas for
|
||||||
|
scalability and reliability and used typescript on serverless.js
|
||||||
|
for ease of development.</p>
|
||||||
|
|
||||||
|
<p>The new frontend was developed with react, written in typescript
|
||||||
|
and served via cloudfront and s3 for scalabilty, reliability and
|
||||||
|
simplicity. It also had to meet the accessiblity requirements of US
|
||||||
|
and UK educational institutions. I was tasked with implementing
|
||||||
|
some of the more complicated problems, including realtime
|
||||||
|
highlighting of skills within a text area as users type (The
|
||||||
|
parsing of the skills was handled by an external API), and
|
||||||
|
identifying boilerplate text accross many courses in a curriculum
|
||||||
|
for parsing. </p>
|
||||||
|
<h4>Technologies Used</h4>
|
||||||
|
<p>Typescript, Terraform, Gitlab CI, Fugue, OpenId/OAuth, React,
|
||||||
|
Accessibility, Serverless.js, react-query, JWT, KONG</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Skillsmatch</h3>
|
||||||
|
<div class="project">
|
||||||
|
<div class="project-image">
|
||||||
|
<img class="main-image" src="images/thumbnails/th_skills-match-img-1.webp"/>
|
||||||
|
<img class="main-image" src="images/thumbnails/th_skills-match-2.png"/>
|
||||||
|
<img class="main-image" src="images/thumbnails/th_skills-match-img-3.webp"/>
|
||||||
|
</div>
|
||||||
|
<div class="project-info">
|
||||||
|
<h4>Project Goals</h4>
|
||||||
|
<p>Skillsmtch is an web based app that schools can provide to their
|
||||||
|
students and potential student who are coming back to school after
|
||||||
|
some time in the workplace. Skillsmatch helps these adult learners
|
||||||
|
catalogue the skills they already have so they can see what skills
|
||||||
|
they could then learn to make help with either making themselves
|
||||||
|
more marketable or shifting careers. It then helps them identify
|
||||||
|
courses or degrees that could help them to learn and demonstrate
|
||||||
|
those skills to potential employers.</p>
|
||||||
|
<h4>My Contributions</h4>
|
||||||
|
<p>I worked with my team as a newly hired developer at the company
|
||||||
|
to quickly rebuild the already developed prototype. We build out
|
||||||
|
out necessary APIs using serverless.js/Mongodb and frontend in
|
||||||
|
Javascript with React/Redux. I wrote code for, reviewed and QA'd
|
||||||
|
tickets. I particularly enjoyed teaching new developers and interns
|
||||||
|
as they were hired, implementing API tests on our new APIs and
|
||||||
|
building out dev ops systems to automatically deploy to AWS.</p>
|
||||||
|
<h4>Technologies Used</h4>
|
||||||
|
<p>React, Cypress, AWS Elastic Beanstalk, Serverless.js,
|
||||||
|
Accessibility, Redux, Docker, Codeship CI</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>Dash Drop</h3>
|
||||||
|
<div class="project">
|
||||||
|
<div class="project-image">
|
||||||
|
<img class="main-image" src="images/thumbnails/th_dashdrop-1.png"/>
|
||||||
|
<img class="main-image" src="images/thumbnails/th_dashdrop-2.png"/>
|
||||||
|
<img class="main-image" src="images/thumbnails/th_dashdrop-3.png"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h4>Project Goals</h4>
|
<h4>Project Goals</h4>
|
||||||
|
@ -71,13 +152,12 @@
|
||||||
Javascript, where needed, to change the structure of the
|
Javascript, where needed, to change the structure of the
|
||||||
interface.</p>
|
interface.</p>
|
||||||
<h4>Technologies Used</h4>
|
<h4>Technologies Used</h4>
|
||||||
<p>CSS and some JQuery</p>
|
<p>HTML, CSS, Javascript and some JQuery</p> </div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<h3>NewVistas Website Platform</h3>
|
<h3>NewVistas Website Platform</h3>
|
||||||
<div class="project">
|
<div class="project">
|
||||||
<div class="project-image">
|
<div class="project-image">
|
||||||
<img class="main-image" src="images/newvistas.jpg"/>
|
<img class="main-image" src="images/thumbnails/th_newvistas.jpg"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h4>Project Goals</h4>
|
<h4>Project Goals</h4>
|
||||||
|
@ -130,8 +210,8 @@
|
||||||
<h3>Payzoom Econosystem Platform</h3>
|
<h3>Payzoom Econosystem Platform</h3>
|
||||||
<div class="project">
|
<div class="project">
|
||||||
<div class="project-image">
|
<div class="project-image">
|
||||||
<img alt="The Payzoom Logo" src="images/PayZoom_logo.png"/>
|
<img alt="The Payzoom Logo" src="images/thumbnails/th_PayZoom_logo.png"/>
|
||||||
<img class="main-image" alt="Payzoom Software Demo" src="images/payzoom.png"/>
|
<img class="main-image" alt="Payzoom Software Demo" src="images/thumbnails/th_payzoom.png"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h4>Project Goal</h4>
|
<h4>Project Goal</h4>
|
||||||
|
@ -172,8 +252,8 @@
|
||||||
<h3>Novatek Native Wrapper App</h3>
|
<h3>Novatek Native Wrapper App</h3>
|
||||||
<div class="project">
|
<div class="project">
|
||||||
<div class="project-image">
|
<div class="project-image">
|
||||||
<img class="main-image" alt="Workaids App Icon" src="images/icon_work_aids.png" />
|
<img class="main-image" alt="Workaids App Icon" src="images/thumbnails/th_icon_work_aids.png" />
|
||||||
<img alt="Workaids Login Screen" src="images/workaids_login2.png" />
|
<img alt="Workaids Login Screen" src="images/thumbnails/th_workaids_login2.png" />
|
||||||
</div>
|
</div>
|
||||||
<div class="project-info">
|
<div class="project-info">
|
||||||
<h4>Project Goals</h4>
|
<h4>Project Goals</h4>
|
||||||
|
@ -184,7 +264,7 @@
|
||||||
built to wrap the current web interface and allow
|
built to wrap the current web interface and allow
|
||||||
reusing the existing web based code as much as
|
reusing the existing web based code as much as
|
||||||
possible.</p>
|
possible.</p>
|
||||||
<h4>My Contrinbutions</h4>
|
<h4>My Contributions</h4>
|
||||||
<p>Initially I helped write many of the api calls for
|
<p>Initially I helped write many of the api calls for
|
||||||
the js library the web app used to communicate through
|
the js library the web app used to communicate through
|
||||||
the webFrame to the native code. Later I helped out
|
the webFrame to the native code. Later I helped out
|
||||||
|
@ -209,4 +289,13 @@
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="image-modal-wrapper" hidden>
|
||||||
|
<div id="image-modal-flex">
|
||||||
|
<div id="image-modal">
|
||||||
|
<div class="close-x clickable" aria-label="click to close modal">×</div>
|
||||||
|
<div id="image-modal-contents" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="./js/image-thumbnails.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
51
js/image-thumbnails.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
'use srict';
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
const imageModalWrapper = document.querySelector('#image-modal-wrapper');
|
||||||
|
const imageModalFlex = document.querySelector('#image-modal-flex');
|
||||||
|
const imageModal = document.querySelector('#image-modal');
|
||||||
|
const imageModalContents = document.querySelector('#image-modal-contents');
|
||||||
|
|
||||||
|
imageModal.querySelector('.close-x').addEventListener('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
imageModalWrapper.hidden = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
imageModalFlex.addEventListener('click', function(e) {
|
||||||
|
if(e.target === imageModalFlex) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
imageModalWrapper.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keydown', function(e) {
|
||||||
|
const modalCloseKeys = ['escape', 'tab']
|
||||||
|
console.log("e.key: ", e.key);
|
||||||
|
if(!imageModalWrapper.hidden) {
|
||||||
|
if(modalCloseKeys.includes(e.key.toLowerCase())) {
|
||||||
|
imageModalWrapper.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const images = document.querySelectorAll('img');
|
||||||
|
images.forEach(function(img) {
|
||||||
|
const name = img.src.split('/').pop();
|
||||||
|
|
||||||
|
if(name.match(/^th_/)) {
|
||||||
|
img.classList.add('clickable');
|
||||||
|
img.addEventListener('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
imageModalWrapper.hidden = undefined;
|
||||||
|
const fullImage = document.createElement('img');
|
||||||
|
fullImage.src = `./images/${name.replace(/^th_/, '')}`;
|
||||||
|
imageModalContents.replaceChildren(fullImage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
|
@ -5,7 +5,8 @@
|
||||||
"main": "index.html",
|
"main": "index.html",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo 'no tests yet.... so... pass?'",
|
"test": "echo 'no tests yet.... so... pass?'",
|
||||||
"start": "http-server ./"
|
"start": "http-server ./",
|
||||||
|
"thumbs": "./scripts/process-images.sh"
|
||||||
},
|
},
|
||||||
"author": "john@jshaver.net",
|
"author": "john@jshaver.net",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
|
|
17
scripts/process-images.sh
Executable file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
IMAGES_DIR="./images"
|
||||||
|
WIDTH=320
|
||||||
|
|
||||||
|
for f in ./images/*
|
||||||
|
do
|
||||||
|
case "$f" in
|
||||||
|
*.jpeg|*.jpg|*.png|*.webp|*.gif)
|
||||||
|
echo "Processing $f"
|
||||||
|
convert $f -resize $WIDTH\> ./images/thumbnails/th_`basename $f`
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Ignoring $f"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|