refactor(arch): eliminate direct service calls in components — route all through hooks
New hook files: - useAuth.ts: useLogin, useSwitchDemoRole, useSwitchOrganization - useAI.ts: useParseNeed, useGenerateOfferEmail, useGenerateDecisionBrief, useParseListingText - useAssistant.ts: useAssistantSuggestions (useQuery), useAssistantAnswer (useMutation) - useOfferReport.ts: useOfferReportByInquiry, useCreateOfferReport, useUpdateOfferReport, useGenerateOfferReportPdf - useInquiryReport.ts: useInquiryReportByInquiry, useCreateInquiryReport, useUpdateInquiryReport, useFinalizeInquiryReport - useMarketReport.ts: useMarketReport - useWeighting.ts: useDefaultWeights (synchronous wrapper) - useUnitMatches.ts: useUnitMatchesMap, useUnitBundle, useBundleMatches (useMemo wrappers) Extended hooks: useProperties (add update/create/remove mutations), useNeeds (add useCreateNeed), useMatches (add useNeedMatchesForProperty, useAdditionalMatchesForInquiry), useReviewQueue (add useCreateReviewTask) Updated 21 components/pages: all direct service imports replaced with hooks. Deliberate exception: getRecommendedActions in DataQuality.tsx (pure sync utility, no provider access). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,13 +5,13 @@ import { useLocation } from 'react-router'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useAssistantStore } from '../../stores/assistantStore'
|
||||
import { useSessionStore } from '../../stores/sessionStore'
|
||||
import { aiAssistantService } from '../../services/aiAssistantService'
|
||||
import { useAssistantSuggestions, useAssistantAnswer } from '../../hooks/useAssistant'
|
||||
import { AssistantContextSummary } from './AssistantContextSummary'
|
||||
import { AssistantMessageList } from './AssistantMessageList'
|
||||
import { AssistantPromptSuggestions } from './AssistantPromptSuggestions'
|
||||
import { AssistantLoadingState } from './AssistantLoadingState'
|
||||
import { AssistantErrorState } from './AssistantErrorState'
|
||||
import type { AssistantContext, SuggestedQuestion } from '../../domain/assistant'
|
||||
import type { AssistantContext } from '../../domain/assistant'
|
||||
import type { WorkspaceType } from '../../domain/enums'
|
||||
|
||||
function resolveWorkspace(pathname: string): WorkspaceType | null {
|
||||
@@ -27,24 +27,13 @@ export function GlobalAIAssistantDrawer() {
|
||||
const { currentUser } = useSessionStore()
|
||||
const location = useLocation()
|
||||
|
||||
const [suggestions, setSuggestions] = useState<SuggestedQuestion[]>([])
|
||||
const [inputText, setInputText] = useState('')
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Build context from route when drawer opens
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
const ctx: AssistantContext = {
|
||||
currentRoute: location.pathname,
|
||||
workspace: resolveWorkspace(location.pathname),
|
||||
userRole: currentUser?.role ?? 'VIEWER',
|
||||
organizationId: currentUser?.organizationId ?? '',
|
||||
}
|
||||
setContext(ctx)
|
||||
aiAssistantService.getSuggestions(ctx).then(setSuggestions)
|
||||
}, [isOpen, location.pathname])
|
||||
const answerQuestion = useAssistantAnswer()
|
||||
const { data: suggestions = [] } = useAssistantSuggestions(isOpen ? context : null)
|
||||
|
||||
// Refresh suggestions when route changes while open
|
||||
// Build context from route when drawer opens or route changes while open
|
||||
useEffect(() => {
|
||||
if (!isOpen) return
|
||||
const ctx: AssistantContext = {
|
||||
@@ -54,8 +43,7 @@ export function GlobalAIAssistantDrawer() {
|
||||
organizationId: currentUser?.organizationId ?? '',
|
||||
}
|
||||
setContext(ctx)
|
||||
aiAssistantService.getSuggestions(ctx).then(setSuggestions)
|
||||
}, [location.pathname])
|
||||
}, [isOpen, location.pathname])
|
||||
|
||||
// Auto-scroll on new messages
|
||||
useEffect(() => {
|
||||
@@ -64,7 +52,7 @@ export function GlobalAIAssistantDrawer() {
|
||||
}
|
||||
}, [messages, isLoading])
|
||||
|
||||
const handleQuestion = useCallback(async (question: string) => {
|
||||
const handleQuestion = useCallback((question: string) => {
|
||||
if (!question.trim() || isLoading) return
|
||||
setInputText('')
|
||||
setError(null)
|
||||
@@ -78,26 +66,32 @@ export function GlobalAIAssistantDrawer() {
|
||||
addMessage(userMsg)
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const ctx = context ?? {
|
||||
currentRoute: location.pathname,
|
||||
workspace: resolveWorkspace(location.pathname),
|
||||
userRole: currentUser?.role ?? 'VIEWER',
|
||||
organizationId: currentUser?.organizationId ?? '',
|
||||
}
|
||||
const answer = await aiAssistantService.answerQuestion(ctx, question)
|
||||
addMessage({
|
||||
id: crypto.randomUUID(),
|
||||
role: 'assistant',
|
||||
createdAt: new Date().toISOString(),
|
||||
...answer,
|
||||
})
|
||||
} catch {
|
||||
setError('Antwort konnte nicht generiert werden. Bitte erneut versuchen.')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
const ctx = context ?? {
|
||||
currentRoute: location.pathname,
|
||||
workspace: resolveWorkspace(location.pathname),
|
||||
userRole: currentUser?.role ?? 'VIEWER',
|
||||
organizationId: currentUser?.organizationId ?? '',
|
||||
}
|
||||
}, [context, isLoading, location.pathname, currentUser, addMessage, setLoading, setError])
|
||||
|
||||
answerQuestion.mutate(
|
||||
{ context: ctx, question },
|
||||
{
|
||||
onSuccess: (answer) => {
|
||||
addMessage({
|
||||
id: crypto.randomUUID(),
|
||||
role: 'assistant',
|
||||
createdAt: new Date().toISOString(),
|
||||
...answer,
|
||||
})
|
||||
setLoading(false)
|
||||
},
|
||||
onError: () => {
|
||||
setError('Antwort konnte nicht generiert werden. Bitte erneut versuchen.')
|
||||
setLoading(false)
|
||||
},
|
||||
},
|
||||
)
|
||||
}, [context, isLoading, location.pathname, currentUser, addMessage, setLoading, setError, answerQuestion])
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
@@ -108,10 +102,6 @@ export function GlobalAIAssistantDrawer() {
|
||||
|
||||
const handleClear = () => {
|
||||
clearConversation()
|
||||
setSuggestions([])
|
||||
if (context) {
|
||||
aiAssistantService.getSuggestions(context).then(setSuggestions)
|
||||
}
|
||||
}
|
||||
|
||||
const showSuggestions = suggestions.length > 0 && messages.length === 0
|
||||
|
||||
Reference in New Issue
Block a user