Skip to content

Commit 3f8dd64

Browse files
committed
merge with main
Signed-off-by: Isaac Milarsky <imilarsky@gmail.com>
2 parents f8099c3 + 7862f28 commit 3f8dd64

7 files changed

Lines changed: 322 additions & 10 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.DS_Store

MAINTAINERS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ Total number of contributors: <!--CONTRIBUTOR COUNT START--> <!--CONTRIBUTOR COU
4949
<sub><b>Natalia Luzuriaga</b></sub>
5050
</a>
5151
</td>
52+
<td align="center">
53+
<a href="https://github.com/sachin-panayil">
54+
<img src="https://avatars.githubusercontent.com/u/79382140?v=4" width="100;" alt="sachin-panayil"/>
55+
<br />
56+
<sub><b>Sachin Panayil</b></sub>
57+
</a>
58+
</td>
5259
<td align="center">
5360
<a href="https://github.com/decause-gov">
5461
<img src="https://avatars.githubusercontent.com/u/107957201?v=4" width="100;" alt="decause-gov"/>

css/styles.css

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,59 @@ body {
44

55
textarea {
66
margin-bottom: 10px;
7+
}
8+
9+
#auto-generation {
10+
border-radius: 15px;
11+
padding: 20px;
12+
padding-bottom: 25px;
13+
margin-top: 20px;
14+
margin-bottom: 20px;
15+
background: lightgray;
16+
width: 100%;
17+
max-width: 800px;
18+
box-sizing: border-box;
19+
}
20+
21+
#github-url-form {
22+
margin-top: 25px;
23+
width: 100%;
24+
}
25+
26+
#repo-url {
27+
width: 100%;
28+
max-width: 750px;
29+
box-sizing: border-box;
30+
}
31+
32+
#repo-url-button {
33+
margin-top: 10px;
34+
border-radius: 5px;
35+
}
36+
37+
#notification {
38+
padding: 10px;
39+
margin-bottom: 15px;
40+
border-radius: 5px;
41+
position: relative;
42+
width: 100%;
43+
max-width: 800px;
44+
box-sizing: border-box;
45+
animation: slideDown 0.3s ease;
46+
}
47+
48+
#notification-message {
49+
margin: 0;
50+
padding-right: 20px;
51+
}
52+
53+
@keyframes slideDown {
54+
from {
55+
opacity: 0;
56+
transform: translateY(-10px);
57+
}
58+
to {
59+
opacity: 1;
60+
transform: translateY(0);
61+
}
762
}

index.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<html>
22
<head>
3+
<meta charset="utf-8">
34
<!-- USWDS not working -->
45
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> -->
56
<!-- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> -->
@@ -13,6 +14,9 @@
1314
<!-- <script src="https://cdn.form.io/formiojs/formio.full.js"></script> -->
1415
<!-- <script src="https://cdn.form.io/js/formio.embed.js"></script> -->
1516
<meta charset="utf-8">
17+
18+
<link rel="icon" type="image/x-icon" href="favicon.ico">
19+
1620
<!-- Uses bootstrap for now -->
1721
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>
1822
<link rel="stylesheet" href="css/styles.css">
@@ -24,6 +28,7 @@
2428
<!-- Render the form -->
2529
<script src="js/generateFormComponents.js"></script>
2630
<script src="js/formDataToJson.js"></script>
31+
<script src="js/autoGenerateFields.js"></script>
2732
<script type="text/javascript">
2833
createFormComponents()
2934
.then((components) => {
@@ -38,6 +43,7 @@
3843
},
3944
components: components,
4045
}).then(function (form) {
46+
window.formIOInstance = form;
4147
form.on("submit", function (submission) {
4248
console.log("form submission here:", submission);
4349
createCodeJson(submission.data);
@@ -51,13 +57,29 @@
5157
</head>
5258
<body>
5359
<div id="form-header"></div>
60+
61+
<div id="notification" style="display: none;">
62+
<p id="notification-message"></p>
63+
</div>
64+
65+
<div id="auto-generation">
66+
<p id="auto-generation-header"></p>
67+
<form id="github-url-form">
68+
<label for="repo-url">GitHub Repository URL</label><br>
69+
<input type="text" id="repo-url" name="repo-url"><br>
70+
<input type="submit" id="repo-url-button" value="Submit">
71+
</form>
72+
</div>
73+
5474
<div id="formio"></div>
75+
5576
<div id="output">
5677
<label for="json-result">Your JSON Metadata </label>
5778
<textarea class="form-control" rows="10" id="json-result" readonly></textarea>
5879
<button type="button" class="btn btn-outline" href="#" onclick="copyToClipboard(event)">Copy</button>
5980
<button type="button" class="btn btn-outline" href="#" onclick="downloadFile(event)">Download</button>
6081
<button type="button" class="btn btn-outline" href="#" onclick="createProjectPR(event)">Create Pull Request</button>
6182
</div>
83+
6284
</body>
6385
</html>

js/autoGenerateFields.js

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
document.addEventListener("DOMContentLoaded", function() {
2+
setupFormHandler();
3+
setupNotificationSystem();
4+
});
5+
6+
// This works by creating an object with methods for different notification types of either error or success
7+
// Calling either of these methods calls the main functionality, show(), which manipulates the notification element in HTML
8+
// The show() method changes the element based on type and displays the message to the user
9+
// The hide() function makes sure that the notification fades away after 5 seconds
10+
const notificationSystem = {
11+
show: function(message, type = 'error') {
12+
const notification = document.getElementById('notification');
13+
const messageElement = document.getElementById('notification-message');
14+
15+
messageElement.textContent = message;
16+
17+
if (type === 'error') {
18+
notification.style.backgroundColor = '#f8d7da';
19+
notification.style.color = '#721c24';
20+
notification.style.border = '1px solid #f5c6cb';
21+
} else {
22+
notification.style.backgroundColor = '#d4edda';
23+
notification.style.color = '#155724';
24+
notification.style.border = '1px solid #c3e6cb';
25+
}
26+
27+
notification.style.display = 'block';
28+
setTimeout(() => {
29+
notification.style.opacity = '1';
30+
}, 10);
31+
32+
clearTimeout(this.timeout);
33+
this.timeout = setTimeout(() => this.hide(), 5000);
34+
},
35+
36+
hide: function() {
37+
const notification = document.getElementById('notification');
38+
notification.style.opacity = '0';
39+
setTimeout(() => {
40+
notification.style.display = 'none';
41+
}, 500);
42+
},
43+
44+
error: function(message) {
45+
this.show(message, 'error');
46+
},
47+
48+
success: function(message) {
49+
this.show(message, 'success');
50+
},
51+
};
52+
53+
function setupNotificationSystem() {
54+
const notification = document.getElementById('notification');
55+
if (notification) {
56+
notification.style.opacity = '0';
57+
notification.style.transition = 'opacity 0.5s ease';
58+
}
59+
}
60+
61+
function setupFormHandler() {
62+
const form = document.getElementById("github-url-form");
63+
64+
form.addEventListener("submit", async function(event) {
65+
event.preventDefault();
66+
67+
const submitButton = document.getElementById("repo-url-button");
68+
69+
submitButton.value = "Loading...";
70+
submitButton.disabled = true;
71+
72+
try {
73+
const repoURL = document.getElementById("repo-url").value;
74+
75+
if (repoURL.length == 0) {
76+
throw new Error("Please enter a GitHub repository URL");
77+
}
78+
79+
const repoInfo = extractGitHubInfo(repoURL);
80+
81+
if (!repoInfo) {
82+
throw new Error("Invalid GitHub URL format. Please enter a valid GitHub repository URL ->(https://github.com/username/repository)");
83+
}
84+
85+
const repositoryInfo = await getRepoInformation(repoInfo);
86+
const languages = await getRepoLanguages(repoInfo)
87+
88+
if (repositoryInfo) {
89+
preFillFields(repositoryInfo, languages);
90+
notificationSystem.success("Repository data loaded successfully!");
91+
} else {
92+
throw new Error("Could not fetch repository information. Please check the URL and try again.");
93+
}
94+
95+
} catch (error) {
96+
console.error(error.message);
97+
notificationSystem.error(error.message);
98+
} finally {
99+
submitButton.value = "Submit";
100+
submitButton.disabled = false;
101+
}
102+
});
103+
}
104+
105+
function extractGitHubInfo(url) {
106+
// Regex pattern to match GitHub URLs and extract org and repo
107+
const regex = /(?:https?:\/\/)?(?:www\.)?github\.com\/([^\/]+)\/([^\/\s]+)/;
108+
const match = url.match(regex);
109+
110+
if (match && match.length === 3) {
111+
return {
112+
organization: match[1],
113+
repository: match[2]
114+
};
115+
}
116+
117+
return null;
118+
}
119+
120+
async function getRepoInformation(repoInfo) {
121+
const baseURL = "https://api.github.com/repos/";
122+
const endpoint = `${baseURL}${repoInfo.organization}/${repoInfo.repository}`;
123+
124+
try {
125+
const response = await fetch(endpoint);
126+
127+
if (!response.ok) {
128+
throw new Error(`GitHub API error (${response.status}): ${response.statusText}`);
129+
}
130+
131+
return await response.json();
132+
} catch (error) {
133+
console.error("Fetch error:", error.message);
134+
}
135+
}
136+
137+
async function getRepoLanguages(repoInfo) {
138+
const endpoint = `https://api.github.com/repos/${repoInfo.organization}/${repoInfo.repository}/languages`
139+
140+
try {
141+
const response = await fetch(endpoint);
142+
143+
if (!response.ok) {
144+
throw new Error(`GitHub API error (${response.status}): ${response.statusText}`);
145+
}
146+
147+
return await response.json();
148+
} catch (error) {
149+
console.error("Fetch error:", error.message);
150+
}
151+
}
152+
153+
function preFillFields(repoData, languages) {
154+
if (!window.formIOInstance) {
155+
notificationSystem.error("Form interface not initialized. Please refresh and try again.");
156+
return;
157+
}
158+
159+
try {
160+
const currentSubmission = {}
161+
162+
window.formIOInstance.components.forEach(component => {
163+
if (component.components) {
164+
component.components.forEach(nestedComp => {
165+
if (nestedComp.key) {
166+
currentSubmission[nestedComp.key] = nestedComp.getValue()
167+
}
168+
});
169+
} else if (component.key) {
170+
currentSubmission[component.key] = component.getValue()
171+
}
172+
})
173+
174+
let licenses = [];
175+
if (repoData.license && repoData.license.spdx_id) {
176+
licenses.push({
177+
name: repoData.license.spdx_id,
178+
URL: repoData.html_url + "/blob/main/LICENSE"
179+
});
180+
}
181+
182+
const newSubmission = {
183+
name: repoData.name || '',
184+
description: repoData.description || '',
185+
repositoryURL: repoData.html_url || '',
186+
repositoryVisibility: repoData.private ? "private" : "public",
187+
vcs: 'git',
188+
permissions: {
189+
licenses: licenses
190+
},
191+
reuseFrequency: {
192+
forks: repoData.forks_count || 0
193+
},
194+
languages: Object.keys(languages) || [],
195+
date: {
196+
created: repoData.created_at || '',
197+
lastModified: repoData.updated_at || '',
198+
metaDataLastUpdated: new Date().toISOString()
199+
},
200+
tags: repoData.topics || [],
201+
feedbackMechanisms: [repoData.html_url + "/issues"]
202+
}
203+
204+
const mergedSubmission = { ...currentSubmission, ...newSubmission}
205+
206+
window.formIOInstance.setSubmission({ data: mergedSubmission })
207+
208+
} catch (error) {
209+
notificationSystem.error("Error filling form fields with repository data. Please refresh and try again");
210+
console.error("Form fill error:", error);
211+
}
212+
}
213+
214+
// This is global so we could use this throughout the website!
215+
window.showErrorNotification = function(message) {
216+
notificationSystem.error(message);
217+
};
218+
219+
window.showSuccessNotification = function(message) {
220+
notificationSystem.success(message);
221+
};

js/generateFormComponents.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ function createFormHeading(title, description) {
250250
container.innerHTML = `<h1>${title}</h1>\n<h2>${description}</h2>`;
251251
}
252252

253+
function createAutoGenerationBox() {
254+
const container = document.getElementById("auto-generation-header")
255+
container.innerHTML = `<h3>Auto Generate Fields</h3> \n <h4> Please enter your repositories GitHub URL in order to automatically pre-fill some of the fields in this form! </h4> \n <h6> <i>This currently only works on <b>public</b> repositories</i> </h6>`
256+
}
257+
253258
// Iterates through each json field and creates component array for Form.io
254259
function createAllComponents(schema, prefix = ""){
255260
let components = [];
@@ -293,6 +298,7 @@ async function createFormComponents() {
293298
console.log("JSON Data:", jsonData);
294299

295300
createFormHeading(jsonData["title"], jsonData["description"]);
301+
createAutoGenerationBox()
296302

297303
components = createAllComponents(jsonData);
298304

0 commit comments

Comments
 (0)