diff --git a/src/manifests/chrome.json b/src/manifests/chrome.json index a342f257..e70ddbaa 100644 --- a/src/manifests/chrome.json +++ b/src/manifests/chrome.json @@ -32,6 +32,7 @@ "scripts/jquery-3.2.1.min.js", "scripts/emailClientAdapter.js", "scripts/gitlabHelper.js", + "scripts/utils.js", "scripts/scrumHelper.js" ] } diff --git a/src/manifests/firefox.json b/src/manifests/firefox.json index 9972205c..0307a4e1 100644 --- a/src/manifests/firefox.json +++ b/src/manifests/firefox.json @@ -32,6 +32,7 @@ "scripts/jquery-3.2.1.min.js", "scripts/emailClientAdapter.js", "scripts/gitlabHelper.js", + "scripts/utils.js", "scripts/scrumHelper.js" ] } diff --git a/src/popup.html b/src/popup.html index ed324e5c..5582e652 100644 --- a/src/popup.html +++ b/src/popup.html @@ -615,6 +615,7 @@

Note:

+ diff --git a/src/scripts/main.js b/src/scripts/main.js index 7a8e2f5c..fe83206d 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -118,8 +118,8 @@ function handleYesterdayContributionChange() { if (value) { startingDateElement.readOnly = true; endingDateElement.readOnly = true; - endingDateElement.value = getToday(); - startingDateElement.value = getYesterday(); + endingDateElement.value = window.scrumUtils.getTodayDateString(); + startingDateElement.value = window.scrumUtils.getYesterdayDateString(); handleEndingDateChange(); handleStartingDateChange(); labelElement.classList.add('selectedLabel'); @@ -132,18 +132,6 @@ function handleYesterdayContributionChange() { } browser.storage.local.set({ yesterdayContribution: value }); } - -function getYesterday() { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - return yesterday.toISOString().split('T')[0]; -} -function getToday() { - const today = new Date(); - return today.toISOString().split('T')[0]; -} - function handlePlatformUsernameChange() { const value = platformUsernameElement.value; browser.storage.local.get(['platform']).then((result) => { diff --git a/src/scripts/popup.js b/src/scripts/popup.js index 82f28091..f1041845 100644 --- a/src/scripts/popup.js +++ b/src/scripts/popup.js @@ -78,18 +78,6 @@ function setupButtonTooltips() { } } -function getToday() { - const today = new Date(); - return today.toISOString().split('T')[0]; -} - -function getYesterday() { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - return yesterday.toISOString().split('T')[0]; -} - function applyI18n() { document.querySelectorAll('[data-i18n]').forEach((el) => { const key = el.getAttribute('data-i18n'); @@ -344,12 +332,8 @@ document.addEventListener('DOMContentLoaded', () => { } githubTokenInput.addEventListener('input', checkTokenForFilter); - githubTokenInput.addEventListener('input', () => - checkTokenForShowCommits({ persistState: false }), - ); - githubTokenInput.addEventListener('input', () => - checkTokenForMergedPRs({ persistState: false }), - ); + githubTokenInput.addEventListener('input', () => checkTokenForShowCommits({ persistState: false })); + githubTokenInput.addEventListener('input', () => checkTokenForMergedPRs({ persistState: false })); darkModeToggle.addEventListener('click', function () { body.classList.toggle('dark-mode'); @@ -501,34 +485,37 @@ document.addEventListener('DOMContentLoaded', () => { 'lastScrumReportUsername', 'githubUsername', 'gitlabUsername', - 'platformUsername' + 'platformUsername', ]); let lastScrumReportHtml = storageValues[`${activePlatform}LastScrumReportHtml`]; let lastScrumReportCacheKey = storageValues[`${activePlatform}LastScrumReportCacheKey`]; let lastScrumReportUsername = storageValues[`${activePlatform}LastScrumReportUsername`]; - if (storageValues.lastScrumReportHtml && (!storageValues.lastScrumReportPlatform || storageValues.lastScrumReportPlatform === activePlatform) && !lastScrumReportHtml) { + if ( + storageValues.lastScrumReportHtml && + (!storageValues.lastScrumReportPlatform || storageValues.lastScrumReportPlatform === activePlatform) && + !lastScrumReportHtml + ) { lastScrumReportHtml = storageValues.lastScrumReportHtml; lastScrumReportCacheKey = storageValues.lastScrumReportCacheKey; lastScrumReportUsername = storageValues.lastScrumReportUsername; } - const expectedUsername = activePlatform === 'gitlab' - ? (storageValues.gitlabUsername || storageValues.platformUsername) - : (storageValues.githubUsername || storageValues.platformUsername); + const expectedUsername = + activePlatform === 'gitlab' + ? storageValues.gitlabUsername || storageValues.platformUsername + : storageValues.githubUsername || storageValues.platformUsername; - const isUsernameMatch = lastScrumReportUsername + const isUsernameMatch = lastScrumReportUsername ? lastScrumReportUsername === expectedUsername - : (lastScrumReportCacheKey && expectedUsername && lastScrumReportCacheKey.startsWith(expectedUsername + '-')); + : lastScrumReportCacheKey && expectedUsername && lastScrumReportCacheKey.startsWith(expectedUsername + '-'); if (age < ttlMs) { const cacheKey = cache?.cacheKey ?? null; const reportEmpty = !scrumReport.innerHTML || !scrumReport.innerHTML.trim(); - const matches = - (!lastScrumReportCacheKey || lastScrumReportCacheKey === cacheKey) && - isUsernameMatch; + const matches = (!lastScrumReportCacheKey || lastScrumReportCacheKey === cacheKey) && isUsernameMatch; if (reportEmpty && lastScrumReportHtml && matches) { scrumReport.innerHTML = lastScrumReportHtml; @@ -658,8 +645,7 @@ document.addEventListener('DOMContentLoaded', () => { platformUsername.value = result[platformUsernameKey] || ''; checkTokenForShowCommits(); checkTokenForMergedPRs(); - }, - ); + }); // Button setup const generateBtn = document.getElementById('generateReport'); @@ -822,8 +808,8 @@ document.addEventListener('DOMContentLoaded', () => { const endDateInput = document.getElementById('endingDate'); if (items.selectedTimeframe === 'yesterdayContribution') { - startDateInput.value = getYesterday(); - endDateInput.value = getToday(); + startDateInput.value = window.scrumUtils.getYesterdayDateString(); + endDateInput.value = window.scrumUtils.getTodayDateString(); } startDateInput.readOnly = endDateInput.readOnly = true; @@ -983,7 +969,9 @@ document.addEventListener('DOMContentLoaded', () => { // Show notice instead of applying immediately const modeLabel = mode === 'popup' ? 'Popup' : 'Side Panel'; if (displayModeNotice && displayModeNoticeText) { - displayModeNoticeText.textContent = chrome?.i18n.getMessage('displayModeNotice', [modeLabel]) || `The extension will open in ${modeLabel} mode on the next launch.`; + displayModeNoticeText.textContent = + chrome?.i18n.getMessage('displayModeNotice', [modeLabel]) || + `The extension will open in ${modeLabel} mode on the next launch.`; displayModeNotice.classList.remove('hidden'); } }); @@ -1083,7 +1071,9 @@ document.addEventListener('DOMContentLoaded', () => { } catch {} if (platform !== 'github') { // Do not run repo fetch for non-GitHub platforms - if (repoStatus) repoStatus.textContent = chrome?.i18n.getMessage('repoFilteringGithubOnly') || 'Repository filtering is only available for GitHub.'; + if (repoStatus) + repoStatus.textContent = + chrome?.i18n.getMessage('repoFilteringGithubOnly') || 'Repository filtering is only available for GitHub.'; return; } if (!useRepoFilter.checked) { @@ -1172,7 +1162,10 @@ document.addEventListener('DOMContentLoaded', () => { if (platform !== 'github') { repoFilterContainer.classList.add('hidden'); useRepoFilter.checked = false; - if (repoStatus) repoStatus.textContent = chrome?.i18n.getMessage('repoFilteringGithubOnly') || 'Repository filtering is only available for GitHub.'; + if (repoStatus) + repoStatus.textContent = + chrome?.i18n.getMessage('repoFilteringGithubOnly') || + 'Repository filtering is only available for GitHub.'; return; } const enabled = useRepoFilter.checked; @@ -1202,7 +1195,8 @@ document.addEventListener('DOMContentLoaded', () => { }); checkTokenForFilter(); if (enabled) { - repoStatus.textContent = chrome?.i18n.getMessage('loadingReposAutomatically') || 'Loading repos automatically...'; + repoStatus.textContent = + chrome?.i18n.getMessage('loadingReposAutomatically') || 'Loading repos automatically...'; try { const cacheData = await browser.storage.local.get(['repoCache']); @@ -1351,7 +1345,9 @@ document.addEventListener('DOMContentLoaded', () => { platform = items.platform || 'github'; } catch {} if (platform !== 'github') { - if (repoStatus) repoStatus.textContent = chrome?.i18n.getMessage('repoLoadingGithubOnly') || 'Repository loading is only available for GitHub.'; + if (repoStatus) + repoStatus.textContent = + chrome?.i18n.getMessage('repoLoadingGithubOnly') || 'Repository loading is only available for GitHub.'; return; } console.log('window.fetchUserRepositories exists:', !!window.fetchUserRepositories); @@ -1391,7 +1387,9 @@ document.addEventListener('DOMContentLoaded', () => { platform = items.platform || 'github'; } catch (e) {} if (platform !== 'github') { - if (repoStatus) repoStatus.textContent = chrome?.i18n.getMessage('repoFetchingGithubOnly') || 'Repository fetching is only available for GitHub.'; + if (repoStatus) + repoStatus.textContent = + chrome?.i18n.getMessage('repoFetchingGithubOnly') || 'Repository fetching is only available for GitHub.'; return; } console.log('[POPUP-DEBUG] performRepoFetch called.'); @@ -1694,11 +1692,11 @@ platformSelect.addEventListener('change', () => { const platform = platformSelect.value; browser.storage.local.set({ platform }).then(() => { const scrumReport = document.getElementById('scrumReport'); - if(scrumReport){ + if (scrumReport) { scrumReport.innerHTML = ''; } const generateBtn = document.getElementById('generateReport'); - if(typeof bootstrapScrumReportOnPopupLoad === 'function'){ + if (typeof bootstrapScrumReportOnPopupLoad === 'function') { bootstrapScrumReportOnPopupLoad(generateBtn); } }); @@ -1728,11 +1726,7 @@ const platformSelectHidden = document.getElementById('platformSelect'); function buildScrumSubjectFromPopup() { const projectName = document.getElementById('projectName')?.value?.trim() || ''; - const now = new Date(); - const dateCode = - String(now.getFullYear()) + String(now.getMonth() + 1).padStart(2, '0') + String(now.getDate()).padStart(2, '0'); - - return `[Scrum]${projectName ? ' - ' + projectName : ''} - ${dateCode}`; + return window.scrumUtils.buildScrumSubject(projectName); } function setPlatformDropdown(value) { @@ -1754,10 +1748,10 @@ function setPlatformDropdown(value) { platformSelectHidden.value = value; browser.storage.local.set({ platform: value }).then(() => { const scrumReport = document.getElementById('scrumReport'); - if(scrumReport) scrumReport.innerHTML = ''; + if (scrumReport) scrumReport.innerHTML = ''; const generateBtn = document.getElementById('generateReport'); - if(typeof bootstrapScrumReportOnPopupLoad === 'function'){ + if (typeof bootstrapScrumReportOnPopupLoad === 'function') { bootstrapScrumReportOnPopupLoad(generateBtn); } }); @@ -2009,8 +2003,8 @@ function toggleRadio(radio) { console.log('Toggling radio:', radio.id); if (radio.id === 'yesterdayContribution') { - startDateInput.value = getYesterday(); - endDateInput.value = getToday(); + startDateInput.value = window.scrumUtils.getYesterdayDateString(); + endDateInput.value = window.scrumUtils.getTodayDateString(); } startDateInput.readOnly = endDateInput.readOnly = true; diff --git a/src/scripts/scrumHelper.js b/src/scripts/scrumHelper.js index c5fedf2b..7e735555 100644 --- a/src/scripts/scrumHelper.js +++ b/src/scripts/scrumHelper.js @@ -255,28 +255,13 @@ function allIncluded(outputTarget = 'email') { endingDate, gitlabToken, ); + const gitlabApiBase = gitlabHelper?.baseUrl; - function mapGitLabItem(item, projects, type) { - const project = projects.find((p) => p.id === item.project_id); - const repoName = project ? project.name : 'unknown'; - - return { - ...item, - repository_url: `https://gitlab.com/api/v4/projects/${item.project_id}`, - html_url: - type === 'issue' - ? item.web_url || (project ? `${project.web_url}/-/issues/${item.iid}` : '') - : item.web_url || (project ? `${project.web_url}/-/merge_requests/${item.iid}` : ''), - number: item.iid, - title: item.title, - state: type === 'issue' && item.state === 'opened' ? 'open' : item.state, - project: repoName, - pull_request: type === 'mr', - }; - } - const mappedIssues = (data.issues || []).map((issue) => mapGitLabItem(issue, data.projects, 'issue')); + const mappedIssues = (data.issues || []).map((issue) => + window.scrumUtils.mapGitLabItem(issue, data.projects, 'issue', gitlabApiBase), + ); const mappedMRs = (data.mergeRequests || data.mrs || []).map((mr) => - mapGitLabItem(mr, data.projects, 'mr'), + window.scrumUtils.mapGitLabItem(mr, data.projects, 'mr', gitlabApiBase), ); const mappedData = { githubIssuesData: { items: mappedIssues }, @@ -285,18 +270,8 @@ function allIncluded(outputTarget = 'email') { }; githubUserData = mappedData.githubUserData; - const name = - githubUserData?.name || githubUserData?.username || platformUsernameLocal || platformUsername; const project = projectName; - const curDate = new Date(); - const year = curDate.getFullYear().toString(); - let date = curDate.getDate(); - let month = curDate.getMonth() + 1; - if (month < 10) month = '0' + month; - if (date < 10) date = '0' + date; - const dateCode = year.toString() + month.toString() + date.toString(); - const subject = `[Scrum]${project ? ' - ' + project : ''} - ${dateCode}`; - subjectForEmail = subject; + subjectForEmail = window.scrumUtils.buildScrumSubject(project); await processGithubData(mappedData, true, subjectForEmail); scrumGenerationInProgress = false; @@ -327,26 +302,12 @@ function allIncluded(outputTarget = 'email') { gitlabHelper .fetchGitLabData(platformUsernameLocal, startingDate, endingDate, gitlabToken) .then((data) => { - function mapGitLabItem(item, projects, type) { - const project = projects.find((p) => p.id === item.project_id); - const repoName = project ? project.name : 'unknown'; - return { - ...item, - repository_url: `https://gitlab.com/api/v4/projects/${item.project_id}`, - html_url: - type === 'issue' - ? item.web_url || (project ? `${project.web_url}/-/issues/${item.iid}` : '') - : item.web_url || (project ? `${project.web_url}/-/merge_requests/${item.iid}` : ''), - number: item.iid, - title: item.title, - state: type === 'issue' && item.state === 'opened' ? 'open' : item.state, - project: repoName, - pull_request: type === 'mr', - }; - } - const mappedIssues = (data.issues || []).map((issue) => mapGitLabItem(issue, data.projects, 'issue')); + const gitlabApiBase = gitlabHelper?.baseUrl; + const mappedIssues = (data.issues || []).map((issue) => + window.scrumUtils.mapGitLabItem(issue, data.projects, 'issue', gitlabApiBase), + ); const mappedMRs = (data.mergeRequests || data.mrs || []).map((mr) => - mapGitLabItem(mr, data.projects, 'mr'), + window.scrumUtils.mapGitLabItem(mr, data.projects, 'mr', gitlabApiBase), ); const mappedData = { githubIssuesData: { items: mappedIssues }, @@ -413,19 +374,8 @@ function allIncluded(outputTarget = 'email') { getChromeData(); function handleYesterdayContributionChange() { - endingDate = getToday(); - startingDate = getYesterday(); - } - - function getYesterday() { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); - return yesterday.toISOString().split('T')[0]; - } - function getToday() { - const today = new Date(); - return today.toISOString().split('T')[0]; + endingDate = window.scrumUtils.getTodayDateString(); + startingDate = window.scrumUtils.getYesterdayDateString(); } // Global cache object @@ -1258,18 +1208,8 @@ ${blockerText}`; return; } setTimeout(() => { - const name = githubUserData?.name || githubUserData?.username || platformUsernameLocal || platformUsername; const project = projectName; - const curDate = new Date(); - const year = curDate.getFullYear().toString(); - let date = curDate.getDate(); - let month = curDate.getMonth(); - month++; - if (month < 10) month = '0' + month; - if (date < 10) date = '0' + date; - const dateCode = year.toString() + month.toString() + date.toString(); - - const subject = `[Scrum]${project ? ' - ' + project : ''} - ${dateCode}`; + const subject = window.scrumUtils.buildScrumSubject(project); log('Generated subject:', subject); githubCache.subject = subject; saveToStorage(githubCache.data, subject); diff --git a/src/scripts/utils.js b/src/scripts/utils.js new file mode 100644 index 00000000..0aa0fc3e --- /dev/null +++ b/src/scripts/utils.js @@ -0,0 +1,75 @@ +(function (globalScope) { + function formatDateToIsoDay(date) { + return date.toISOString().split('T')[0]; + } + + function getTodayDateString(now = new Date()) { + return formatDateToIsoDay(now); + } + + function getYesterdayDateString(now = new Date()) { + const yesterday = new Date(now); + yesterday.setUTCDate(now.getUTCDate() - 1); + return formatDateToIsoDay(yesterday); + } + + function getScrumDateCode(now = new Date()) { + const year = String(now.getUTCFullYear()); + const month = String(now.getUTCMonth() + 1).padStart(2, '0'); + const day = String(now.getUTCDate()).padStart(2, '0'); + return `${year}${month}${day}`; + } + + function buildScrumSubject(projectName = '', now = new Date()) { + const projectPart = projectName ? ` - ${projectName}` : ''; + return `[Scrum]${projectPart} - ${getScrumDateCode(now)}`; + } + + function normalizeGitLabState(state, type) { + if (type === 'issue') { + return state === 'opened' ? 'open' : state; + } + + if (type === 'mr') { + if (state === 'opened') { + return 'open'; + } + if (state === 'merged' || state === 'closed') { + return 'closed'; + } + } + + return state; + } + + function mapGitLabItem(item, projects = [], type, apiBaseUrl = 'https://gitlab.com/api/v4') { + const project = projects.find((projectItem) => projectItem.id === item.project_id); + const repoName = project ? project.name : 'unknown'; + const normalizedApiBase = apiBaseUrl.replace(/\/+$/, ''); + const normalizedState = normalizeGitLabState(item.state, type); + const pullRequestData = type === 'mr' ? { merged_at: item.merged_at || null } : false; + + return { + ...item, + repository_url: `${normalizedApiBase}/projects/${item.project_id}`, + html_url: + type === 'issue' + ? item.web_url || (project ? `${project.web_url}/-/issues/${item.iid}` : '') + : item.web_url || (project ? `${project.web_url}/-/merge_requests/${item.iid}` : ''), + number: item.iid, + title: item.title, + state: normalizedState, + project: repoName, + pull_request: pullRequestData, + }; + } + + globalScope.scrumUtils = { + ...(globalScope.scrumUtils || {}), + getTodayDateString, + getYesterdayDateString, + getScrumDateCode, + buildScrumSubject, + mapGitLabItem, + }; +})(typeof window !== 'undefined' ? window : globalThis);