@@ -475,113 +475,8 @@ document.addEventListener('DOMContentLoaded', function () {
475475 projectNameInput.addEventListener('input', function () {
476476 chrome.storage.local.set({ projectName: projectNameInput.value });
477477 });
478-
479- // Organization validation function (using function expression to avoid hoisting issues)
480- const validateOrganization = async function(orgName, githubToken = '') {
481- const validationMessage = document.getElementById('orgValidationMessage');
482- const validationText = document.getElementById('orgValidationText');
483-
484- // Safety check - if elements don't exist, return early
485- if (!validationMessage || !validationText) {
486- console.warn('Validation elements not found');
487- return true;
488- }
489-
490- // Hide validation for empty input (valid case)
491- if (!orgName || orgName.trim() === '') {
492- validationMessage.classList.add('hidden');
493- return true;
494- }
495-
496- const cleanOrgName = orgName.trim();
497-
498- // GitHub org/username validation: alphanumeric, hyphens, underscores
499- const validPattern = /^[a-zA-Z0-9\-_]+$/;
500- if (!validPattern.test(cleanOrgName)) {
501- showValidationMessage('error', `Organization name contains invalid characters`);
502- return false;
503- }
504-
505- // Check length constraints (GitHub max is 39 characters)
506- if (cleanOrgName.length > 39) {
507- showValidationMessage('error', `Organization name too long (max 39 characters)`);
508- return false;
509- }
510-
511- // Optional: Only validate against API if user has provided a token
512- if (githubToken && githubToken.trim()) {
513- try {
514- const headers = { 'Authorization': `token ${githubToken}` };
515- const response = await fetch(`https://api.github.com/orgs/${cleanOrgName}`, {
516- method: 'HEAD',
517- headers
518- });
519-
520- if (response.status === 200) {
521- showValidationMessage('success', `Organization "${cleanOrgName}" verified`);
522- return true;
523- } else if (response.status === 404) {
524- showValidationMessage('warning', `Organization "${cleanOrgName}" not found (will search public repos)`);
525- return true; // Still allow it - might be private or user/org distinction
526- } else if (response.status === 403) {
527- showValidationMessage('warning', `Rate limited or private organization`);
528- return true;
529- }
530- } catch (error) {
531- // Network error - fail silently, don't block user
532- console.log('Org validation network error:', error);
533- }
534- }
535-
536- // Default: assume it's valid, user will find out during report generation if not
537- validationMessage.classList.add('hidden');
538- return true;
539- }
540-
541- const showValidationMessage = function(type, message) {
542- const validationMessage = document.getElementById('orgValidationMessage');
543- const validationText = document.getElementById('orgValidationText');
544-
545- // Safety check
546- if (!validationMessage || !validationText) return;
547-
548- validationMessage.className = `${type} text-xs mt-1 mb-2 px-3 py-2 rounded-lg`;
549- validationText.textContent = message; // Use textContent for security
550- validationMessage.classList.remove('hidden');
551- };
552-
553- // Track the latest validation request to avoid race conditions
554- let orgValidationRequestId = 0;
555-
556- // CORRECT IMPLEMENTATION: Save only on blur (when user clicks out)
557- orgInput.addEventListener('focus', function () {
558- // Add visual highlight when field becomes active
559- this.classList.add('org-input-active');
560- });
561-
562- orgInput.addEventListener('blur', async function () {
563- // Remove highlight when field loses focus
564- this.classList.remove('org-input-active');
565-
566- // Save to storage only when user finishes editing (clicks out)
567- const cleanValue = this.value.trim().toLowerCase();
568- chrome.storage.local.set({
569- orgName: cleanValue,
570- githubCache: null // Clear cache when organization changes
571- });
572-
573- // Increment validation request id to handle race conditions
574- const currentRequestId = ++orgValidationRequestId;
575-
576- // Validate and show feedback only after user finishes editing
577- const result = await new Promise(resolve => {
578- chrome.storage.local.get(['githubToken'], resolve);
579- });
580-
581- // Only process the result if this is the latest request
582- if (currentRequestId === orgValidationRequestId) {
583- await validateOrganization(this.value, result.githubToken);
584- }
478+ orgInput.addEventListener('input', function () {
479+ chrome.storage.local.set({ orgName: orgInput.value.trim().toLowerCase() });
585480 });
586481 userReasonInput.addEventListener('input', function () {
587482 chrome.storage.local.set({ userReason: userReasonInput.value });
@@ -1630,4 +1525,77 @@ async function triggerRepoFetchIfEnabled() {
16301525 if (window.triggerRepoFetchIfEnabled) {
16311526 await window.triggerRepoFetchIfEnabled();
16321527 }
1633- }
1528+ }
1529+
1530+ const handleOrgInput = debounce(function () {
1531+ let org = orgInput.value.trim().toLowerCase();
1532+ if (!org) {
1533+ chrome.storage.local.set({ orgName: '' }, () => {
1534+ console.log(`Org cleared, triggering repo fetch for all git`);
1535+ chrome.storage.local.remove(['githubCache', 'repoCache']);
1536+ triggerRepoFetchIfEnabled();
1537+ })
1538+ return;
1539+ }
1540+ console.log('[Org Check] Checking organization:', org);
1541+ fetch(`https://api.github.com/orgs/${org}`)
1542+ .then(res => {
1543+ console.log('[Org Check] Response status for', org, ':', res.status);
1544+ if (res.status === 404) {
1545+ console.log('[Org Check] Organization not found on GitHub:', org);
1546+ const oldToast = document.getElementById('invalid-org-toast');
1547+ if (oldToast) oldToast.parentNode.removeChild(oldToast);
1548+ const toastDiv = document.createElement('div');
1549+ toastDiv.id = 'invalid-org-toast';
1550+ toastDiv.className = 'toast';
1551+ toastDiv.style.background = '#dc2626';
1552+ toastDiv.style.color = '#fff';
1553+ toastDiv.style.fontWeight = 'bold';
1554+ toastDiv.style.padding = '12px 24px';
1555+ toastDiv.style.borderRadius = '8px';
1556+ toastDiv.style.position = 'fixed';
1557+ toastDiv.style.top = '24px';
1558+ toastDiv.style.left = '50%';
1559+ toastDiv.style.transform = 'translateX(-50%)';
1560+ toastDiv.style.zIndex = '9999';
1561+ toastDiv.innerText = chrome.i18n.getMessage('orgNotFoundMessage');
1562+ document.body.appendChild(toastDiv);
1563+ setTimeout(() => {
1564+ if (toastDiv.parentNode) toastDiv.parentNode.removeChild(toastDiv);
1565+ }, 3000);
1566+ return;
1567+ }
1568+ const oldToast = document.getElementById('invalid-org-toast');
1569+ if (oldToast) oldToast.parentNode.removeChild(oldToast);
1570+ console.log('[Org Check] Organisation exists on GitHub:', org);
1571+ chrome.storage.local.set({ orgName: org }, function () {
1572+ // if (window.generateScrumReport) window.generateScrumReport();
1573+ triggerRepoFetchIfEnabled();
1574+ });
1575+ })
1576+ .catch((err) => {
1577+ console.log('[Org Check] Error validating organisation:', org, err);
1578+ const oldToast = document.getElementById('invalid-org-toast');
1579+ if (oldToast) oldToast.parentNode.removeChild(oldToast);
1580+ const toastDiv = document.createElement('div');
1581+ toastDiv.id = 'invalid-org-toast';
1582+ toastDiv.className = 'toast';
1583+ toastDiv.style.background = '#dc2626';
1584+ toastDiv.style.color = '#fff';
1585+ toastDiv.style.fontWeight = 'bold';
1586+ toastDiv.style.padding = '12px 24px';
1587+ toastDiv.style.borderRadius = '8px';
1588+ toastDiv.style.position = 'fixed';
1589+ toastDiv.style.top = '24px';
1590+ toastDiv.style.left = '50%';
1591+ toastDiv.style.transform = 'translateX(-50%)';
1592+ toastDiv.style.zIndex = '9999';
1593+ toastDiv.innerText = chrome.i18n.getMessage('orgValidationErrorMessage');
1594+ document.body.appendChild(toastDiv);
1595+ setTimeout(() => {
1596+ if (toastDiv.parentNode) toastDiv.parentNode.removeChild(toastDiv);
1597+ }, 3000);
1598+ });
1599+ }, 500);
1600+
1601+ orgInput.addEventListener('input', handleOrgInput);
0 commit comments