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
This commit is contained in:
Chris 2026-04-18 00:29:27 +03:00 committed by GitHub
parent 46329fc82b
commit 0440f46645
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 82 additions and 8 deletions

View file

@ -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;
}

View file

@ -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();
});
});

View file

@ -212,6 +212,7 @@ const ProjectDropdown: React.FC<ProjectDropdownProps> = ({
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}
/>
<button
type="button"