Files
goodgo-platform/apps/web/lib/transfer-wizard-store.ts

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,
}),
},
),
);