175 lines
5.1 KiB
TypeScript
175 lines
5.1 KiB
TypeScript
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
import type { TransferCategory, TransferCondition, TransferPricingSource } from './chuyen-nhuong-api';
|
|
|
|
// ─── Types ──────────────────────────────────────────────
|
|
|
|
export interface TransferItemDraft {
|
|
id: string; // client-side only
|
|
name: string;
|
|
brand?: string;
|
|
modelName?: string;
|
|
condition: TransferCondition;
|
|
purchaseYear?: number;
|
|
originalPriceVND?: number;
|
|
askingPriceVND: number;
|
|
quantity: number;
|
|
notes?: string;
|
|
}
|
|
|
|
export interface AiEstimate {
|
|
estimatedPriceVND: string;
|
|
confidence: number;
|
|
factors: unknown;
|
|
}
|
|
|
|
export interface AiEstimateResult {
|
|
estimates: AiEstimate[];
|
|
totalEstimateVND: string;
|
|
avgConfidence: number;
|
|
}
|
|
|
|
export interface TransferWizardState {
|
|
// Step tracking
|
|
currentStep: number;
|
|
|
|
// Step 1: Category
|
|
category: TransferCategory | null;
|
|
|
|
// Step 2: Items
|
|
items: TransferItemDraft[];
|
|
|
|
// Step 2 (premises): Additional fields
|
|
areaM2?: number;
|
|
monthlyRentVND?: number;
|
|
depositMonths?: number;
|
|
remainingLeaseMo?: number;
|
|
businessType?: string;
|
|
footTraffic?: string;
|
|
|
|
// Step 3: AI estimate
|
|
aiEstimate: AiEstimateResult | null;
|
|
isEstimating: boolean;
|
|
|
|
// Step 4: Review & submit
|
|
title: string;
|
|
description: string;
|
|
address: string;
|
|
ward: string;
|
|
district: string;
|
|
city: string;
|
|
contactName: string;
|
|
contactPhone: string;
|
|
askingPriceVND: number;
|
|
pricingSource: TransferPricingSource;
|
|
isNegotiable: boolean;
|
|
|
|
// Actions
|
|
setStep: (step: number) => void;
|
|
setCategory: (category: TransferCategory) => void;
|
|
addItem: (item: Omit<TransferItemDraft, 'id'>) => void;
|
|
updateItem: (id: string, item: Partial<TransferItemDraft>) => void;
|
|
removeItem: (id: string) => void;
|
|
setPremisesFields: (fields: Partial<Pick<TransferWizardState, 'areaM2' | 'monthlyRentVND' | 'depositMonths' | 'remainingLeaseMo' | 'businessType' | 'footTraffic'>>) => void;
|
|
setAiEstimate: (result: AiEstimateResult | null) => void;
|
|
setIsEstimating: (loading: boolean) => void;
|
|
setListingDetails: (details: Partial<Pick<TransferWizardState, 'title' | 'description' | 'address' | 'ward' | 'district' | 'city' | 'contactName' | 'contactPhone' | 'askingPriceVND' | 'pricingSource' | 'isNegotiable'>>) => void;
|
|
reset: () => void;
|
|
}
|
|
|
|
// ─── Initial state ──────────────────────────────────────
|
|
|
|
const initialState = {
|
|
currentStep: 0,
|
|
category: null as TransferCategory | null,
|
|
items: [] as TransferItemDraft[],
|
|
areaM2: undefined,
|
|
monthlyRentVND: undefined,
|
|
depositMonths: undefined,
|
|
remainingLeaseMo: undefined,
|
|
businessType: undefined,
|
|
footTraffic: undefined,
|
|
aiEstimate: null as AiEstimateResult | null,
|
|
isEstimating: false,
|
|
title: '',
|
|
description: '',
|
|
address: '',
|
|
ward: '',
|
|
district: '',
|
|
city: 'Hồ Chí Minh',
|
|
contactName: '',
|
|
contactPhone: '',
|
|
askingPriceVND: 0,
|
|
pricingSource: 'MANUAL' as TransferPricingSource,
|
|
isNegotiable: true,
|
|
};
|
|
|
|
// ─── Store ──────────────────────────────────────────────
|
|
|
|
let nextItemId = 1;
|
|
|
|
export const useTransferWizardStore = create<TransferWizardState>()(
|
|
persist(
|
|
(set) => ({
|
|
...initialState,
|
|
|
|
setStep: (step) => set({ currentStep: step }),
|
|
|
|
setCategory: (category) => set({ category }),
|
|
|
|
addItem: (item) => {
|
|
const id = `item-${nextItemId++}`;
|
|
set((state) => ({ items: [...state.items, { ...item, id }] }));
|
|
},
|
|
|
|
updateItem: (id, updates) =>
|
|
set((state) => ({
|
|
items: state.items.map((item) =>
|
|
item.id === id ? { ...item, ...updates } : item,
|
|
),
|
|
})),
|
|
|
|
removeItem: (id) =>
|
|
set((state) => ({ items: state.items.filter((item) => item.id !== id) })),
|
|
|
|
setPremisesFields: (fields) => set(fields),
|
|
|
|
setAiEstimate: (result) => set({ aiEstimate: result }),
|
|
|
|
setIsEstimating: (isEstimating) => set({ isEstimating }),
|
|
|
|
setListingDetails: (details) => set(details),
|
|
|
|
reset: () => {
|
|
nextItemId = 1;
|
|
set(initialState);
|
|
},
|
|
}),
|
|
{
|
|
name: 'goodgo-transfer-wizard',
|
|
partialize: (state) => ({
|
|
currentStep: state.currentStep,
|
|
category: state.category,
|
|
items: state.items,
|
|
areaM2: state.areaM2,
|
|
monthlyRentVND: state.monthlyRentVND,
|
|
depositMonths: state.depositMonths,
|
|
remainingLeaseMo: state.remainingLeaseMo,
|
|
businessType: state.businessType,
|
|
footTraffic: state.footTraffic,
|
|
title: state.title,
|
|
description: state.description,
|
|
address: state.address,
|
|
ward: state.ward,
|
|
district: state.district,
|
|
city: state.city,
|
|
contactName: state.contactName,
|
|
contactPhone: state.contactPhone,
|
|
askingPriceVND: state.askingPriceVND,
|
|
pricingSource: state.pricingSource,
|
|
isNegotiable: state.isNegotiable,
|
|
}),
|
|
},
|
|
),
|
|
);
|