feat: all 4 UX priorities — wizard, examples, onboarding, pipeline
- Offer wizard: 4→3 steps by merging 'Prüfen' into PDF review step - Search mask: quick-select chips for area ranges, budget, and city presets - Onboarding: WelcomeDialog (3 slides, localStorage) on first visit - Deal Pipeline: Kanban board (6 stages, 8 mock items, advance-to-next-stage) added to demand workspace navigation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Box, Button, CircularProgress, Typography } from '@mui/material'
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react'
|
||||
import { ArrowLeft, Send } from 'lucide-react'
|
||||
import { useOfferWizardStore } from '../../stores/offerWizardStore'
|
||||
import { offerService } from '../../services/offerService'
|
||||
import { useUpdateOfferField, useGeneratePdfPreview } from '../../hooks/useOffers'
|
||||
import { useUpdateOfferField, useGeneratePdfPreview, useMarkOfferChecked } from '../../hooks/useOffers'
|
||||
import { useToastStore } from '../../stores/toastStore'
|
||||
import { MockPdfPreview } from './MockPdfPreview'
|
||||
import { EditableOfferFieldList } from './EditableOfferFieldList'
|
||||
@@ -22,6 +21,7 @@ export function OfferPdfReviewStep() {
|
||||
|
||||
const updateFieldMut = useUpdateOfferField()
|
||||
const genPreview = useGeneratePdfPreview()
|
||||
const markChecked = useMarkOfferChecked()
|
||||
const showToast = useToastStore(s => s.showToast)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -66,13 +66,34 @@ export function OfferPdfReviewStep() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleNext = () => {
|
||||
const needTitle = useOfferWizardStore(s => s.needTitle)
|
||||
const addAttachment = useOfferWizardStore(s => s.addAttachment)
|
||||
const setMessageDraft = useOfferWizardStore(s => s.setMessageDraft)
|
||||
const setMessageSubject = useOfferWizardStore(s => s.setMessageSubject)
|
||||
|
||||
const handleNext = async () => {
|
||||
if (!offerDraftId) return
|
||||
if (!pdfReady) {
|
||||
showToast('PDF-Vorschau wird noch generiert...', 'info')
|
||||
return
|
||||
}
|
||||
setStep('checked')
|
||||
const res = await markChecked.mutateAsync(offerDraftId)
|
||||
if (res.error) {
|
||||
showToast(`Fehler: ${res.error}`, 'error')
|
||||
return
|
||||
}
|
||||
addAttachment({
|
||||
id: crypto.randomUUID(),
|
||||
fileName: `Angebot_${needTitle.replace(/\s+/g, '_')}.pdf`,
|
||||
fileType: 'application/pdf',
|
||||
fileSize: 320_000,
|
||||
generated: true,
|
||||
})
|
||||
setMessageSubject(`Passende Gewerbeflächen zu Ihrer Anfrage: ${needTitle}`)
|
||||
setMessageDraft(
|
||||
`Sehr geehrte Damen und Herren,\n\nbitte finden Sie anbei unser Angebot zu Ihrem Bedarf "${needTitle}". Gerne stehen wir für Rückfragen und Besichtigungstermine zur Verfügung.\n\nFreundliche Grüsse\nWincasa AG`,
|
||||
)
|
||||
setStep('send')
|
||||
}
|
||||
|
||||
if (loadingDraft || !draft) {
|
||||
@@ -153,12 +174,12 @@ export function OfferPdfReviewStep() {
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
endIcon={<ArrowRight size={14} />}
|
||||
endIcon={markChecked.isPending ? <CircularProgress size={14} sx={{ color: 'white' }} /> : <Send size={14} />}
|
||||
onClick={handleNext}
|
||||
disabled={!pdfReady}
|
||||
disabled={!pdfReady || markChecked.isPending}
|
||||
sx={{ textTransform: 'none', bgcolor: '#1e3a5f', '&:hover': { bgcolor: '#16304d' } }}
|
||||
>
|
||||
Angebot prüfen
|
||||
{markChecked.isPending ? 'Wird geprüft…' : 'Angebot senden →'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user