From 0440f466452264712d6468e6322d8677be2d7667 Mon Sep 17 00:00:00 2001 From: Chris Date: Sat, 18 Apr 2026 00:29:27 +0300 Subject: [PATCH] fix(ui): Auto-focus project search field when opened (Issue #992) (#1038) * fix(oidc): Add logging to help debug missing OIDC button (Issue #1036) When OIDC is configured but the login button doesn't appear, users had no way to debug what was wrong. This adds comprehensive logging: - Log when OIDC is disabled with helpful message - Log when providers are successfully loaded - Warn when numbered providers are skipped due to missing fields - Error when single provider config is incomplete with specific missing fields - Warn when OIDC is enabled but no valid providers found This helps users quickly identify configuration issues like: - Typos in environment variable names - Missing required fields (issuer, clientId, clientSecret) - Incorrect OIDC_ENABLED value Fixes #1036 * fix(ui): Auto-focus project search field when opened (Issue #992) When clicking on the project field in a task to add or change the project, the search input field now automatically receives focus, eliminating the need to manually click on it. This improves the user experience by reducing friction in the project assignment workflow. Fixes #992 --- backend/modules/oidc/providerConfig.js | 41 ++++++++++++---- .../unit/modules/oidc/providerConfig.test.js | 48 +++++++++++++++++++ .../components/Shared/ProjectDropdown.tsx | 1 + 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/backend/modules/oidc/providerConfig.js b/backend/modules/oidc/providerConfig.js index 5bcb096..69b0751 100644 --- a/backend/modules/oidc/providerConfig.js +++ b/backend/modules/oidc/providerConfig.js @@ -8,6 +8,9 @@ function parseCommaSeparated(value) { function loadProvidersFromEnv() { if (process.env.OIDC_ENABLED !== 'true') { + console.log( + 'OIDC is disabled. Set OIDC_ENABLED=true to enable SSO authentication.' + ); return []; } @@ -31,17 +34,24 @@ function loadProvidersFromEnv() { ), }; - if ( - !provider.slug || - !provider.name || - !provider.issuer || - !provider.clientId || - !provider.clientSecret - ) { + const missingFields = []; + if (!provider.slug) missingFields.push(`OIDC_PROVIDER_${i}_SLUG`); + if (!provider.name) missingFields.push(`OIDC_PROVIDER_${i}_NAME`); + if (!provider.issuer) missingFields.push(`OIDC_PROVIDER_${i}_ISSUER`); + if (!provider.clientId) + missingFields.push(`OIDC_PROVIDER_${i}_CLIENT_ID`); + if (!provider.clientSecret) + missingFields.push(`OIDC_PROVIDER_${i}_CLIENT_SECRET`); + + if (missingFields.length > 0) { + console.warn( + `Skipping OIDC provider ${i} due to missing required fields: ${missingFields.join(', ')}` + ); i++; continue; } + console.log(`Loaded OIDC provider ${i}: ${provider.name}`); providers.push(provider); i++; } @@ -60,13 +70,28 @@ function loadProvidersFromEnv() { ), }; - if (!provider.issuer || !provider.clientId || !provider.clientSecret) { + const missingFields = []; + if (!provider.issuer) missingFields.push('OIDC_ISSUER_URL'); + if (!provider.clientId) missingFields.push('OIDC_CLIENT_ID'); + if (!provider.clientSecret) missingFields.push('OIDC_CLIENT_SECRET'); + + if (missingFields.length > 0) { + console.error( + `Cannot load OIDC provider "${provider.name}": missing required fields: ${missingFields.join(', ')}` + ); return []; } + console.log(`Loaded OIDC provider: ${provider.name}`); providers.push(provider); } + if (providers.length === 0) { + console.warn( + 'OIDC is enabled but no valid providers are configured. Please check your environment variables.' + ); + } + return providers; } diff --git a/backend/tests/unit/modules/oidc/providerConfig.test.js b/backend/tests/unit/modules/oidc/providerConfig.test.js index a821160..af01a60 100644 --- a/backend/tests/unit/modules/oidc/providerConfig.test.js +++ b/backend/tests/unit/modules/oidc/providerConfig.test.js @@ -120,6 +120,10 @@ describe('OIDC Provider Configuration', () => { }); it('should return empty array if configuration is incomplete', () => { + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_NAME = 'Google'; @@ -127,6 +131,11 @@ describe('OIDC Provider Configuration', () => { const providers = providerConfig.getAllProviders(); expect(providers).toEqual([]); + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining('Cannot load OIDC provider') + ); + + consoleErrorSpy.mockRestore(); }); }); @@ -155,6 +164,13 @@ describe('OIDC Provider Configuration', () => { }); it('should skip numbered providers with incomplete config', () => { + const consoleWarnSpy = jest + .spyOn(console, 'warn') + .mockImplementation(); + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_1_NAME = 'Google'; @@ -171,6 +187,15 @@ describe('OIDC Provider Configuration', () => { expect(providers).toHaveLength(1); expect(providers[0].slug).toBe('okta'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('Skipping OIDC provider 1') + ); + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringContaining('Loaded OIDC provider 2: Okta') + ); + + consoleWarnSpy.mockRestore(); + consoleLogSpy.mockRestore(); }); it('should handle different settings per provider', () => { @@ -229,6 +254,10 @@ describe('OIDC Provider Configuration', () => { describe('isOidcEnabled', () => { it('should return true when OIDC is enabled with valid provider', () => { + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_NAME = 'Google'; process.env.OIDC_PROVIDER_SLUG = 'google'; @@ -238,18 +267,37 @@ describe('OIDC Provider Configuration', () => { providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(true); + + consoleLogSpy.mockRestore(); }); it('should return false when OIDC_ENABLED is false', () => { + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'false'; providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(false); + + consoleLogSpy.mockRestore(); }); it('should return false when no providers configured', () => { + const consoleWarnSpy = jest + .spyOn(console, 'warn') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(false); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'OIDC is enabled but no valid providers are configured' + ) + ); + + consoleWarnSpy.mockRestore(); }); }); diff --git a/frontend/components/Shared/ProjectDropdown.tsx b/frontend/components/Shared/ProjectDropdown.tsx index 6835fb0..f66cb6f 100644 --- a/frontend/components/Shared/ProjectDropdown.tsx +++ b/frontend/components/Shared/ProjectDropdown.tsx @@ -212,6 +212,7 @@ const ProjectDropdown: React.FC = ({ disabled={disabled} className="w-full bg-transparent border-none outline-none text-sm text-gray-900 dark:text-gray-100 disabled:cursor-not-allowed pr-8" autoComplete="off" + autoFocus={dropdownOpen} />