From fd108faa7dcc27377385dd20cd656e0a5fbacfbd Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 4 Feb 2026 11:39:50 +0000 Subject: [PATCH] feat(profiles): de-emphasize AI suggestions and fix multi-tenant support UI/UX Improvements for AI-skeptical users: - Only show 'Ideas' button if AI is enabled AND configured - Renamed 'Suggest Profile' to 'Ideas' with lightbulb icon - Moved 'New Profile' button to primary position - Changed AI button styling from prominent purple to subtle gray - Updated modal title to 'Profile Ideas' with neutral language Multi-tenant bug fix: - ProfileSuggestionHandler now uses MultiTenantPersistence - Properly resolves tenant-specific persistence from request context - Fixes potential nil pointer panic in multi-tenant deployments - Existing profiles are now correctly loaded per-tenant for AI context Tests updated to use MultiTenantPersistence with org context injection. --- .../Settings/AgentProfilesPanel.tsx | 38 +++++++++++++------ .../Settings/SuggestProfileModal.tsx | 22 +++++------ .../__tests__/SuggestProfileModal.test.tsx | 6 +-- internal/api/config_profiles.go | 10 +---- internal/api/profile_suggestions.go | 30 +++++++++------ .../api/profile_suggestions_handlers_test.go | 33 ++++++++++------ 6 files changed, 82 insertions(+), 57 deletions(-) diff --git a/frontend-modern/src/components/Settings/AgentProfilesPanel.tsx b/frontend-modern/src/components/Settings/AgentProfilesPanel.tsx index 51cc11538..1797d13dd 100644 --- a/frontend-modern/src/components/Settings/AgentProfilesPanel.tsx +++ b/frontend-modern/src/components/Settings/AgentProfilesPanel.tsx @@ -3,6 +3,7 @@ import { useWebSocket } from '@/App'; import { Card } from '@/components/shared/Card'; import SettingsPanel from '@/components/shared/SettingsPanel'; import { AgentProfilesAPI, type AgentProfile, type AgentProfileAssignment, type ProfileSuggestion } from '@/api/agentProfiles'; +import { AIAPI } from '@/api/ai'; import { LicenseAPI } from '@/api/license'; import { notificationStore } from '@/stores/notifications'; import { logger } from '@/utils/logger'; @@ -15,7 +16,7 @@ import Trash2 from 'lucide-solid/icons/trash-2'; import Crown from 'lucide-solid/icons/crown'; import Users from 'lucide-solid/icons/users'; import Settings from 'lucide-solid/icons/settings'; -import Sparkles from 'lucide-solid/icons/sparkles'; +import Lightbulb from 'lucide-solid/icons/lightbulb'; export const AgentProfilesPanel: Component = () => { @@ -25,6 +26,9 @@ export const AgentProfilesPanel: Component = () => { const [hasFeature, setHasFeature] = createSignal(false); const [checkingLicense, setCheckingLicense] = createSignal(true); + // AI state - only show AI features if enabled + const [aiAvailable, setAiAvailable] = createSignal(false); + // Data state const [profiles, setProfiles] = createSignal([]); const [assignments, setAssignments] = createSignal([]); @@ -100,7 +104,7 @@ export const AgentProfilesPanel: Component = () => { } }; - // Check license on mount + // Check license and AI availability on mount onMount(async () => { try { const features = await LicenseAPI.getFeatures(); @@ -112,6 +116,15 @@ export const AgentProfilesPanel: Component = () => { setCheckingLicense(false); } + // Check if AI is available (enabled and configured) - silently fail if not + try { + const aiSettings = await AIAPI.getSettings(); + setAiAvailable(aiSettings.enabled && aiSettings.configured); + } catch { + // AI not available - that's fine, just hide the Ideas button + setAiAvailable(false); + } + if (hasFeature()) { await loadData(); } else { @@ -284,15 +297,6 @@ export const AgentProfilesPanel: Component = () => { bodyClass="space-y-4" action={
- + {/* Only show AI Ideas button if AI is enabled and configured */} + + +
} > diff --git a/frontend-modern/src/components/Settings/SuggestProfileModal.tsx b/frontend-modern/src/components/Settings/SuggestProfileModal.tsx index b895df095..73d22a9e2 100644 --- a/frontend-modern/src/components/Settings/SuggestProfileModal.tsx +++ b/frontend-modern/src/components/Settings/SuggestProfileModal.tsx @@ -4,7 +4,7 @@ import { notificationStore } from '@/stores/notifications'; import { logger } from '@/utils/logger'; import { formatRelativeTime } from '@/utils/format'; import { KNOWN_SETTINGS_BY_KEY } from './agentProfileSettings'; -import Sparkles from 'lucide-solid/icons/sparkles'; +import Lightbulb from 'lucide-solid/icons/lightbulb'; import AlertCircle from 'lucide-solid/icons/alert-circle'; import Check from 'lucide-solid/icons/check'; import Loader2 from 'lucide-solid/icons/loader-2'; @@ -242,15 +242,15 @@ export const SuggestProfileModal: Component = (props) {/* Header */}
-
- +
+

- Pulse Assistant Profile Suggestion + Profile Ideas

- Describe what you need, and Pulse Assistant will draft a profile + Describe what you need, and we'll help draft a profile

@@ -595,10 +595,10 @@ export const SuggestProfileModal: Component = (props) type="button" onClick={handleSubmit} disabled={loading() || !prompt().trim()} - class="inline-flex items-center gap-2 rounded-lg bg-purple-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-purple-700 disabled:cursor-not-allowed disabled:opacity-60" + class="inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60" > - - {loading() ? 'Generating...' : 'Suggest Profile'} + + {loading() ? 'Generating...' : 'Get Ideas'} } > @@ -606,11 +606,11 @@ export const SuggestProfileModal: Component = (props) type="button" onClick={handleSubmit} disabled={loading() || !prompt().trim()} - class="inline-flex items-center gap-2 rounded-lg bg-purple-100 px-4 py-2 text-sm font-medium text-purple-700 transition-colors hover:bg-purple-200 dark:bg-purple-900/40 dark:text-purple-200 dark:hover:bg-purple-900/60 disabled:cursor-not-allowed disabled:opacity-60" + class="inline-flex items-center gap-2 rounded-lg bg-gray-100 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 disabled:cursor-not-allowed disabled:opacity-60" title="Regenerate using the current prompt" > - - {loading() ? 'Generating...' : 'Regenerate draft'} + + {loading() ? 'Generating...' : 'Try Again'}