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:
@@ -1,10 +1,9 @@
|
||||
import { useState } from 'react'
|
||||
import { Box, Chip, CircularProgress, Divider, Switch, TextField, Typography } from '@mui/material'
|
||||
import { Clock, Layers, ShieldCheck, Target, TrendingUp, Users, Zap } from 'lucide-react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import type { Property } from '../../domain/property'
|
||||
import { MockupUnitProvider } from '../../provider/MockupUnitProvider'
|
||||
import { propertyService } from '../../services/propertyService'
|
||||
import { useUpdateProperty } from '../../hooks/useProperties'
|
||||
import { useToastStore } from '../../stores/toastStore'
|
||||
import { DS_BORDER, DS_PRE_MARKET, DS_SURFACE, DS_TEXT } from '../../lib/ds'
|
||||
import { floorLabel, SectionTitle } from './PropertyDetailHelpers'
|
||||
@@ -14,7 +13,6 @@ export const MOCK_TODAY = new Date('2026-05-20')
|
||||
export function PreMarketPanel({ p }: { p: Property }) {
|
||||
const [enabled, setEnabled] = useState(p.schattenmarktRelease?.enabled ?? false)
|
||||
const [leadTimeMonths, setLeadTimeMonths] = useState(p.schattenmarktRelease?.leadTimeMonths ?? 6)
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [unitStates, setUnitStates] = useState<Record<string, { enabled: boolean; availableFrom: string }>>(() => {
|
||||
const init: Record<string, { enabled: boolean; availableFrom: string }> = {}
|
||||
for (const u of p.units ?? []) {
|
||||
@@ -26,8 +24,9 @@ export function PreMarketPanel({ p }: { p: Property }) {
|
||||
return init
|
||||
})
|
||||
const [unitSaving, setUnitSaving] = useState<Record<string, boolean>>({})
|
||||
const queryClient = useQueryClient()
|
||||
const updateProperty = useUpdateProperty()
|
||||
const showToast = useToastStore(s => s.showToast)
|
||||
const saving = updateProperty.isPending
|
||||
|
||||
if (p.resultType !== 'VERIFIED_PORTFOLIO') return null
|
||||
if (!p.leaseEndDate && !p.breakoutOptionDate) return null
|
||||
@@ -54,18 +53,18 @@ export function PreMarketPanel({ p }: { p: Property }) {
|
||||
(['Zürich', 'Basel', 'Bern', 'Zug'].some(c => (p.location?.city ?? '').includes(c)) ? 4 : 1))
|
||||
const highQualityLeads = Math.max(1, Math.floor(demandProfiles * 0.38))
|
||||
|
||||
async function save(nextEnabled: boolean, nextLeadTime: number) {
|
||||
setSaving(true)
|
||||
try {
|
||||
await propertyService.update(p.id, { schattenmarktRelease: { enabled: nextEnabled, leadTimeMonths: nextLeadTime } })
|
||||
await queryClient.invalidateQueries({ queryKey: ['property', p.id] })
|
||||
await queryClient.invalidateQueries({ queryKey: ['properties'] })
|
||||
showToast(nextEnabled ? 'Pre-Market Matching aktiviert.' : 'Pre-Market Matching deaktiviert.', 'success')
|
||||
} catch {
|
||||
showToast('Fehler beim Speichern.', 'error')
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
function save(nextEnabled: boolean, nextLeadTime: number) {
|
||||
updateProperty.mutate(
|
||||
{ id: p.id, input: { schattenmarktRelease: { enabled: nextEnabled, leadTimeMonths: nextLeadTime } } },
|
||||
{
|
||||
onSuccess: () => {
|
||||
showToast(nextEnabled ? 'Pre-Market Matching aktiviert.' : 'Pre-Market Matching deaktiviert.', 'success')
|
||||
},
|
||||
onError: () => {
|
||||
showToast('Fehler beim Speichern.', 'error')
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
function handleToggle(_: React.ChangeEvent<HTMLInputElement>, checked: boolean) {
|
||||
|
||||
Reference in New Issue
Block a user