Skip to content

Commit 49325df

Browse files
committed
Optimizations, demo site and README refreshes
1 parent a5c8b2a commit 49325df

File tree

5 files changed

+844
-290
lines changed

5 files changed

+844
-290
lines changed

README.md

Lines changed: 120 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,131 @@
1+
# SkillSet
2+
13
![Logo](https://raw.githubusercontent.com/Jac21/SkillSet/master/assets/img/SkillSet.PNG)
24

35
[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)
46
![build workflow](https://github.com/Jac21/SkillSet/actions/workflows/build.yml/badge.svg)
57
[![donate](https://img.shields.io/badge/%24-Buy%20me%20a%20coffee-ff69b4.svg)](https://www.buymeacoffee.com/jac21)
68

7-
Intuitive job-candidate skill visualization, taking advantage of [D3.js](http://d3js.org/) and [JSONResume](https://jsonresume.org/).
9+
SkillSet is a lightweight front-end demo for turning a `JSONResume` skills section into an interactive D3 force graph. Instead of presenting skills as a flat list, it creates a visual map of categories and keywords that is easier to demo, discuss, and explore.
10+
11+
## Why SkillSet
812

9-
Live site:
10-
>- https://jac21.github.io/SkillSet/
13+
- Turns resume data into a more engaging visual story
14+
- Works with the familiar [JSONResume](https://jsonresume.org/) format
15+
- Lets you drag, inspect, and spotlight skill clusters in the browser
16+
- Keeps the project simple enough to customize for personal sites and portfolio demos
1117

12-
Live demo:
13-
>- https://jac21.github.io/viz.html
18+
## Live Links
1419

15-
React wrapper component created by [romain325](https://github.com/romain325):
16-
>- https://github.com/romain325/ReactSkillSet
20+
- Live site: [jac21.github.io/SkillSet](https://jac21.github.io/SkillSet/)
21+
- Live demo: [jac21.github.io/viz.html](https://jac21.github.io/viz.html)
22+
- React wrapper by [romain325](https://github.com/romain325): [ReactSkillSet](https://github.com/romain325/ReactSkillSet)
23+
24+
## Screenshot
1725

18-
Example Screenshot:
19-
-----------
2026
![Example](https://raw.githubusercontent.com/Jac21/SkillSet/master/assets/img/screencap.png)
27+
28+
## How It Works
29+
30+
1. Upload a resume file with a populated `skills` array.
31+
2. SkillSet reads each skill category and its keywords.
32+
3. The app builds a node-link graph where categories connect to individual skills.
33+
4. D3 renders the result as an interactive force-directed visualization.
34+
35+
Each edge is weighted from the JSONResume `level` field so proficiency can influence the relationship styling in the graph.
36+
37+
## Expected Resume Shape
38+
39+
SkillSet looks for the `skills` section from a standard JSONResume payload. A minimal example looks like this:
40+
41+
```json
42+
{
43+
"skills": [
44+
{
45+
"name": "Frontend",
46+
"level": "Advanced",
47+
"keywords": ["JavaScript", "D3.js", "CSS"]
48+
},
49+
{
50+
"name": "Backend",
51+
"level": "Intermediate",
52+
"keywords": ["Node.js", "REST APIs"]
53+
}
54+
]
55+
}
56+
```
57+
58+
Supported level mappings:
59+
60+
- `Beginner` -> `1.0`
61+
- `Intermediate` -> `2.0`
62+
- `Advanced` -> `2.5`
63+
64+
## Getting Started
65+
66+
### Prerequisites
67+
68+
- [Node.js](https://nodejs.org/)
69+
- npm
70+
71+
### Install
72+
73+
```bash
74+
npm install
75+
```
76+
77+
### Run Locally
78+
79+
Build the project:
80+
81+
```bash
82+
npm run build
83+
```
84+
85+
Serve the project locally:
86+
87+
```bash
88+
npm run serve
89+
```
90+
91+
For a development build:
92+
93+
```bash
94+
npm run dev
95+
```
96+
97+
Then open `index.html` through your local server and upload a JSON resume file to generate the visualization.
98+
99+
## Project Structure
100+
101+
```text
102+
.
103+
├── index.html
104+
├── src
105+
│ ├── app.js
106+
│ ├── index.js
107+
│ └── viz.js
108+
├── styles
109+
│ └── style.css
110+
└── assets
111+
├── img
112+
└── test-data
113+
```
114+
115+
## Demo Notes
116+
117+
- The UI is designed to work well for portfolio walkthroughs and screen sharing
118+
- Smaller, cleaner keyword sets produce the most readable graphs
119+
- Nodes can be clicked to emphasize labels and dragged to discuss clusters live
120+
121+
## Tech Stack
122+
123+
- HTML
124+
- CSS
125+
- JavaScript
126+
- [D3.js](https://d3js.org/)
127+
- [Webpack](https://webpack.js.org/)
128+
129+
## License
130+
131+
[MIT](https://opensource.org/licenses/mit-license.php)

index.html

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,105 @@
1515
</head>
1616

1717
<body>
18-
<img src="assets/img/SkillSet.PNG" alt="logo" />
19-
20-
<div class="visual content">
21-
<p class="body-text">
22-
If you'd like to see an example in action, click
23-
<a
24-
href="https://jac21.github.io/viz.html"
25-
target="_blank"
26-
rel="noopener noreferrer"
27-
>here!</a
28-
>
29-
</p>
30-
<!-- basic web form for json uploading and visualizing -->
31-
<form id="json-form">
32-
<p>
33-
Submit your
34-
<a href="https://jsonresume.org/" target="_blank">JSONResume</a> here
35-
for skillset visualization:
36-
</p>
37-
<input type="file" id="json-input" required />
38-
<button type="submit" id="json-input-button">Visualize</button>
39-
</form>
40-
41-
<p id="error-area"></p>
18+
<div class="page-shell">
19+
<header class="hero">
20+
<div class="hero-copy">
21+
<p class="eyebrow">Resume Storytelling With D3</p>
22+
<h1>Turn a static skill list into a living knowledge map.</h1>
23+
<p class="hero-text">
24+
SkillSet translates a JSONResume skill section into an interactive
25+
network that is easier to scan, demo, and discuss with hiring teams.
26+
</p>
27+
<div class="hero-actions">
28+
<a
29+
class="demo-link"
30+
href="https://jac21.github.io/viz.html"
31+
target="_blank"
32+
rel="noopener noreferrer"
33+
>View live example</a
34+
>
35+
<a
36+
class="text-link"
37+
href="https://jsonresume.org/"
38+
target="_blank"
39+
rel="noopener noreferrer"
40+
>About JSONResume</a
41+
>
42+
</div>
43+
</div>
44+
45+
<div class="hero-brand">
46+
<div class="brand-card">
47+
<img src="assets/img/SkillSet.PNG" alt="SkillSet logo" class="brand-logo" />
48+
<div class="stat-grid">
49+
<div class="stat-card">
50+
<span class="stat-label">Input</span>
51+
<strong>JSONResume</strong>
52+
</div>
53+
<div class="stat-card">
54+
<span class="stat-label">Output</span>
55+
<strong>Force Graph</strong>
56+
</div>
57+
<div class="stat-card">
58+
<span class="stat-label">Use Case</span>
59+
<strong>Live Demos</strong>
60+
</div>
61+
</div>
62+
</div>
63+
</div>
64+
</header>
65+
66+
<main class="workspace">
67+
<section class="panel controls-panel">
68+
<div class="panel-heading">
69+
<p class="eyebrow">Upload</p>
70+
<h2>Generate your visualization</h2>
71+
</div>
72+
73+
<p class="panel-copy">
74+
Drop in a resume export with a populated <code>skills</code> array and
75+
SkillSet will render the relationships between categories and keywords.
76+
</p>
77+
78+
<form id="json-form" class="upload-form">
79+
<label class="file-field" for="json-input">
80+
<span class="file-label">Choose a resume file</span>
81+
<input type="file" id="json-input" accept=".json,application/json" required />
82+
</label>
83+
<button type="submit" id="json-input-button">Visualize Skills</button>
84+
</form>
85+
86+
<p id="error-area"></p>
87+
88+
<div class="tips-card">
89+
<h3>Best for demos</h3>
90+
<p>
91+
Use a resume with a concise set of categories and high-signal keywords.
92+
Click a node to spotlight it, then drag clusters to talk through depth.
93+
</p>
94+
</div>
95+
</section>
96+
97+
<section class="panel viz-panel">
98+
<div class="panel-heading panel-heading-inline">
99+
<div>
100+
<p class="eyebrow">Visualization</p>
101+
<h2>Interactive skill map</h2>
102+
</div>
103+
<span class="viz-badge">D3 Force Layout</span>
104+
</div>
105+
106+
<div id="visualization-root">
107+
<div class="viz-empty-state">
108+
<p class="viz-empty-title">Your graph will appear here.</p>
109+
<p class="viz-empty-copy">
110+
Upload a JSONResume file to generate a clean, presentation-ready network
111+
view of your skills.
112+
</p>
113+
</div>
114+
</div>
115+
</section>
116+
</main>
42117
</div>
43118

44119
<a

src/app.js

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,51 @@
1-
// base csv string object
2-
let csv = "source,target,value";
3-
41
// error area declaration
52
let errorArea = document.getElementById("error-area");
63

7-
// convert JSONResume file input to csv file, download to user's machine
8-
const convertJson = function (e) {
4+
// convert JSONResume file input into force-layout links
5+
const convertJson = async function (e) {
96
e.preventDefault();
107

11-
// grab file value, create object URL to use in getJSON call
128
let jsonFileValue = document.getElementById("json-input").files[0];
13-
let jsonFile = window.URL.createObjectURL(jsonFileValue);
9+
if (!jsonFileValue) {
10+
errorArea.innerHTML =
11+
"<strong>Please choose a JSONResume file before visualizing.</strong>";
12+
return;
13+
}
1414

15-
// if browser supports fetch api
16-
if (self.fetch) {
17-
fetch(jsonFile)
18-
.then(function (response) {
19-
return response.json().then(function (json) {
20-
// iterate through json resume skill section
21-
for (var i = 0; i < json.skills.length; i++) {
22-
// iterate through keywords sub-section, append values to csv
23-
for (var j = 0; j < json.skills[i].keywords.length; j++) {
24-
csv +=
25-
"\n" +
26-
json.skills[i].name +
27-
"," +
28-
json.skills[i].keywords[j] +
29-
"," +
30-
setSkillValue(json.skills[i].level);
31-
}
32-
}
33-
// clear error area and visualize
34-
errorArea.innerHTML = "";
15+
if (!jsonFileValue.text) {
16+
errorArea.innerHTML =
17+
"<strong>Your browser does not support file parsing in this app. Please try Chrome, Firefox, or Safari.</strong>";
18+
return;
19+
}
3520

36-
forceLayoutVisualize(
37-
"data:attachment/csv," + encodeURIComponent(csv)
38-
);
21+
try {
22+
const rawJson = await jsonFileValue.text();
23+
const json = JSON.parse(rawJson);
24+
const links = [];
25+
26+
if (!Array.isArray(json.skills)) {
27+
throw new Error("The uploaded JSONResume file is missing a valid skills array.");
28+
}
29+
30+
json.skills.forEach(function (skill) {
31+
const keywords = Array.isArray(skill.keywords) ? skill.keywords : [];
32+
33+
keywords.forEach(function (keyword) {
34+
links.push({
35+
source: skill.name,
36+
target: keyword,
37+
value: setSkillValue(skill.level)
3938
});
40-
})
41-
.catch(function (error) {
42-
errorArea.innerHTML =
43-
"<strong>There has been a problem with your fetch operation: " +
44-
error.message +
45-
"</strong>";
4639
});
47-
} else {
40+
});
41+
42+
errorArea.innerHTML = "";
43+
forceLayoutVisualize(links);
44+
} catch (error) {
4845
errorArea.innerHTML =
49-
"<strong> Your browser does not support the fetch API! Please try this once more in Chrome or Firefox.</strong>";
46+
"<strong>There was a problem reading your JSONResume file: " +
47+
error.message +
48+
"</strong>";
5049
}
5150
};
5251

@@ -65,14 +64,14 @@ document
6564

6665
// set skill value numeric value from json value
6766
const setSkillValue = function (level) {
68-
var skillLevelValue = "0";
67+
var skillLevelValue = 0;
6968

7069
if (level === "Beginner") {
71-
skillLevelValue = "1.0";
70+
skillLevelValue = 1.0;
7271
} else if (level === "Intermediate") {
73-
skillLevelValue = "2.0";
72+
skillLevelValue = 2.0;
7473
} else if (level === "Advanced") {
75-
skillLevelValue = "2.5";
74+
skillLevelValue = 2.5;
7675
}
7776

7877
return skillLevelValue;

0 commit comments

Comments
 (0)