From 81c235fc50299abed25c8d7998adff0cf486bd53 Mon Sep 17 00:00:00 2001 From: Vedansh Saini <77830698+vedansh-5@users.noreply.github.com> Date: Sat, 31 May 2025 17:08:29 +0530 Subject: [PATCH 01/13] Added release drafter configuration and workflow Signed-off-by: Vedansh Saini <77830698+vedansh-5@users.noreply.github.com> --- .github/release-drafter.yml | 31 ++++++++++++++++++++ .github/workflows/release-drafter.yml | 42 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..fe1351d --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,31 @@ +name-template: 'v$RESOLVED_VERSION 🌈' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +change-template: '- $TITLE @$AUTHOR (#$NUMBER)' +change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +template: | + ## Changes + + $CHANGES \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..d84c395 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,42 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + # pull_request_target: + # types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + #- name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v6.1.0 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From dac1f9497289f3054196781aac01db663aabac1f Mon Sep 17 00:00:00 2001 From: Vedansh Saini <77830698+vedansh-5@users.noreply.github.com> Date: Sat, 31 May 2025 17:23:31 +0530 Subject: [PATCH 02/13] label -> labels Signed-off-by: Vedansh Saini <77830698+vedansh-5@users.noreply.github.com> --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index fe1351d..75c57c9 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -11,7 +11,7 @@ categories: - 'bugfix' - 'bug' - title: '🧰 Maintenance' - label: 'chore' + labels: 'chore' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. version-resolver: From cd30226c8ebff502d058d22314f1305947ced8b3 Mon Sep 17 00:00:00 2001 From: Preeti9764 Date: Mon, 2 Jun 2025 16:42:09 +0530 Subject: [PATCH 03/13] custumization and template saving --- src/index.css | 179 ++++++++++-- src/manifest.json | 19 +- src/popup.html | 191 +++++++----- src/scripts/emailClientAdapter.js | 12 +- src/scripts/main.js | 40 +-- src/scripts/popup.js | 33 ++- src/scripts/scrumHelper.js | 467 ++++++++++++++++++------------ src/scripts/settings.js | 115 ++++++++ 8 files changed, 730 insertions(+), 326 deletions(-) create mode 100644 src/scripts/settings.js diff --git a/src/index.css b/src/index.css index 77bed34..76a64af 100644 --- a/src/index.css +++ b/src/index.css @@ -7,82 +7,205 @@ body { * { box-sizing: border-box; } -.datepicker { - margin-bottom: 5px !important; -} -.tabs .tab a:hover, -.tabs .tab a.active { - background-color: transparent; - color: #3f51b5; + +/* Tab Content Styles */ +.tab-content { + padding: 0; } + +/* Tabs styling */ .tabs { background-color: transparent; + margin-bottom: 5px; } + .tabs .tab a { color: #3f51b5; + font-weight: 500; } + +.tabs .tab a:hover, +.tabs .tab a.active { + background-color: transparent; + color: #3f51b5; +} + .tabs .indicator { background-color: #3f51b5; + height: 3px; } -.switch label input[type="checkbox"]:checked + .lever { + +/* Settings Styles */ +#settingsBox { + padding: 20px 0; +} + +#settingsBox h5 { + margin-top: 10px; + margin-bottom: 10px; + padding-bottom: 10px; + border-bottom: 1px solid #e0e0e0; +} + +.template-list { + margin-top: 15px; + max-height: 200px; + overflow-y: auto; + padding: 10px; + background-color: #f5f5f5; + border-radius: 4px; +} + +.template-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; + margin: 5px 0; + background-color: white; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.template-item button { + margin-left: 8px; +} + +.template-actions { + display: flex; + gap: 8px; +} + +/* Form Elements */ +.datepicker { + margin-bottom: 5px !important; +} + +.switch label input[type="checkbox"]:checked+.lever { background-color: #3f51b5; } -.switch label input[type="checkbox"]:checked + .lever:after { + +.switch label input[type="checkbox"]:checked+.lever:after { background-color: #fcfcfc; left: 24px; } -[type="checkbox"].filled-in:checked + label:after { + +[type="checkbox"].filled-in:checked+label:after { border: 2px solid #3f51b5; background-color: #3f51b5; } + +/* Buttons */ +.btn, +.btn-large { + background-color: #3f51b5; +} + .btn:hover, .btn-large:hover { background-color: #3f51b5; + opacity: 0.9; } + +/* Links */ a { color: #3f51b5; } -.btn, -.btn-large { - background-color: #3f51b5; -} + +/* Lists */ li { list-style-type: disc !important; margin-left: 1rem; } + +/* Labels */ .selectedLabel { font-weight: 400; color: #333333; } .unselectedLabel { - font-weight: 300; - color: #666666; + font-weight: 300; + color: #666666; } -.selectedLabel, .unselectedLabel { +.selectedLabel, +.unselectedLabel { transition: color 0.3s ease-in-out, font-weight 0.3s ease-in-out; } +/* Scrum Report */ #scrumReport { - border: 1px solid #ccc; - padding: 10px; - min-height: 200px; - max-height: 400px; - overflow-y: auto; - background-color: white; + border: 1px solid #ccc; + padding: 10px; + min-height: 200px; + max-height: 400px; + overflow-y: auto; + background-color: white; + margin-bottom: 15px; + border-radius: 4px; } #scrumReport:focus { - outline: none; - border-color: #26a69a; + outline: none; + border-color: #26a69a; } #scrumReport a { - color: #26a69a; - text-decoration: none; + color: #26a69a; + text-decoration: none; } #scrumReport a:hover { - text-decoration: underline; + text-decoration: underline; +} + +/* Tab content visibility */ +#mainContentBox { + display: block; +} + +#settingsBox { + display: none; +} + +/* When settings tab is active */ +.tabs .tab a.active[href="#settingsBox"]~#mainContentBox { + display: none; +} + +.tabs .tab a.active[href="#settingsBox"]~#settingsBox { + display: block; +} + +/* Settings specific styles */ +#settingsBox { + padding: 20px; +} + +.section-box { + display: none; } + +.section-box.active { + display: block; +} + +/* Make checkboxes more visible */ +[type="checkbox"]:checked+label:before { + border-right: 2px solid #26a69a; + border-bottom: 2px solid #26a69a; +} + +/* Make the settings tab content scrollable independently */ +#settingsBox .row { + max-height: calc(100vh - 200px); + overflow-y: auto; +} + +/* Ensure proper spacing in settings */ +#settingsBox .col.s12 { + margin-bottom: 20px; +} + +/* Fixed button container styles */ \ No newline at end of file diff --git a/src/manifest.json b/src/manifest.json index 32710b4..577725e 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -17,17 +17,20 @@ "content_scripts": [ { "matches": [ - "*://groups.google.com/forum/*", + "*://groups.google.com/forum/*", "*://groups.google.com/g/*", - "*://mail.google.com/*", - "*://outlook.live.com/*", - "*://outlook.office.com/*", - "*://mail.yahoo.com/*" + "*://mail.google.com/*", + "*://outlook.live.com/*", + "*://outlook.office.com/*", + "*://mail.yahoo.com/*" ], - "js": ["scripts/jquery-3.2.1.min.js", "scripts/scrumHelper.js", "scripts/emailClientAdapter.js"] + "js": [ + "scripts/jquery-3.2.1.min.js", + "scripts/scrumHelper.js", + "scripts/emailClientAdapter.js" + ] } ], - "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self';" }, @@ -45,4 +48,4 @@ "*://*.yahoo.com/*", "https://api.github.com/*" ] -} +} \ No newline at end of file diff --git a/src/popup.html b/src/popup.html index 7a924f2..d28d5dd 100644 --- a/src/popup.html +++ b/src/popup.html @@ -1,5 +1,6 @@ + @@ -8,21 +9,22 @@ + html, + body { + width: 350px !important; + height: 600px !important; + max-height: 600px !important; + overflow-y: scroll; + } + +

SCRUM Helper

SCRUM helper prefills the Scrums in google groups, with your FOSSASIA's contributions.
-
-
+ + + -
-
- - -
-
- - -
-
Fetch your contributions between: - - -
-
-
Starting Date:
- - -
-
-
Ending Date
- -
-
-
- - -
-
- - -
-
-
Scrum Report
-
-
-
-
- - - + +
+
+
+ + +
+
+ + +
+
+ Fetch your contributions between: + + +
+ +
+
Starting Date:
+ +
+
+
Ending Date
+ +
+
+ + +
+
+
Scrum Report
+
+
+
+
+ + +
-
- -
Note:
-
-
    -
  • The PRs fetched are according to the date last reviewed by anyone. So if you reviewed a PR 10 days back, and someone reviewed it 2 days back, it will appear in your last week's activity. See this issue. -
  • -
  • By using the extension you understand that there might be discrepancies in the SCRUM generated. You are advised to edit the SCRUM afterwards to remove any discrepancies. -
  • -
-
+
+ + +
+ + - + + \ No newline at end of file diff --git a/src/scripts/emailClientAdapter.js b/src/scripts/emailClientAdapter.js index 2b24f71..7338cfc 100644 --- a/src/scripts/emailClientAdapter.js +++ b/src/scripts/emailClientAdapter.js @@ -41,12 +41,12 @@ class EmailClientAdapter { '[data-test-id*="compose"][contenteditable="true"]', '.compose-editor [contenteditable="true"]' ].join(', '), - subject: [ - '#compose-subject-input', - 'input[placeholder="Subject"]', - 'input[aria-label*="subject" i]', - 'input[data-test-id*="subject" i]' - ].join(', ') + subject: [ + '#compose-subject-input', + 'input[placeholder="Subject"]', + 'input[aria-label*="subject" i]', + 'input[data-test-id*="subject" i]' + ].join(', ') }, eventTypes: { contentChange: 'input', diff --git a/src/scripts/main.js b/src/scripts/main.js index 590d579..6e603cc 100644 --- a/src/scripts/main.js +++ b/src/scripts/main.js @@ -68,13 +68,13 @@ function handleBodyOnLoad() { } if (items.selectedTab === 'gsoc') { handleGsocClick(); - } + } else { handleCodeheatClick(); } - + // initialize materialize tabs - $('.tabs').tabs('select_tab', items.selectedTab === 'gsoc' ? 'gsocBox' : 'codeheatBox' ); + $('.tabs').tabs('select_tab', items.selectedTab === 'gsoc' ? 'gsocBox' : 'codeheatBox'); }, ); } @@ -95,21 +95,21 @@ function handleLastWeekContributionChange() { var labelElement = document.querySelector("label[for='lastWeekContribution']"); if (value) { - startingDateElement.disabled = true; - endingDateElement.disabled = true; - endingDateElement.value = getToday(); - startingDateElement.value = getLastWeek(); - handleEndingDateChange(); - handleStartingDateChange(); - labelElement.classList.add("selectedLabel"); - labelElement.classList.remove("unselectedLabel"); + startingDateElement.disabled = true; + endingDateElement.disabled = true; + endingDateElement.value = getToday(); + startingDateElement.value = getLastWeek(); + handleEndingDateChange(); + handleStartingDateChange(); + labelElement.classList.add("selectedLabel"); + labelElement.classList.remove("unselectedLabel"); } else { - startingDateElement.disabled = false; - endingDateElement.disabled = false; - labelElement.classList.add("unselectedLabel"); - labelElement.classList.remove("selectedLabel"); + startingDateElement.disabled = false; + endingDateElement.disabled = false; + labelElement.classList.add("unselectedLabel"); + labelElement.classList.remove("selectedLabel"); } - + chrome.storage.local.set({ lastWeekContribution: value }); } @@ -156,11 +156,11 @@ function handleOpenLabelChange() { var labelElement = document.querySelector("label[for='showOpenLabel']"); if (value) { - labelElement.classList.add("selectedLabel"); - labelElement.classList.remove("unselectedLabel"); + labelElement.classList.add("selectedLabel"); + labelElement.classList.remove("unselectedLabel"); } else { - labelElement.classList.add("unselectedLabel"); - labelElement.classList.remove("selectedLabel"); + labelElement.classList.add("unselectedLabel"); + labelElement.classList.remove("selectedLabel"); } chrome.storage.local.set({ showOpenLabel: value }); diff --git a/src/scripts/popup.js b/src/scripts/popup.js index a9c2b4e..68f27a7 100644 --- a/src/scripts/popup.js +++ b/src/scripts/popup.js @@ -1,31 +1,46 @@ -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', function () { const generateBtn = document.getElementById('generateReport'); const copyBtn = document.getElementById('copyReport'); - - generateBtn.addEventListener('click', function() { + + generateBtn.addEventListener('click', function (e) { + // Prevent default button behavior + e.preventDefault(); + e.stopPropagation(); + + // Disable button and show loading state this.innerHTML = ' Generating...'; this.disabled = true; - + + // Generate report window.generateScrumReport(); + + // Reset button state after a short delay + setTimeout(() => { + this.innerHTML = ' Generate Report'; + this.disabled = false; + + // Ensure the window stops scrolling + window.scrollTo(0, window.scrollY); + }, 1000); }); - - copyBtn.addEventListener('click', function() { + + copyBtn.addEventListener('click', function () { const scrumReport = document.getElementById('scrumReport'); - + // Create container for HTML content const tempDiv = document.createElement('div'); tempDiv.innerHTML = scrumReport.innerHTML; document.body.appendChild(tempDiv); tempDiv.style.position = 'absolute'; tempDiv.style.left = '-9999px'; - + // Select the content const range = document.createRange(); range.selectNode(tempDiv); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); - + try { // Copy HTML content const success = document.execCommand('copy'); diff --git a/src/scripts/scrumHelper.js b/src/scripts/scrumHelper.js index 2f8ade3..84ae294 100644 --- a/src/scripts/scrumHelper.js +++ b/src/scripts/scrumHelper.js @@ -1,7 +1,89 @@ var enableToggle = true; + +// Settings and template management +const DEFAULT_SETTINGS = { + sections: { + tasks: true, + prs: true, + reviewed: true, + blockers: true + }, + filters: { + openOnly: false, + excludeDrafts: false + } +}; + +// Load settings from storage +function loadSettings() { + return new Promise((resolve) => { + chrome.storage.local.get(['scrumSettings', 'scrumTemplates'], (result) => { + const settings = result.scrumSettings || DEFAULT_SETTINGS; + const templates = result.scrumTemplates || {}; + resolve({ settings, templates }); + }); + }); +} + +// Save settings to storage +function saveSettings(settings) { + return new Promise((resolve) => { + chrome.storage.local.set({ scrumSettings: settings }, resolve); + }); +} + +// Save template +function saveTemplate(name, settings) { + return new Promise((resolve) => { + chrome.storage.local.get(['scrumTemplates'], (result) => { + const templates = result.scrumTemplates || {}; + templates[name] = settings; + chrome.storage.local.set({ scrumTemplates: templates }, resolve); + }); + }); +} + +// Load template +function loadTemplate(name) { + return new Promise((resolve) => { + chrome.storage.local.get(['scrumTemplates'], (result) => { + const templates = result.scrumTemplates || {}; + resolve(templates[name]); + }); + }); +} + +// Delete template +function deleteTemplate(name) { + return new Promise((resolve) => { + chrome.storage.local.get(['scrumTemplates'], (result) => { + const templates = result.scrumTemplates || {}; + delete templates[name]; + chrome.storage.local.set({ scrumTemplates: templates }, resolve); + }); + }); +} + +// Filter GitHub data based on settings +function filterGithubData(data, settings) { + if (!data || !data.items) return data; + + let filteredItems = data.items; + + if (settings.filters.openOnly) { + filteredItems = filteredItems.filter(item => item.state === 'open'); + } + + if (settings.filters.excludeDrafts) { + filteredItems = filteredItems.filter(item => !item.draft); + } + + return { ...data, items: filteredItems }; +} + function allIncluded(outputTarget = 'email') { console.log('allIncluded called with outputTarget:', outputTarget); - console.log('Current window context:', window.location.href); + console.log('Current window context:', window.location.href); /* global $*/ var scrumBody = null; var scrumSubject = null; @@ -71,23 +153,23 @@ function allIncluded(outputTarget = 'email') { } if (items.githubUsername) { githubUsername = items.githubUsername; - console.log("About to fetch GitHub data for:", githubUsername); + console.log("About to fetch GitHub data for:", githubUsername); fetchGithubData(); - } else { - if (outputTarget === 'popup') { - console.log("No username found - popup context"); - // Show error in popup - const generateBtn = document.getElementById('generateReport'); - if (generateBtn) { - generateBtn.innerHTML = ' Generate Report'; - generateBtn.disabled = false; - } - Materialize.toast('Please enter your GitHub username', 3000); - } else { - console.log("No username found - email context"); - console.warn('No GitHub username found in storage'); - } - } + } else { + if (outputTarget === 'popup') { + console.log("No username found - popup context"); + // Show error in popup + const generateBtn = document.getElementById('generateReport'); + if (generateBtn) { + generateBtn.innerHTML = ' Generate Report'; + generateBtn.disabled = false; + } + Materialize.toast('Please enter your GitHub username', 3000); + } else { + console.log("No username found - email context"); + console.warn('No GitHub username found in storage'); + } + } if (items.projectName) { projectName = items.projectName; } @@ -223,61 +305,59 @@ function allIncluded(outputTarget = 'email') { function writeScrumBody() { if (!enableToggle) return; - setTimeout(() => { - // Generate content first - var lastWeekUl = '
    '; - var i; - for (i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i]; - for (i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i]; - lastWeekUl += '
'; - - var nextWeekUl = '
    '; - for (i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i]; - nextWeekUl += '
'; - - var weekOrDay = gsoc == 1 ? 'yesterday' : 'last week'; - var weekOrDay2 = gsoc == 1 ? 'today' : 'this week'; - - // Create the complete content - let content; - if (lastWeekContribution == true) { - content = `1. What did I do ${weekOrDay}?
-${lastWeekUl}
-2. What I plan to do ${weekOrDay2}?
-${nextWeekUl}
-3. What is stopping me from doing my work?
-${userReason}`; - } else { - content = `1. What did I do from ${formatDate(startingDate)} to ${formatDate(endingDate)}?
-${lastWeekUl}
-2. What I plan to do ${weekOrDay2}?
-${nextWeekUl}
-3. What is stopping me from doing my work?
-${userReason}`; - } - - if (outputTarget === 'popup') { - const scrumReport = document.getElementById('scrumReport'); - if (scrumReport) { - console.log("found div, updating content"); - scrumReport.innerHTML = content; - - // Reset generate button - const generateBtn = document.getElementById('generateReport'); - if (generateBtn) { - generateBtn.innerHTML = ' Generate Report'; - generateBtn.disabled = false; - } + loadSettings().then(({ settings }) => { + setTimeout(() => { + // Generate content based on enabled sections + var lastWeekUl = '
    '; + var i; + + if (settings.sections.tasks || settings.sections.prs) { + for (i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i]; } - } else { - const elements = window.emailClientAdapter.getEditorElements(); - if (!elements || !elements.body) { - console.error('Email client editor not found'); - return; + if (settings.sections.reviewed) { + for (i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i]; } - window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange); - } + + lastWeekUl += '
'; + + var nextWeekUl = '
    '; + for (i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i]; + nextWeekUl += '
'; + + // Determine the time period text based on whether it's custom dates or default + var timePeriodText; + if (lastWeekContribution) { + timePeriodText = gsoc == 1 ? 'yesterday' : 'last week'; + } else { + timePeriodText = `from ${formatDate(startingDate)} to ${formatDate(endingDate)}`; + } + + // Create content with enabled sections + let content = ''; + + content += `1. What did I do ${timePeriodText}?
${lastWeekUl}
`; + content += `2. What I plan to do this week?
${nextWeekUl}
`; + + if (settings.sections.blockers) { + content += `3. What is stopping me from doing my work?
${userReason}`; + } + + // Update the scrum report + if (outputTarget === 'popup') { + const reportElement = document.getElementById('scrumReport'); + if (reportElement) { + reportElement.innerHTML = content; + } + } else { + const elements = window.emailClientAdapter.getEditorElements(); + if (!elements || !elements.body) { + console.error('Email client editor not found'); + return; + } + window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange); + } + }, 100); }); } @@ -310,126 +390,142 @@ ${userReason}`; } function writeGithubPrsReviews() { - var items = githubPrsReviewData.items; - - reviewedPrsArray = []; - githubPrsReviewDataProcessed = {}; - - for (var i = 0; i < items.length; i++) { - var item = items[i]; - console.log(`Review item ${i + 1}/${items.length}:`, { - number: item.number, - author: item.user.login, - type: item.pull_request ? "PR" : "Issue", - state: item.state, - title: item.title - }); - - if (item.user.login === githubUsername) { - continue; - } - - var repository_url = item.repository_url; - var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); - var title = item.title; - var number = item.number; - var html_url = item.html_url; - - if (!githubPrsReviewDataProcessed[project]) { - githubPrsReviewDataProcessed[project] = []; - } - - var obj = { - number: number, - html_url: html_url, - title: title, - state: item.state, - }; - githubPrsReviewDataProcessed[project].push(obj); - } - - for (var repo in githubPrsReviewDataProcessed) { - var repoLi = '
  • (' + repo + ') - Reviewed '; - if (githubPrsReviewDataProcessed[repo].length > 1) { - repoLi += 'PRs - '; - } else { - repoLi += 'PR - '; - } - - if (githubPrsReviewDataProcessed[repo].length <= 1) { - for (var pr in githubPrsReviewDataProcessed[repo]) { - var pr_arr = githubPrsReviewDataProcessed[repo][pr]; - var prText = ''; - prText += `#${pr_arr.number} (${pr_arr.title}) `; - if (pr_arr.state === 'open') { - prText += issue_opened_button; - } else { - prText += issue_closed_button; + if (!githubPrsReviewData) return; + + // Load settings before processing reviews + loadSettings().then(({ settings }) => { + if (settings.sections.reviewed) { + const filteredData = filterGithubData(githubPrsReviewData, settings); + var items = filteredData.items; + + reviewedPrsArray = []; + githubPrsReviewDataProcessed = {}; + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + console.log(`Review item ${i + 1}/${items.length}:`, { + number: item.number, + author: item.user.login, + type: item.pull_request ? "PR" : "Issue", + state: item.state, + title: item.title + }); + + if (item.user.login === githubUsername) { + continue; + } + + var repository_url = item.repository_url; + var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); + var title = item.title; + var number = item.number; + var html_url = item.html_url; + + if (!githubPrsReviewDataProcessed[project]) { + githubPrsReviewDataProcessed[project] = []; } - prText += '  '; - repoLi += prText; + + var obj = { + number: number, + html_url: html_url, + title: title, + state: item.state, + }; + githubPrsReviewDataProcessed[project].push(obj); } - } else { - repoLi += '
      '; - for (var pr1 in githubPrsReviewDataProcessed[repo]) { - var pr_arr1 = githubPrsReviewDataProcessed[repo][pr1]; - var prText1 = ''; - prText1 += `
    • #${pr_arr1.number} (${pr_arr1.title}) `; - if (pr_arr1.state === 'open') { - prText1 += issue_opened_button; + + for (var repo in githubPrsReviewDataProcessed) { + var repoLi = '
    • (' + repo + ') - Reviewed '; + if (githubPrsReviewDataProcessed[repo].length > 1) { + repoLi += 'PRs - '; } else { - prText1 += issue_closed_button; + repoLi += 'PR - '; } - prText1 += '  
    • '; - repoLi += prText1; + + if (githubPrsReviewDataProcessed[repo].length <= 1) { + for (var pr in githubPrsReviewDataProcessed[repo]) { + var pr_arr = githubPrsReviewDataProcessed[repo][pr]; + var prText = ''; + prText += `#${pr_arr.number} (${pr_arr.title}) `; + if (pr_arr.state === 'open') { + prText += issue_opened_button; + } else { + prText += issue_closed_button; + } + prText += '  '; + repoLi += prText; + } + } else { + repoLi += '
        '; + for (var pr1 in githubPrsReviewDataProcessed[repo]) { + var pr_arr1 = githubPrsReviewDataProcessed[repo][pr1]; + var prText1 = ''; + prText1 += `
      • #${pr_arr1.number} (${pr_arr1.title}) `; + if (pr_arr1.state === 'open') { + prText1 += issue_opened_button; + } else { + prText1 += issue_closed_button; + } + prText1 += '  
      • '; + repoLi += prText1; + } + repoLi += '
      '; + } + repoLi += ''; + reviewedPrsArray.push(repoLi); } - repoLi += '
    '; + + writeScrumBody(); } - repoLi += '
  • '; - reviewedPrsArray.push(repoLi); - } - - writeScrumBody(); + }); } function writeGithubIssuesPrs() { - var data = githubIssuesData; - var items = data.items; - - lastWeekArray = []; - nextWeekArray = []; - - for (var i = 0; i < items.length; i++) { - var item = items[i]; - var html_url = item.html_url; - var repository_url = item.repository_url; - var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); - var title = item.title; - var number = item.number; - var li = ''; - - if (item.pull_request) { - if (item.state === 'closed') { - li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_merged_button}
  • `; - } else if (item.state === 'open') { - li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_unmerged_button}
  • `; - } - } else { - if (item.state === 'open' && item.body && item.body.toUpperCase().indexOf('YES') > 0) { - var li2 = `
  • (${project}) - Work on Issue(#${number}) - ${title} ${issue_opened_button}
  • `; - nextWeekArray.push(li2); - } - if (item.state === 'open') { - li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_opened_button}
  • `; - } else if (item.state === 'closed') { - li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_closed_button}
  • `; - } - } - if (li) { - lastWeekArray.push(li); - } else { + if (!githubIssuesData) return; + + // Load settings before processing data + loadSettings().then(({ settings }) => { + const filteredData = filterGithubData(githubIssuesData, settings); + lastWeekArray = []; // Reset the array before processing + + // Process issues and PRs based on section toggles + if (settings.sections.tasks || settings.sections.prs) { + filteredData.items.forEach((item) => { + const isPR = 'pull_request' in item; + if ((isPR && settings.sections.prs) || (!isPR && settings.sections.tasks)) { + var html_url = item.html_url; + var repository_url = item.repository_url; + var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); + var title = item.title; + var number = item.number; + var li = ''; + + if (item.pull_request) { + if (item.state === 'closed') { + li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_merged_button}
  • `; + } else if (item.state === 'open') { + li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_unmerged_button}
  • `; + } + } else { + if (item.state === 'open' && item.body && item.body.toUpperCase().indexOf('YES') > 0) { + var li2 = `
  • (${project}) - Work on Issue(#${number}) - ${title} ${issue_opened_button}
  • `; + nextWeekArray.push(li2); + } + if (item.state === 'open') { + li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_opened_button}
  • `; + } else if (item.state === 'closed') { + li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_closed_button}
  • `; + } + } + if (li) { + lastWeekArray.push(li); + } + } + }); } - } - writeScrumBody(); + + // Now process PR reviews + writeGithubPrsReviews(); + }); } var intervalBody = setInterval(() => { if (!window.emailClientAdapter) return; @@ -457,18 +553,19 @@ ${userReason}`; var intervalWriteGithub = setInterval(() => { if (scrumBody && githubUsername && githubIssuesData && githubPrsReviewData) { clearInterval(intervalWriteGithub); + // First process the PRs and issues writeGithubIssuesPrs(); - writeGithubPrsReviews(); + // writeGithubPrsReviews will be called from within writeGithubIssuesPrs after it's done } }, 500); } -allIncluded('email'); +allIncluded('email'); $('button>span:contains(New conversation)').parent('button').click(() => { - allIncluded(); + allIncluded(); }); -window.generateScrumReport = function() { - allIncluded('popup'); +window.generateScrumReport = function () { + allIncluded('popup'); }; $('button>span:contains(New conversation)') diff --git a/src/scripts/settings.js b/src/scripts/settings.js new file mode 100644 index 0000000..d269a2c --- /dev/null +++ b/src/scripts/settings.js @@ -0,0 +1,115 @@ +// Initialize settings UI +document.addEventListener('DOMContentLoaded', () => { + // Load and apply settings + loadSettings().then(({ settings, templates }) => { + // Initialize section toggles + Object.keys(settings.sections).forEach(section => { + const toggle = document.getElementById(`toggle-${section}`); + if (toggle) { + toggle.checked = settings.sections[section]; + toggle.addEventListener('change', () => { + settings.sections[section] = toggle.checked; + saveSettings(settings).then(() => { + // Refresh report + document.getElementById('generateReport').click(); + }); + }); + } + }); + + // Initialize filters + document.getElementById('filter-open-only').checked = settings.filters.openOnly; + document.getElementById('filter-exclude-drafts').checked = settings.filters.excludeDrafts; + + // Add filter change listeners + ['filter-open-only', 'filter-exclude-drafts'].forEach(filterId => { + const filter = document.getElementById(filterId); + filter.addEventListener('change', () => { + settings.filters.openOnly = document.getElementById('filter-open-only').checked; + settings.filters.excludeDrafts = document.getElementById('filter-exclude-drafts').checked; + saveSettings(settings).then(() => { + // Refresh report + document.getElementById('generateReport').click(); + }); + }); + }); + + // Initialize template list + refreshTemplateList(templates); + }); + + // Template save button + document.getElementById('save-template').addEventListener('click', () => { + const name = document.getElementById('template-name').value.trim(); + if (!name) { + Materialize.toast('Please enter a template name', 3000); + return; + } + + loadSettings().then(({ settings }) => { + saveTemplate(name, settings).then(() => { + document.getElementById('template-name').value = ''; + Materialize.toast('Template saved successfully', 3000); + refreshTemplateList(); + }); + }); + }); +}); + +// Refresh template list +function refreshTemplateList() { + const templateList = document.querySelector('.template-list'); + + loadSettings().then(({ templates }) => { + templateList.innerHTML = ''; + + Object.keys(templates).forEach(name => { + const templateItem = document.createElement('div'); + templateItem.className = 'template-item'; + + const templateName = document.createElement('span'); + templateName.textContent = name; + templateItem.appendChild(templateName); + + const actionDiv = document.createElement('div'); + actionDiv.className = 'template-actions'; + + // Load button + const loadBtn = document.createElement('button'); + loadBtn.className = 'btn-small waves-effect waves-light'; + loadBtn.innerHTML = ''; + loadBtn.title = 'Load template'; + loadBtn.addEventListener('click', () => { + loadTemplate(name).then(settings => { + if (settings) { + saveSettings(settings).then(() => { + Materialize.toast('Template loaded successfully', 3000); + // Refresh UI and report + location.reload(); + }); + } + }); + }); + + // Delete button + const deleteBtn = document.createElement('button'); + deleteBtn.className = 'btn-small waves-effect waves-light red'; + deleteBtn.innerHTML = ''; + deleteBtn.title = 'Delete template'; + deleteBtn.addEventListener('click', () => { + if (confirm(`Are you sure you want to delete the template "${name}"?`)) { + deleteTemplate(name).then(() => { + Materialize.toast('Template deleted successfully', 3000); + refreshTemplateList(); + }); + } + }); + + actionDiv.appendChild(loadBtn); + actionDiv.appendChild(deleteBtn); + templateItem.appendChild(actionDiv); + + templateList.appendChild(templateItem); + }); + }); +} \ No newline at end of file From e793bfaf683f7c8829c6980cadc0794c722091bb Mon Sep 17 00:00:00 2001 From: Preeti Yadav Date: Mon, 2 Jun 2025 18:21:06 +0530 Subject: [PATCH 04/13] Delete .github/release-drafter.yml --- .github/release-drafter.yml | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index 75c57c9..0000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,31 +0,0 @@ -name-template: 'v$RESOLVED_VERSION 🌈' -tag-template: 'v$RESOLVED_VERSION' -categories: - - title: '🚀 Features' - labels: - - 'feature' - - 'enhancement' - - title: '🐛 Bug Fixes' - labels: - - 'fix' - - 'bugfix' - - 'bug' - - title: '🧰 Maintenance' - labels: 'chore' -change-template: '- $TITLE @$AUTHOR (#$NUMBER)' -change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. -version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - patch: - labels: - - 'patch' - default: patch -template: | - ## Changes - - $CHANGES \ No newline at end of file From 078533d75ec746041eb0055b23152a6411a50151 Mon Sep 17 00:00:00 2001 From: Preeti Yadav Date: Mon, 2 Jun 2025 18:34:57 +0530 Subject: [PATCH 05/13] Delete .github/workflows/release-drafter.yml --- .github/workflows/release-drafter.yml | 42 --------------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index d84c395..0000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Release Drafter - -on: - push: - # branches to consider in the event; optional, defaults to all - branches: - - master - - # pull_request event is required only for autolabeler - pull_request: - # Only following types are handled by the action, but one can default to all as well - types: [opened, reopened, synchronize] - # pull_request_target event is required for autolabeler to support PRs from forks - # pull_request_target: - # types: [opened, reopened, synchronize] - -permissions: - contents: read - -jobs: - update_release_draft: - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - steps: - # (Optional) GitHub Enterprise requires GHE_HOST variable set - #- name: Set GHE_HOST - # run: | - # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV - - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v6.1.0 - # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml - # with: - # config-name: my-config.yml - # disable-autolabeler: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From ad87372e8e3ea9aa6bf89df192a6084a74082da6 Mon Sep 17 00:00:00 2001 From: Preeti9764 Date: Fri, 6 Jun 2025 17:20:05 +0530 Subject: [PATCH 06/13] updated data fetching --- src/scripts/scrumHelper.js | 624 +++++++++++++++++++++++-------------- src/scripts/settings.js | 14 +- 2 files changed, 402 insertions(+), 236 deletions(-) diff --git a/src/scripts/scrumHelper.js b/src/scripts/scrumHelper.js index 84ae294..f61d276 100644 --- a/src/scripts/scrumHelper.js +++ b/src/scripts/scrumHelper.js @@ -14,6 +14,8 @@ const DEFAULT_SETTINGS = { } }; +const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds + // Load settings from storage function loadSettings() { return new Promise((resolve) => { @@ -81,6 +83,56 @@ function filterGithubData(data, settings) { return { ...data, items: filteredItems }; } +// Add cache management functions +function getCachedData(key) { + return new Promise((resolve) => { + chrome.storage.local.get(['githubDataCache'], (result) => { + const cache = result.githubDataCache || {}; + const cachedItem = cache[key]; + + if (cachedItem && (Date.now() - cachedItem.timestamp) < CACHE_DURATION) { + resolve(cachedItem.data); + } else { + resolve(null); + } + }); + }); +} + +function setCachedData(key, data) { + return new Promise((resolve) => { + chrome.storage.local.get(['githubDataCache'], (result) => { + const cache = result.githubDataCache || {}; + cache[key] = { + data: data, + timestamp: Date.now() + }; + chrome.storage.local.set({ githubDataCache: cache }, resolve); + }); + }); +} + +function checkRateLimit() { + return new Promise((resolve, reject) => { + $.ajax({ + url: 'https://api.github.com/rate_limit', + type: 'GET', + success: (response) => { + const remaining = response.rate.remaining; + if (remaining < 10) { + const resetTime = new Date(response.rate.reset * 1000); + reject(`GitHub API rate limit nearly exceeded. Resets at ${resetTime.toLocaleTimeString()}`); + } else { + resolve(remaining); + } + }, + error: (xhr) => { + reject('Failed to check rate limit'); + } + }); + }); +} + function allIncluded(outputTarget = 'email') { console.log('allIncluded called with outputTarget:', outputTarget); console.log('Current window context:', window.location.href); @@ -228,71 +280,82 @@ function allIncluded(outputTarget = 'email') { ('00' + WeekDay.toString()).slice(-2); return WeekDisplayPadded; } - // fetch github data - function fetchGithubData() { - var issueUrl = 'https://api.github.com/search/issues?q=author%3A' + - githubUsername + - '+org%3Afossasia+created%3A' + - startingDate + - '..' + - endingDate + - '&per_page=100'; - - $.ajax({ - dataType: 'json', - type: 'GET', - url: issueUrl, - error: (xhr, textStatus, errorThrown) => { - console.error('Error fetching GitHub data:', { - status: xhr.status, - textStatus: textStatus, - error: errorThrown - }); - }, - success: (data) => { - githubIssuesData = data; + // Modify fetchGithubData to use caching and rate limit checking + async function fetchGithubData() { + try { + // Check rate limit first + await checkRateLimit(); + + const cacheKey = `${githubUsername}-${startingDate}-${endingDate}`; + const cachedData = await getCachedData(cacheKey); + + if (cachedData) { + githubIssuesData = cachedData.issues; + githubPrsReviewData = cachedData.prs; + githubUserData = cachedData.userData; writeGithubIssuesPrs(); - }, - }); + scrumSubjectLoaded(); + return; + } - // PR reviews fetch - var prUrl = 'https://api.github.com/search/issues?q=commenter%3A' + - githubUsername + - '+org%3Afossasia+updated%3A' + - startingDate + - '..' + - endingDate + - '&per_page=100'; + var issueUrl = 'https://api.github.com/search/issues?q=author%3A' + + githubUsername + + '+org%3Afossasia+created%3A' + + startingDate + + '..' + + endingDate + + '&per_page=100'; + + var prUrl = 'https://api.github.com/search/issues?q=commenter%3A' + + githubUsername + + '+org%3Afossasia+updated%3A' + + startingDate + + '..' + + endingDate + + '&per_page=100'; + + var userUrl = 'https://api.github.com/users/' + githubUsername; + + // Use Promise.all to fetch all data in parallel + const [issuesResponse, prsResponse, userData] = await Promise.all([ + $.ajax({ + dataType: 'json', + type: 'GET', + url: issueUrl + }), + $.ajax({ + dataType: 'json', + type: 'GET', + url: prUrl + }), + $.ajax({ + dataType: 'json', + type: 'GET', + url: userUrl + }) + ]); + + githubIssuesData = issuesResponse; + githubPrsReviewData = prsResponse; + githubUserData = userData; + + // Cache all the responses + await setCachedData(cacheKey, { + issues: issuesResponse, + prs: prsResponse, + userData: userData, + timestamp: Date.now() + }); - $.ajax({ - dataType: 'json', - type: 'GET', - url: prUrl, - error: (xhr, textStatus, errorThrown) => { - console.error('Error fetching PR reviews:', { - status: xhr.status, - textStatus: textStatus, - error: errorThrown - }); - }, - success: (data) => { - githubPrsReviewData = data; - writeGithubPrsReviews(); - }, - }); - // fetch github user data - var userUrl = 'https://api.github.com/users/' + githubUsername; - $.ajax({ - dataType: 'json', - type: 'GET', - url: userUrl, - error: (xhr, textStatus, errorThrown) => { - // error - }, - success: (data) => { - githubUserData = data; - }, - }); + writeGithubIssuesPrs(); + scrumSubjectLoaded(); + } catch (error) { + console.error('Error fetching GitHub data:', error); + // Show error to user + if (typeof Materialize !== 'undefined') { + Materialize.toast(error.toString(), 4000); + } + } } function formatDate(dateString) { @@ -303,61 +366,86 @@ function allIncluded(outputTarget = 'email') { //load initial text in scrum body function writeScrumBody() { - if (!enableToggle) return; + if (!enableToggle) { + console.log('Scrum generation disabled'); + return; + } + // Load settings first loadSettings().then(({ settings }) => { - setTimeout(() => { - // Generate content based on enabled sections - var lastWeekUl = '
      '; - var i; + console.log('Writing scrum body with settings:', settings); - if (settings.sections.tasks || settings.sections.prs) { - for (i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i]; - } - - if (settings.sections.reviewed) { - for (i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i]; - } + // Generate content first + var lastWeekUl = '
        '; + var i; - lastWeekUl += '
      '; + // Apply section filters + if (settings.sections.tasks || settings.sections.prs) { + for (i = 0; i < lastWeekArray.length; i++) lastWeekUl += lastWeekArray[i]; + } - var nextWeekUl = '
        '; - for (i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i]; - nextWeekUl += '
      '; + if (settings.sections.reviewed) { + for (i = 0; i < reviewedPrsArray.length; i++) lastWeekUl += reviewedPrsArray[i]; + } - // Determine the time period text based on whether it's custom dates or default - var timePeriodText; - if (lastWeekContribution) { - timePeriodText = gsoc == 1 ? 'yesterday' : 'last week'; - } else { - timePeriodText = `from ${formatDate(startingDate)} to ${formatDate(endingDate)}`; - } + lastWeekUl += '
    '; + + var nextWeekUl = '
      '; + for (i = 0; i < nextWeekArray.length; i++) nextWeekUl += nextWeekArray[i]; + nextWeekUl += '
    '; + + var weekOrDay = gsoc == 1 ? 'yesterday' : 'last week'; + var weekOrDay2 = gsoc == 1 ? 'today' : 'this week'; + + // Create the complete content + let content; + if (lastWeekContribution == true) { + content = `1. What did I do ${weekOrDay}?
    +${lastWeekUl}
    +2. What I plan to do ${weekOrDay2}?
    +${nextWeekUl}
    `; + } else { + content = `1. What did I do from ${formatDate(startingDate)} to ${formatDate(endingDate)}?
    +${lastWeekUl}
    +2. What I plan to do ${weekOrDay2}?
    +${nextWeekUl}
    `; + } - // Create content with enabled sections - let content = ''; + // Add blockers section based on settings + if (settings.sections.blockers) { + content += `3. What is stopping me from doing my work?
    ${userReason}`; + } - content += `1. What did I do ${timePeriodText}?
    ${lastWeekUl}
    `; - content += `2. What I plan to do this week?
    ${nextWeekUl}
    `; + console.log('Generated content with filters applied'); - if (settings.sections.blockers) { - content += `3. What is stopping me from doing my work?
    ${userReason}`; - } + if (outputTarget === 'popup') { + const scrumReport = document.getElementById('scrumReport'); + if (scrumReport) { + console.log("Found scrum report div, updating content"); + scrumReport.innerHTML = content; - // Update the scrum report - if (outputTarget === 'popup') { - const reportElement = document.getElementById('scrumReport'); - if (reportElement) { - reportElement.innerHTML = content; + // Reset generate button + const generateBtn = document.getElementById('generateReport'); + if (generateBtn) { + generateBtn.innerHTML = ' Generate Report'; + generateBtn.disabled = false; } } else { - const elements = window.emailClientAdapter.getEditorElements(); - if (!elements || !elements.body) { - console.error('Email client editor not found'); - return; - } - window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange); + console.error('Scrum report element not found'); + } + } else { + if (!window.emailClientAdapter) { + console.error('Email client adapter not found'); + return; + } + + const elements = window.emailClientAdapter.getEditorElements(); + if (!elements || !elements.body) { + console.error('Email client editor not found'); + return; } - }, 100); + window.emailClientAdapter.injectContent(elements.body, content, elements.eventTypes.contentChange); + } }); } @@ -372,158 +460,195 @@ function allIncluded(outputTarget = 'email') { return project; } function scrumSubjectLoaded() { - if (!enableToggle) return; - setTimeout(() => { - var name = githubUserData.name || githubUsername; - var project = getProject(); - var curDate = new Date(); - var year = curDate.getFullYear().toString(); - var date = curDate.getDate(); - var month = curDate.getMonth(); - month++; - if (month < 10) month = '0' + month; - if (date < 10) date = '0' + date; - var dateCode = year.toString() + month.toString() + date.toString(); - scrumSubject.value = '[Scrum] ' + name + ' - ' + project + ' - ' + dateCode + ' - False'; - scrumSubject.dispatchEvent(new Event('input', { bubbles: true })); + if (!enableToggle || !githubUserData) { + console.log('Subject load skipped - toggle or user data missing'); + return; + } + + // Get the elements first + const elements = window.emailClientAdapter?.getEditorElements(); + if (!elements || !elements.subject) { + console.log('Email editor or subject not ready'); + return; + } + + scrumSubject = elements.subject; + + var name = githubUserData.name || githubUsername; + var project = getProject(); + var curDate = new Date(); + var year = curDate.getFullYear().toString(); + var date = curDate.getDate(); + var month = curDate.getMonth(); + month++; + if (month < 10) month = '0' + month; + if (date < 10) date = '0' + date; + var dateCode = year.toString() + month.toString() + date.toString(); + + console.log('Setting subject with:', { + name: name, + project: project, + dateCode: dateCode }); + + scrumSubject.value = '[Scrum] ' + name + ' - ' + project + ' - ' + dateCode + ' - False'; + scrumSubject.dispatchEvent(new Event('input', { bubbles: true })); + console.log('Subject updated:', scrumSubject.value); } function writeGithubPrsReviews() { - if (!githubPrsReviewData) return; + if (!githubPrsReviewData || !githubPrsReviewData.items) { + console.error('No PR review data available'); + return; + } - // Load settings before processing reviews loadSettings().then(({ settings }) => { - if (settings.sections.reviewed) { - const filteredData = filterGithubData(githubPrsReviewData, settings); - var items = filteredData.items; - - reviewedPrsArray = []; - githubPrsReviewDataProcessed = {}; - - for (var i = 0; i < items.length; i++) { - var item = items[i]; - console.log(`Review item ${i + 1}/${items.length}:`, { - number: item.number, - author: item.user.login, - type: item.pull_request ? "PR" : "Issue", - state: item.state, - title: item.title - }); - - if (item.user.login === githubUsername) { - continue; - } + if (!settings.sections.reviewed) { + console.log('PR reviews section disabled'); + writeScrumBody(); + return; + } - var repository_url = item.repository_url; - var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); - var title = item.title; - var number = item.number; - var html_url = item.html_url; + const filteredData = filterGithubData(githubPrsReviewData, settings); + var items = filteredData.items; - if (!githubPrsReviewDataProcessed[project]) { - githubPrsReviewDataProcessed[project] = []; - } + reviewedPrsArray = []; + githubPrsReviewDataProcessed = {}; + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + console.log(`Review item ${i + 1}/${items.length}:`, { + number: item.number, + author: item.user.login, + type: item.pull_request ? "PR" : "Issue", + state: item.state, + title: item.title + }); - var obj = { - number: number, - html_url: html_url, - title: title, - state: item.state, - }; - githubPrsReviewDataProcessed[project].push(obj); + if (item.user.login === githubUsername) { + continue; } - for (var repo in githubPrsReviewDataProcessed) { - var repoLi = '
  • (' + repo + ') - Reviewed '; - if (githubPrsReviewDataProcessed[repo].length > 1) { - repoLi += 'PRs - '; - } else { - repoLi += 'PR - '; - } + var repository_url = item.repository_url; + var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); + var title = item.title; + var number = item.number; + var html_url = item.html_url; + + if (!githubPrsReviewDataProcessed[project]) { + githubPrsReviewDataProcessed[project] = []; + } + + var obj = { + number: number, + html_url: html_url, + title: title, + state: item.state, + }; + githubPrsReviewDataProcessed[project].push(obj); + } + + for (var repo in githubPrsReviewDataProcessed) { + var repoLi = '
  • (' + repo + ') - Reviewed '; + if (githubPrsReviewDataProcessed[repo].length > 1) { + repoLi += 'PRs - '; + } else { + repoLi += 'PR - '; + } - if (githubPrsReviewDataProcessed[repo].length <= 1) { - for (var pr in githubPrsReviewDataProcessed[repo]) { - var pr_arr = githubPrsReviewDataProcessed[repo][pr]; - var prText = ''; - prText += `#${pr_arr.number} (${pr_arr.title}) `; - if (pr_arr.state === 'open') { - prText += issue_opened_button; - } else { - prText += issue_closed_button; - } - prText += '  '; - repoLi += prText; + if (githubPrsReviewDataProcessed[repo].length <= 1) { + for (var pr in githubPrsReviewDataProcessed[repo]) { + var pr_arr = githubPrsReviewDataProcessed[repo][pr]; + var prText = ''; + prText += `#${pr_arr.number} (${pr_arr.title}) `; + if (pr_arr.state === 'open') { + prText += issue_opened_button; + } else { + prText += issue_closed_button; } - } else { - repoLi += '
      '; - for (var pr1 in githubPrsReviewDataProcessed[repo]) { - var pr_arr1 = githubPrsReviewDataProcessed[repo][pr1]; - var prText1 = ''; - prText1 += `
    • #${pr_arr1.number} (${pr_arr1.title}) `; - if (pr_arr1.state === 'open') { - prText1 += issue_opened_button; - } else { - prText1 += issue_closed_button; - } - prText1 += '  
    • '; - repoLi += prText1; + prText += '  '; + repoLi += prText; + } + } else { + repoLi += '
        '; + for (var pr1 in githubPrsReviewDataProcessed[repo]) { + var pr_arr1 = githubPrsReviewDataProcessed[repo][pr1]; + var prText1 = ''; + prText1 += `
      • #${pr_arr1.number} (${pr_arr1.title}) `; + if (pr_arr1.state === 'open') { + prText1 += issue_opened_button; + } else { + prText1 += issue_closed_button; } - repoLi += '
      '; + prText1 += '  '; + repoLi += prText1; } - repoLi += ''; - reviewedPrsArray.push(repoLi); + repoLi += '
    '; } - - writeScrumBody(); + repoLi += '
  • '; + reviewedPrsArray.push(repoLi); } + + writeScrumBody(); }); } function writeGithubIssuesPrs() { - if (!githubIssuesData) return; + if (!githubIssuesData || !githubIssuesData.items) { + console.error('No GitHub issues data available'); + return; + } - // Load settings before processing data + // Load settings and apply filters loadSettings().then(({ settings }) => { + console.log('Processing GitHub issues/PRs data with filters:', settings.filters); + + // Apply filters to the data const filteredData = filterGithubData(githubIssuesData, settings); - lastWeekArray = []; // Reset the array before processing + const items = filteredData.items; - // Process issues and PRs based on section toggles - if (settings.sections.tasks || settings.sections.prs) { - filteredData.items.forEach((item) => { - const isPR = 'pull_request' in item; - if ((isPR && settings.sections.prs) || (!isPR && settings.sections.tasks)) { - var html_url = item.html_url; - var repository_url = item.repository_url; - var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); - var title = item.title; - var number = item.number; - var li = ''; - - if (item.pull_request) { - if (item.state === 'closed') { - li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_merged_button}
  • `; - } else if (item.state === 'open') { - li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_unmerged_button}
  • `; - } - } else { - if (item.state === 'open' && item.body && item.body.toUpperCase().indexOf('YES') > 0) { - var li2 = `
  • (${project}) - Work on Issue(#${number}) - ${title} ${issue_opened_button}
  • `; - nextWeekArray.push(li2); - } - if (item.state === 'open') { - li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_opened_button}
  • `; - } else if (item.state === 'closed') { - li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_closed_button}
  • `; - } + lastWeekArray = []; + nextWeekArray = []; + + // Process items based on section settings + items.forEach(item => { + const isPR = 'pull_request' in item; + if ((isPR && settings.sections.prs) || (!isPR && settings.sections.tasks)) { + var html_url = item.html_url; + var repository_url = item.repository_url; + var project = repository_url.substr(repository_url.lastIndexOf('/') + 1); + var title = item.title; + var number = item.number; + var li = ''; + + if (item.pull_request) { + if (item.state === 'closed') { + li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_merged_button}
  • `; + } else if (item.state === 'open') { + li = `
  • (${project}) - Made PR (#${number}) - ${title} ${pr_unmerged_button}
  • `; } - if (li) { - lastWeekArray.push(li); + } else { + if (item.state === 'open' && item.body && item.body.toUpperCase().indexOf('YES') > 0) { + var li2 = `
  • (${project}) - Work on Issue(#${number}) - ${title} ${issue_opened_button}
  • `; + nextWeekArray.push(li2); + } + if (item.state === 'open') { + li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_opened_button}
  • `; + } else if (item.state === 'closed') { + li = `
  • (${project}) - Opened Issue(#${number}) - ${title} ${issue_closed_button}
  • `; } } - }); - } + if (li) { + lastWeekArray.push(li); + } + } + }); - // Now process PR reviews + console.log('Processed items with filters:', { + lastWeekItems: lastWeekArray.length, + nextWeekItems: nextWeekArray.length + }); + + // Now process PR reviews with the same settings writeGithubPrsReviews(); }); } @@ -539,13 +664,24 @@ function allIncluded(outputTarget = 'email') { }, 500); var intervalSubject = setInterval(() => { - if (!githubUserData || !window.emailClientAdapter) return; + if (!window.emailClientAdapter) { + console.log('Email client adapter not ready'); + return; + } const elements = window.emailClientAdapter.getEditorElements(); - if (!elements || !elements.subject) return; + if (!elements || !elements.subject) { + console.log('Subject element not ready'); + return; + } + + if (!githubUserData) { + console.log('GitHub user data not ready'); + return; + } + console.log('All requirements met for subject loading'); clearInterval(intervalSubject); - scrumSubject = elements.subject; scrumSubjectLoaded(); }, 500); @@ -565,6 +701,12 @@ $('button>span:contains(New conversation)').parent('button').click(() => { }); window.generateScrumReport = function () { + console.log('Generating scrum report for popup'); + const generateBtn = document.getElementById('generateReport'); + if (generateBtn) { + generateBtn.innerHTML = ' Generating...'; + generateBtn.disabled = true; + } allIncluded('popup'); }; @@ -572,4 +714,22 @@ $('button>span:contains(New conversation)') .parent('button') .click(() => { allIncluded(); - }); \ No newline at end of file + }); + +// Modify the refreshReportFromCache function to also handle subject +function refreshReportFromCache() { + const cacheKey = `${githubUsername}-${startingDate}-${endingDate}`; + getCachedData(cacheKey).then(cachedData => { + if (cachedData) { + githubIssuesData = cachedData.issues; + githubPrsReviewData = cachedData.prs; + githubUserData = cachedData.userData; + writeGithubIssuesPrs(); + + // Update subject if we're in email context + if (scrumSubject) { + scrumSubjectLoaded(); + } + } + }); +} \ No newline at end of file diff --git a/src/scripts/settings.js b/src/scripts/settings.js index d269a2c..bb8b94a 100644 --- a/src/scripts/settings.js +++ b/src/scripts/settings.js @@ -10,8 +10,11 @@ document.addEventListener('DOMContentLoaded', () => { toggle.addEventListener('change', () => { settings.sections[section] = toggle.checked; saveSettings(settings).then(() => { - // Refresh report - document.getElementById('generateReport').click(); + // Keep original behavior - regenerate report + const generateBtn = document.getElementById('generateReport'); + if (generateBtn) { + generateBtn.click(); + } }); }); } @@ -28,8 +31,11 @@ document.addEventListener('DOMContentLoaded', () => { settings.filters.openOnly = document.getElementById('filter-open-only').checked; settings.filters.excludeDrafts = document.getElementById('filter-exclude-drafts').checked; saveSettings(settings).then(() => { - // Refresh report - document.getElementById('generateReport').click(); + // Keep original behavior - regenerate report + const generateBtn = document.getElementById('generateReport'); + if (generateBtn) { + generateBtn.click(); + } }); }); }); From 731a5a8e602dc7ac50fc6354f0340b761e0e9ca3 Mon Sep 17 00:00:00 2001 From: Preeti9764 Date: Sat, 7 Jun 2025 13:18:15 +0530 Subject: [PATCH 07/13] added note --- src/popup.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/popup.html b/src/popup.html index d28d5dd..399cce8 100644 --- a/src/popup.html +++ b/src/popup.html @@ -149,6 +149,22 @@
    Templates
    + +
    +
    Note:
    +
    +
      +
    • The PRs fetched are according to the date last reviewed by anyone. So if you reviewed a PR 10 days back, + and someone reviewed it 2 days back, it will appear in your last week's activity. See this issue. +
    • +
    • By using the extension you understand that there might be discrepancies in the SCRUM generated. You are + advised to edit the SCRUM afterwards to remove any discrepancies. +
    • +
    +
    +
    +