Skip to content

Commit bb87132

Browse files
authored
Merge branch 'master' into settingEnhanc
2 parents 5a32694 + 15b6d3e commit bb87132

File tree

6 files changed

+261
-63
lines changed

6 files changed

+261
-63
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: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -253,87 +253,96 @@ 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

339348
.dark-mode .tooltip-container.tooltip-right .tooltip-bubble::after {
@@ -353,4 +362,7 @@ body,input,div,h3,h4,p,label,hr, #scrumReport{
353362
}
354363
.dark-mode #homeButton:active {
355364
background: #374151;
365+
=======
366+
.dark-mode .token-preview-char {
367+
box-shadow: 0 1px 4px rgba(0,0,0,0.18);
356368
}

src/popup.html

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

119119
<div class="">
120-
<p class="text-sm font-medium">Enter cache TTL <span class="text-sm text-gray-800 font-normal">(in minutes)</span>
120+
<div class="flex items-center justify-between">
121+
<div class="flex">
122+
<h4>Your Github Token</h4>
123+
<span class="tooltip-container">
124+
<i class="fa fa-question-circle question-icon"></i>
125+
<span class="tooltip-bubble">
126+
<b>Why is it recommended to add a GitHub token?</b><br>
127+
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>
128+
<b>How to obtain:</b><br>
129+
1. Go to <a href="https://github.com/settings/tokens" target="_blank" style="color:#2563eb;text-decoration:underline;">GitHub Developer Settings</a>.<br>
130+
3. Click "Generate new token", select classic token<br>
131+
4. Paste it here.<br>
132+
<i>Keep your token secret!</i>
133+
</span>
134+
</span>
135+
</div>
136+
<button id="toggleTokenVisibility" type="button" class="focus:outline-none">
137+
<i id="tokenEyeIcon" class="fa fa-eye text-gray-600"></i>
138+
</button>
139+
</div>
140+
<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">
141+
<p class="text-sm font-medium">Enter cache TTL <span class="text-sm font-normal">(in minutes)</span>
121142
<span class="tooltip-container">
122143
<i class="fa fa-question-circle question-icon"></i>
123144
<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
@@ -54,6 +54,10 @@ document.addEventListener('DOMContentLoaded', function() {
5454
const settingsSection = document.getElementById('settingsSection');
5555

5656
let isSettingsVisible = false;
57+
const githubTokenInput = document.getElementById('githubToken');
58+
const toggleTokenBtn = document.getElementById('toggleTokenVisibility');
59+
const tokenEyeIcon = document.getElementById('tokenEyeIcon');
60+
let tokenVisible = false;
5761

5862
chrome.storage.local.get(['darkMode'], function(result) {
5963
if(result.darkMode) {
@@ -65,6 +69,18 @@ document.addEventListener('DOMContentLoaded', function() {
6569
}
6670
});
6771

72+
toggleTokenBtn.addEventListener('click', function() {
73+
tokenVisible = !tokenVisible;
74+
githubTokenInput.type = tokenVisible ? 'text' : 'password';
75+
76+
tokenEyeIcon.classList.add('eye-animating');
77+
setTimeout(() => tokenEyeIcon.classList.remove('eye-animating'), 400);
78+
tokenEyeIcon.className = tokenVisible ? 'fa fa-eye-slash text-gray-600' : 'fa fa-eye text-gray-600';
79+
80+
githubTokenInput.classList.add('token-animating');
81+
setTimeout(() => githubTokenInput.classList.remove('token-animating'), 300);
82+
});
83+
6884
darkModeToggle.addEventListener('click', function() {
6985
body.classList.toggle('dark-mode');
7086
const isDarkMode = body.classList.contains('dark-mode');
@@ -74,8 +90,28 @@ document.addEventListener('DOMContentLoaded', function() {
7490
if(settingsIcon){
7591
settingsIcon.src = isDarkMode ? 'icons/settings-night.png' : 'icons/settings-light.png';
7692
}
93+
renderTokenPreview();
7794
});
7895

96+
function renderTokenPreview() {
97+
tokenPreview.innerHTML = '';
98+
const value = githubTokenInput.value;
99+
const isDark = document.body.classList.contains('dark-mode');
100+
for (let i = 0; i < value.length; i++) {
101+
const charBox = document.createElement('span');
102+
charBox.className = 'token-preview-char' + (isDark ? ' dark-mode' : '');
103+
if (tokenVisible) {
104+
charBox.textContent = value[i];
105+
} else {
106+
const dot = document.createElement('span');
107+
dot.className = 'token-preview-dot' + (isDark ? ' dark-mode' : '');
108+
charBox.appendChild(dot);
109+
}
110+
tokenPreview.appendChild(charBox);
111+
setTimeout(() => charBox.classList.add('flip'), 10 + i * 30);
112+
}
113+
}
114+
79115
function updateContentState(enableToggle) {
80116
const elementsToToggle = [
81117
'startingDate',
@@ -87,6 +123,7 @@ document.addEventListener('DOMContentLoaded', function() {
87123
'showOpenLabel',
88124
'scrumReport',
89125
'githubUsername',
126+
'githubToken',
90127
'projectName',
91128
'settingsToggle',
92129
];
@@ -305,6 +342,46 @@ document.addEventListener('DOMContentLoaded', function() {
305342

306343
});
307344

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

0 commit comments

Comments
 (0)