Skip to content

Commit 15b6d3e

Browse files
authored
Merge pull request #118 from vedansh-5/authApi
Authenticated API
2 parents ef91d8b + df13a16 commit 15b6d3e

File tree

6 files changed

+260
-65
lines changed

6 files changed

+260
-65
lines changed

README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,41 @@ Scrum Helper is not limited to the [FOSSASIA](https://github.com/fossasia) organ
116116
* Save your changes.
117117
* For Chrome: Rebuild or reload the extension in your browser (`chrome://extensions` → Refresh your extension).
118118
* For Firefox: Reload the temporary add-on by going to `about:debugging`"This Firefox" → Click "Reload" next to your extension.
119-
4. **Get Customized SCRUM Reports**
120-
- The reports will now be generated using contributions from your organization.
119+
120+
4. **How to Obtain a GitHub Personal Access Token**
121+
122+
- To use Scrum Helper with authenticated requests (for higher rate limits and private repositories), you need a GitHub personal access token.
123+
124+
#### Steps to Generate a Token
125+
126+
1. **Go to GitHub Developer Settings:**
127+
Visit [https://github.com/settings/tokens](https://github.com/settings/tokens) while logged in to your GitHub account.
128+
129+
2. **Choose Token Type:**
130+
- Select **"Personal access tokens (classic)"**.
131+
132+
3. **Generate a New Token:**
133+
- Click **"Generate new token"**.
134+
- Give your token a descriptive name (e.g., "Scrum Helper Extension").
135+
- Set an expiration date if desired.
136+
137+
4. **Create and Copy the Token:**
138+
- Click **"Generate token"** at the bottom.
139+
- **Copy the token** and save it securely. You will not be able to see it again!
140+
141+
5. **Paste the Token in Scrum Helper:**
142+
- Open the Scrum Helper extension popup.
143+
- Paste your token into the "GitHub Token" field.
144+
145+
> **Keep your token secret!** Never share it or commit it to public repositories.
146+
147+
**Why use a token?**
148+
GitHub tokens allow the extension to make authenticated requests, increasing your API rate limit and enabling access to private repositories if you grant those permissions.
149+
150+
---
151+
5. **Get Customized SCRUM Reports**
152+
- The reports will now be generated using contributions from your organization.
153+
121154

122155

123156
## About contributing

src/index.css

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -253,89 +253,98 @@ body,input,div,h3,h4,p,label,hr, #scrumReport{
253253
}
254254

255255
.tooltip-bubble {
256-
visibility: hidden;
257-
opacity: 0;
258-
position: absolute;
259-
background-color: #1f2937;
260-
color: white;
261-
text-align: left;
262-
padding: 12px;
263-
border-radius: 8px;
264-
font-size: 12px;
265-
line-height: 1.4;
256+
position: fixed !important;
257+
z-index: 9999;
258+
background: #f9fafb;
259+
color: #222;
260+
border-radius: 10px;
261+
border: 1px solid #e5e7eb;
262+
box-shadow: 0 4px 24px 0 rgba(0,0,0,0.12), 0 1.5px 4px 0 rgba(0,0,0,0.08);
263+
padding: 14px 18px;
264+
font-size: 13px;
265+
line-height: 1.5;
266+
min-width: 220px;
267+
max-width: 320px;
266268
white-space: normal;
267-
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
268-
z-index: 1000;
269-
transition: opacity 0.3s ease, visibility 0.3s ease;
270-
width: 280px;
269+
opacity: 0;
270+
pointer-events: none;
271+
transition: opacity 0.18s;
271272
}
272273

273-
.tooltip-bubble {
274-
bottom: 125%;
275-
left: 150%;
276-
transform: translateX(-50%);
274+
.tooltip-container:hover .tooltip-bubble,
275+
.tooltip-container:focus-within .tooltip-bubble {
276+
opacity: 1;
277+
pointer-events: auto;
278+
}
279+
280+
.tooltip-bubble a {
281+
color: #2563eb;
282+
text-decoration: underline;
277283
}
278284

279285
.tooltip-bubble::after {
280286
content: "";
281287
position: absolute;
282-
top: 100%;
283-
left: 50%;
284-
margin-left: -5px;
285-
border-width: 5px;
288+
width: 0; height: 0;
286289
border-style: solid;
287-
border-color: #1f2937 transparent transparent transparent;
288290
}
289291

290-
.tooltip-container.tooltip-bottom .tooltip-bubble {
291-
top: 125%;
292-
bottom: auto;
293-
}
294292

295-
.tooltip-container.tooltip-bottom .tooltip-bubble::after {
296-
top: -10px;
297-
border-color: transparent transparent #1f2937 transparent;
293+
.dark-mode .tooltip-bubble {
294+
background: #f3f4f6;
295+
color: #222;
296+
border: 1px solid #374151;
298297
}
299298

300-
.tooltip-container.tooltip-right .tooltip-bubble {
301-
top: -5px;
302-
left: 125%;
303-
bottom: auto;
304-
transform: none;
299+
.dark-mode .tooltip-bubble::after {
300+
border-color: #374151 transparent transparent transparent;
305301
}
306302

307-
.tooltip-container.tooltip-right .tooltip-bubble::after {
308-
top: 15px;
309-
left: -10px;
310-
border-color: transparent #1f2937 transparent transparent;
303+
.dark-mode .tooltip-container.tooltip-bottom .tooltip-bubble::after {
304+
border-color: transparent transparent #374151 transparent;
311305
}
312306

313-
.tooltip-container:hover .tooltip-bubble {
314-
visibility: visible;
315-
opacity: 1;
307+
.dark-mode .tooltip-container.tooltip-right .tooltip-bubble::after {
308+
border-color: transparent #374151 transparent transparent;
316309
}
317-
318-
.dark-mode .question-icon {
319-
color: #9ca3af;
310+
#toggleTokenVisibility {
311+
background: none;
312+
border: none;
313+
cursor: pointer;
314+
margin-left: 8px;
315+
padding: 0;
316+
line-height: 1;
320317
}
321-
322-
.dark-mode .question-icon:hover {
323-
color: #60a5fa;
318+
#tokenEyeIcon {
319+
font-size: 18px;
320+
vertical-align: middle;
324321
}
325-
326-
.dark-mode .tooltip-bubble {
327-
background-color: #374151;
328-
border: 1px solid #4b5563;
322+
@keyframes eye-rotate {
323+
0% { transform: rotate(0deg);}
324+
60% { transform: rotate(180deg);}
325+
100% { transform: rotate(180deg);}
326+
}
327+
.eye-animating {
328+
animation: eye-rotate 0.4s cubic-bezier(0.4,0,0.2,1);
329+
}
330+
.token-animating {
331+
transition: box-shadow 0.3s, background 0.3s, color 0.3s, opacity 0.3s, transform 0.3s;
332+
box-shadow: 0 0 0 2px #3b82f6;
333+
opacity: 0.7;
334+
transform: translateX(8px) scale(1.03);
329335
}
330336

331-
.dark-mode .tooltip-bubble::after {
332-
border-color: #374151 transparent transparent transparent;
337+
.dark-mode #githubToken,
338+
.dark-mode .token-preview-char {
339+
background: #404040 !important;
340+
border-color: #505050 !important;
341+
color: #fff !important;
333342
}
334343

335-
.dark-mode .tooltip-container.tooltip-bottom .tooltip-bubble::after {
336-
border-color: transparent transparent #374151 transparent;
344+
.dark-mode .token-preview-dot {
345+
background: #9ca3af !important;
337346
}
338347

339-
.dark-mode .tooltip-container.tooltip-right .tooltip-bubble::after {
340-
border-color: transparent #374151 transparent transparent;
348+
.dark-mode .token-preview-char {
349+
box-shadow: 0 1px 4px rgba(0,0,0,0.18);
341350
}

src/popup.html

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,28 @@ <h6 class="text-base font-semibold">Scrum Report</h6>
112112
<div id="settingsSection" class="tab-content hidden">
113113

114114
<div class="">
115-
<p class="text-sm font-medium">Enter cache TTL <span class="text-sm text-gray-800 font-normal">(in minutes)</span>
115+
<div class="flex items-center justify-between">
116+
<div class="flex">
117+
<h4>Your Github Token</h4>
118+
<span class="tooltip-container">
119+
<i class="fa fa-question-circle question-icon"></i>
120+
<span class="tooltip-bubble">
121+
<b>Why is it recommended to add a GitHub token?</b><br>
122+
Scrum Helper works without a GitHub token, but adding a personal access token is recommended for a better experience. It raises your API limits, allows access to private repos (if permitted), and improves accuracy and speed. Tokens are stored locally and never sent to us and used only to fetch your git data. You can add one anytime in the extension settings.<br><br>
123+
<b>How to obtain:</b><br>
124+
1. Go to <a href="https://github.com/settings/tokens" target="_blank" style="color:#2563eb;text-decoration:underline;">GitHub Developer Settings</a>.<br>
125+
3. Click "Generate new token", select classic token<br>
126+
4. Paste it here.<br>
127+
<i>Keep your token secret!</i>
128+
</span>
129+
</span>
130+
</div>
131+
<button id="toggleTokenVisibility" type="button" class="focus:outline-none">
132+
<i id="tokenEyeIcon" class="fa fa-eye text-gray-600"></i>
133+
</button>
134+
</div>
135+
<input id="githubToken" type="password" class="w-full border-2 border-gray-200 bg-gray-200 rounded-xl text-gray-800 p-2 my-2 pr-10" placeholder="Required for making authenticated requests">
136+
<p class="text-sm font-medium">Enter cache TTL <span class="text-sm font-normal">(in minutes)</span>
116137
<span class="tooltip-container">
117138
<i class="fa fa-question-circle question-icon"></i>
118139
<span class="tooltip-bubble">

src/scripts/main.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
let enableToggleElement = document.getElementById('enable');
22
let githubUsernameElement = document.getElementById('githubUsername');
3+
let githubTokenElement = document.getElementById('githubToken');
34
let cacheInputElement = document.getElementById('cacheInput');
45
let projectNameElement = document.getElementById('projectName');
56
let lastWeekContributionElement = document.getElementById('lastWeekContribution');
@@ -23,11 +24,15 @@ function handleBodyOnLoad() {
2324
'lastWeekContribution',
2425
'yesterdayContribution',
2526
'cacheInput',
27+
'githubToken',
2628
],
2729
(items) => {
2830
if (items.githubUsername) {
2931
githubUsernameElement.value = items.githubUsername;
3032
}
33+
if (items.githubToken) {
34+
githubTokenElement.value = items.githubToken;
35+
}
3136
if (items.projectName) {
3237
projectNameElement.value = items.projectName;
3338
}
@@ -206,6 +211,10 @@ function handleGithubUsernameChange() {
206211
let value = githubUsernameElement.value;
207212
chrome.storage.local.set({ githubUsername: value });
208213
}
214+
function handleGithubTokenChange() {
215+
let value = githubTokenElement.value;
216+
chrome.storage.local.set({ githubToken: value });
217+
}
209218
function handleProjectNameChange() {
210219
let value = projectNameElement.value;
211220
chrome.storage.local.set({ projectName: value });
@@ -235,6 +244,7 @@ function handleUserReasonChange() {
235244
}
236245
enableToggleElement.addEventListener('change', handleEnableChange);
237246
githubUsernameElement.addEventListener('keyup', handleGithubUsernameChange);
247+
githubTokenElement.addEventListener('keyup', handleGithubTokenChange);
238248
cacheInputElement.addEventListener('keyup', handleCacheInputChange);
239249
projectNameElement.addEventListener('keyup', handleProjectNameChange);
240250
startingDateElement.addEventListener('change', handleStartingDateChange);

src/scripts/popup.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ document.addEventListener('DOMContentLoaded', function() {
4747
const darkModeToggle = document.querySelector('img[alt="Night Mode"]');
4848
const settingsIcon = document.getElementById('settingsIcon');
4949
const body = document.body;
50+
const githubTokenInput = document.getElementById('githubToken');
51+
const toggleTokenBtn = document.getElementById('toggleTokenVisibility');
52+
const tokenEyeIcon = document.getElementById('tokenEyeIcon');
53+
let tokenVisible = false;
5054

5155
chrome.storage.local.get(['darkMode'], function(result) {
5256
if(result.darkMode) {
@@ -58,6 +62,18 @@ document.addEventListener('DOMContentLoaded', function() {
5862
}
5963
});
6064

65+
toggleTokenBtn.addEventListener('click', function() {
66+
tokenVisible = !tokenVisible;
67+
githubTokenInput.type = tokenVisible ? 'text' : 'password';
68+
69+
tokenEyeIcon.classList.add('eye-animating');
70+
setTimeout(() => tokenEyeIcon.classList.remove('eye-animating'), 400);
71+
tokenEyeIcon.className = tokenVisible ? 'fa fa-eye-slash text-gray-600' : 'fa fa-eye text-gray-600';
72+
73+
githubTokenInput.classList.add('token-animating');
74+
setTimeout(() => githubTokenInput.classList.remove('token-animating'), 300);
75+
});
76+
6177
darkModeToggle.addEventListener('click', function() {
6278
body.classList.toggle('dark-mode');
6379
const isDarkMode = body.classList.contains('dark-mode');
@@ -67,8 +83,28 @@ document.addEventListener('DOMContentLoaded', function() {
6783
if(settingsIcon){
6884
settingsIcon.src = isDarkMode ? 'icons/settings-night.png' : 'icons/settings-light.png';
6985
}
86+
renderTokenPreview();
7087
});
7188

89+
function renderTokenPreview() {
90+
tokenPreview.innerHTML = '';
91+
const value = githubTokenInput.value;
92+
const isDark = document.body.classList.contains('dark-mode');
93+
for (let i = 0; i < value.length; i++) {
94+
const charBox = document.createElement('span');
95+
charBox.className = 'token-preview-char' + (isDark ? ' dark-mode' : '');
96+
if (tokenVisible) {
97+
charBox.textContent = value[i];
98+
} else {
99+
const dot = document.createElement('span');
100+
dot.className = 'token-preview-dot' + (isDark ? ' dark-mode' : '');
101+
charBox.appendChild(dot);
102+
}
103+
tokenPreview.appendChild(charBox);
104+
setTimeout(() => charBox.classList.add('flip'), 10 + i * 30);
105+
}
106+
}
107+
72108
function updateContentState(enableToggle) {
73109
const elementsToToggle = [
74110
'startingDate',
@@ -80,6 +116,7 @@ document.addEventListener('DOMContentLoaded', function() {
80116
'showOpenLabel',
81117
'scrumReport',
82118
'githubUsername',
119+
'githubToken',
83120
'projectName',
84121
'settingsToggle',
85122
];
@@ -297,6 +334,46 @@ document.addEventListener('DOMContentLoaded', function() {
297334

298335
});
299336

337+
// Tooltip bubble
338+
document.querySelectorAll('.tooltip-container').forEach(container => {
339+
const bubble = container.querySelector('.tooltip-bubble');
340+
if (!bubble) return;
341+
342+
function positionTooltip() {
343+
const icon = container.querySelector('.question-icon') || container;
344+
const rect = icon.getBoundingClientRect();
345+
const bubbleRect = bubble.getBoundingClientRect();
346+
const padding = 8;
347+
348+
let top = rect.top + window.scrollY;
349+
let left = rect.right + padding + window.scrollX;
350+
351+
if (left + bubbleRect.width > window.innerWidth - 10) {
352+
left = rect.left - bubbleRect.width - padding + window.scrollX;
353+
}
354+
if (left < 8) left = 8;
355+
if (top + bubbleRect.height > window.innerHeight - 10) {
356+
top = rect.top - bubbleRect.height - padding + window.scrollY;
357+
}
358+
if (top < 8) top = 8;
359+
360+
bubble.style.left = left + 'px';
361+
bubble.style.top = top + 'px';
362+
}
363+
364+
container.addEventListener('mouseenter', positionTooltip);
365+
container.addEventListener('focusin', positionTooltip);
366+
container.addEventListener('mousemove', positionTooltip);
367+
container.addEventListener('mouseleave', () => {
368+
bubble.style.left = '';
369+
bubble.style.top = '';
370+
});
371+
container.addEventListener('focusout', () => {
372+
bubble.style.left = '';
373+
bubble.style.top = '';
374+
});
375+
});
376+
300377
// Radio button click handlers with toggle functionality
301378
document.querySelectorAll('input[name="timeframe"]').forEach(radio => {
302379
radio.addEventListener('click', function() {

0 commit comments

Comments
 (0)