- Fix seed-products.ts TS error (implicit any) and BOM handling - Re-run full seed to restore correct Persian encoding in DB - Fix productService query→search param mismatch - Fix IngredientWiki hardcoded port 4000→use settingsStore - Restore seed-products-data.json from git after accidental corruption
197 lines
5.9 KiB
TypeScript
197 lines
5.9 KiB
TypeScript
"use client";
|
|
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
import { authService } from "../services/authService";
|
|
import api from "../services/api";
|
|
import { useCartStore } from "./cartStore";
|
|
import { usePetStore } from "./usePetStore";
|
|
|
|
|
|
|
|
export type UserRole = "User_Guest" | "User_PetOwner" | "User_Partner";
|
|
|
|
export interface Address {
|
|
id: string;
|
|
title: string;
|
|
receptorName: string;
|
|
phone: string;
|
|
province: string;
|
|
city: string;
|
|
detail: string;
|
|
zipCode: string;
|
|
isDefault: boolean;
|
|
}
|
|
|
|
export interface Transaction {
|
|
id: string;
|
|
type: 'top_up' | 'purchase';
|
|
amount: number;
|
|
date: string;
|
|
status: 'success' | 'failed' | 'pending';
|
|
}
|
|
|
|
interface UserProfile {
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
mobile: string;
|
|
walletBalance: number;
|
|
charityDonationTotal: number;
|
|
addresses: Address[];
|
|
transactions: Transaction[];
|
|
}
|
|
|
|
interface UserStore {
|
|
role: UserRole;
|
|
isLoggedIn: boolean;
|
|
profile: UserProfile;
|
|
setRole: (role: UserRole) => void;
|
|
setLoggedIn: (isLoggedIn: boolean) => void;
|
|
updateProfile: (profile: Partial<UserProfile>) => Promise<void>;
|
|
addAddress: (address: Address) => Promise<void>;
|
|
updateAddress: (id: string, address: Address) => Promise<void>;
|
|
deleteAddress: (id: string) => Promise<void>;
|
|
setDefaultAddress: (id: string) => Promise<void>;
|
|
topUpWallet: (amount: number) => Promise<void>;
|
|
logout: () => void;
|
|
fetchProfile: () => Promise<void>;
|
|
}
|
|
|
|
export const useUserStore = create<UserStore>()(
|
|
persist(
|
|
(set) => ({
|
|
role: "User_Guest",
|
|
isLoggedIn: false,
|
|
profile: {
|
|
firstName: "",
|
|
lastName: "",
|
|
email: "",
|
|
mobile: "",
|
|
walletBalance: 0,
|
|
charityDonationTotal: 0,
|
|
addresses: [],
|
|
transactions: [],
|
|
},
|
|
setRole: (role) => set({ role }),
|
|
setLoggedIn: (isLoggedIn) => set({ isLoggedIn }),
|
|
updateProfile: async (updates) => {
|
|
try {
|
|
const profileData = await authService.updateProfile(updates);
|
|
set((state) => ({
|
|
profile: {
|
|
...state.profile,
|
|
firstName: profileData.firstName,
|
|
lastName: profileData.lastName,
|
|
email: profileData.email,
|
|
mobile: profileData.mobile || "",
|
|
}
|
|
}));
|
|
} catch (error) {
|
|
console.error("Failed to update profile:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
addAddress: async (address) => {
|
|
try {
|
|
// Remove ID so backend generates UUID
|
|
const { id, ...addressData } = address;
|
|
await api.post('/users/addresses', addressData);
|
|
await useUserStore.getState().fetchProfile();
|
|
} catch (error) {
|
|
console.error("Failed to add address:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
updateAddress: async (id, updated) => {
|
|
try {
|
|
const { id: _, ...addressData } = updated;
|
|
await api.patch(`/users/addresses/${id}`, addressData);
|
|
await useUserStore.getState().fetchProfile();
|
|
} catch (error) {
|
|
console.error("Failed to update address:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
deleteAddress: async (id) => {
|
|
try {
|
|
await api.delete(`/users/addresses/${id}`);
|
|
await useUserStore.getState().fetchProfile();
|
|
} catch (error) {
|
|
console.error("Failed to delete address:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
setDefaultAddress: async (id) => {
|
|
try {
|
|
await api.patch(`/users/addresses/${id}/default`);
|
|
await useUserStore.getState().fetchProfile();
|
|
} catch (error) {
|
|
console.error("Failed to set default address:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
topUpWallet: async (amount) => {
|
|
try {
|
|
await api.post('/users/wallet/top-up', { amount });
|
|
await useUserStore.getState().fetchProfile();
|
|
} catch (error) {
|
|
console.error("Failed to top-up wallet:", error);
|
|
throw error;
|
|
}
|
|
},
|
|
logout: () => {
|
|
authService.logout();
|
|
set({ role: "User_Guest", isLoggedIn: false, profile: {
|
|
firstName: "",
|
|
lastName: "",
|
|
email: "",
|
|
mobile: "",
|
|
walletBalance: 0,
|
|
charityDonationTotal: 0,
|
|
addresses: [],
|
|
transactions: []
|
|
}});
|
|
},
|
|
fetchProfile: async () => {
|
|
try {
|
|
const profileData = await authService.getProfile();
|
|
set({
|
|
isLoggedIn: true,
|
|
role: (profileData.role as UserRole) || "User_PetOwner",
|
|
profile: {
|
|
firstName: profileData.firstName,
|
|
lastName: profileData.lastName,
|
|
email: profileData.email,
|
|
mobile: profileData.mobile || "",
|
|
walletBalance: Number(profileData.walletBalance),
|
|
charityDonationTotal: Number(profileData.charityDonationTotal),
|
|
addresses: (profileData as any).addresses || [],
|
|
transactions: (profileData as any).walletTransactions?.map((t: any) => ({
|
|
id: t.id,
|
|
type: t.type === 'deposit' ? 'top_up' : 'purchase',
|
|
amount: Number(t.amount),
|
|
date: t.createdAt,
|
|
status: t.status === 'completed' ? 'success' : t.status === 'failed' ? 'failed' : 'pending'
|
|
})) || []
|
|
}
|
|
});
|
|
|
|
// Sync orders list to CartStore
|
|
if (profileData.orders) {
|
|
useCartStore.getState().setOrders(profileData.orders);
|
|
}
|
|
// Sync pets list to PetStore
|
|
if (profileData.pets) {
|
|
usePetStore.getState().setPets(profileData.pets);
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to fetch user profile:", error);
|
|
authService.logout();
|
|
set({ isLoggedIn: false, role: "User_Guest" });
|
|
}
|
|
},
|
|
}),
|
|
{ name: "canina-user" }
|
|
)
|
|
);
|