feat: unified error handling + AI service modularisation
Error handling (Prompt 2): - src/services/errors.ts: AppError class, normalizeError(), throwServiceError() helper - 6 services wrapped with try/catch (property, match, need, shortlist, futureSignal, inquiry) - inquiryService aligned from custom ServiceResult<T> to standard ServiceResponse types - Results, MatchCenter, FutureAvailability pages show <ErrorState onRetry> on query failure AI modularisation (Prompt 3): - src/services/aiService.ts reduced from 755 → 19 lines (barrel re-export) - src/services/ai/IAIService.ts: typed interface + all response types - src/services/ai/mock/: needParser, compareBuilder, decisionBrief, listingParser, MockAIService - src/services/ai/openrouter/OpenRouterAIService.ts: model-agnostic skeleton - src/services/ai/prompts/: 4 prompt template files (needParsing, matchExplanation, compareSummary, decisionBrief) - src/services/ai/index.ts: factory selects Mock or OpenRouter via VITE_USE_REAL_AI flag - All existing import paths unchanged — zero call-site modifications Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
DEFAULT_SIGNAL_FILTERS,
|
||||
} from '../../components/future-signals'
|
||||
import { AddToShortlistDialog } from '../../components/shortlist'
|
||||
import { ErrorState } from '../../components/ui'
|
||||
import type { SignalFilterState } from '../../components/future-signals'
|
||||
import type { FutureSignal } from '../../domain/futureSignal'
|
||||
|
||||
@@ -29,7 +30,7 @@ function applyFilters(signals: FutureSignal[], f: SignalFilterState): FutureSign
|
||||
}
|
||||
|
||||
export default function FutureAvailability() {
|
||||
const { data: signals = [], isLoading } = useFutureSignals()
|
||||
const { data: signals = [], isLoading, isError, refetch } = useFutureSignals()
|
||||
const [filters, setFilters] = useState<SignalFilterState>(DEFAULT_SIGNAL_FILTERS)
|
||||
const [selectedSignal, setSelectedSignal] = useState<FutureSignal | null>(null)
|
||||
|
||||
@@ -102,6 +103,8 @@ export default function FutureAvailability() {
|
||||
<Box key={i} sx={{ p: 2, mb: 1, bgcolor: 'white', borderRadius: 1, height: 120 }} />
|
||||
))}
|
||||
</Box>
|
||||
) : isError ? (
|
||||
<ErrorState onRetry={refetch} />
|
||||
) : filtered.length === 0 ? (
|
||||
<FutureSignalEmptyState context={signals.length === 0 ? 'no-signals' : 'filtered-empty'} />
|
||||
) : (
|
||||
|
||||
@@ -15,6 +15,7 @@ import { useNeeds } from '../../hooks/useNeeds'
|
||||
import { useMatchCenterStore } from '../../stores/matchCenterStore'
|
||||
import { useToastStore } from '../../stores/toastStore'
|
||||
import { MatchListCard, MatchBriefingPanel, MatchCenterSkeleton } from '../../components/match-center'
|
||||
import { ErrorState } from '../../components/ui'
|
||||
import type { Match } from '../../domain/match'
|
||||
|
||||
const STRENGTH_OPTIONS = [
|
||||
@@ -32,7 +33,7 @@ const STATUS_OPTIONS = [
|
||||
]
|
||||
|
||||
export default function MatchCenter() {
|
||||
const { data: matches = [], isLoading } = useMatches()
|
||||
const { data: matches = [], isLoading, isError, refetch } = useMatches()
|
||||
const { data: properties = [] } = useProperties()
|
||||
const { data: needs = [] } = useNeeds()
|
||||
const { setSelectedProperty, setSelectedNeed } = useMatchCenterStore()
|
||||
@@ -141,6 +142,8 @@ export default function MatchCenter() {
|
||||
<Box sx={{ flex: 1, overflowY: 'auto', bgcolor: '#f8fafc' }}>
|
||||
{isLoading ? (
|
||||
<MatchCenterSkeleton />
|
||||
) : isError ? (
|
||||
<ErrorState onRetry={refetch} />
|
||||
) : filtered.length === 0 ? (
|
||||
<Box sx={{ p: 6, textAlign: 'center' }}>
|
||||
<Typography color="text.secondary">Keine Matches für diese Filter.</Typography>
|
||||
|
||||
Reference in New Issue
Block a user