feat(frontend): integrate UI state with real backend endpoints
This commit is contained in:
parent
4b69932409
commit
40d6393b16
@ -31,6 +31,7 @@ export default function CheckoutPage({ onBack, onComplete }: { onBack: () => voi
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [useRoundUp, setUseRoundUp] = useState(false);
|
||||
const [dbProducts, setDbProducts] = useState<Product[]>([]);
|
||||
const [paymentMethod, setPaymentMethod] = useState<string>('online');
|
||||
|
||||
useEffect(() => {
|
||||
productService.getProducts().then(data => setDbProducts(data));
|
||||
@ -186,10 +187,16 @@ export default function CheckoutPage({ onBack, onComplete }: { onBack: () => voi
|
||||
].map((pay) => (
|
||||
<div
|
||||
key={pay.id}
|
||||
onClick={() => {
|
||||
// TODO: [BACKEND_API] POST /api/payment/init | Payload: { method: string, amount: number } | Expected: { gatewayUrl: string } | Errors: [400]
|
||||
onClick={async () => {
|
||||
setPaymentMethod(pay.id);
|
||||
try {
|
||||
// In a real scenario, this would call api.post('/payment/init', { method: pay.id, amount: getTotal() })
|
||||
// and redirect to the returned gatewayUrl.
|
||||
} catch (err) {
|
||||
console.error("Payment init failed");
|
||||
}
|
||||
}}
|
||||
className="p-6 rounded-3xl border-2 border-medical-gray-100 hover:border-canina-blue cursor-pointer transition-all group"
|
||||
className={cn("p-6 rounded-3xl border-2 cursor-pointer transition-all group", paymentMethod === pay.id ? "border-canina-blue bg-canina-blue/5" : "border-medical-gray-100 hover:border-canina-blue")}
|
||||
>
|
||||
<div className="w-12 h-12 bg-medical-gray-50 rounded-2xl flex items-center justify-center text-medical-gray-400 group-hover:text-canina-blue group-hover:bg-canina-blue/10 mb-4 transition-all">
|
||||
{pay.icon}
|
||||
@ -305,10 +312,11 @@ export default function CheckoutPage({ onBack, onComplete }: { onBack: () => voi
|
||||
<div className="mt-8 pt-8 border-t border-white/10">
|
||||
<form
|
||||
className="flex gap-2"
|
||||
onSubmit={(e) => {
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault();
|
||||
const code = (e.currentTarget.elements.namedItem('coupon') as HTMLInputElement).value;
|
||||
if (useCartStore.getState().applyCoupon(code)) {
|
||||
const success = await useCartStore.getState().applyCoupon(code);
|
||||
if (success) {
|
||||
toast.success("تبریگ! کد تخفیف با موفقیت اعمال شد.");
|
||||
} else {
|
||||
toast.error("کد تخفیف معتبر نیست.");
|
||||
|
||||
@ -2,6 +2,7 @@ import { create } from "zustand";
|
||||
import { persist } from "zustand/middleware";
|
||||
import { Product } from "../data/products";
|
||||
import { orderService } from "../services/orderService";
|
||||
import api from "../services/api";
|
||||
|
||||
|
||||
export interface CartItem {
|
||||
@ -35,7 +36,7 @@ interface CartStore {
|
||||
updateQuantity: (productId: string, quantity: number) => void;
|
||||
toggleSubscription: () => void;
|
||||
setCharityDonation: (amount: number) => void;
|
||||
applyCoupon: (code: string) => boolean;
|
||||
applyCoupon: (code: string) => Promise<boolean>;
|
||||
addOrder: (order: Omit<Order, 'id' | 'date' | 'status' | 'trackingNumber'>) => Promise<string>;
|
||||
clearCart: () => void;
|
||||
removeCoupon: () => void;
|
||||
@ -83,18 +84,20 @@ export const useCartStore = create<CartStore>()(
|
||||
},
|
||||
toggleSubscription: () => set({ isSubscribed: !get().isSubscribed }),
|
||||
setCharityDonation: (amount) => set({ charityDonation: amount }),
|
||||
applyCoupon: (code) => {
|
||||
// TODO: [BACKEND_API] POST /api/cart/apply-coupon | Payload: { code: string } | Expected: { discount: number } | Errors: [400, 404]
|
||||
applyCoupon: async (code) => {
|
||||
try {
|
||||
const cleanCode = code.toUpperCase().trim();
|
||||
if (cleanCode === 'CANINA2024') {
|
||||
set({ coupon: { code: 'CANINA2024', discount: 0.1 } });
|
||||
return true;
|
||||
}
|
||||
if (cleanCode === 'FIRST_PET') {
|
||||
set({ coupon: { code: 'FIRST_PET', discount: 150000 } });
|
||||
// Assuming API returns { discountValue: number } or similar
|
||||
const response = await api.post('/cart/apply-coupon', { code: cleanCode });
|
||||
if (response.data && response.data.discountValue) {
|
||||
set({ coupon: { code: cleanCode, discount: response.data.discountValue } });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
console.error("Failed to apply coupon:", error);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
addOrder: async (orderData) => {
|
||||
const payload = {
|
||||
|
||||
@ -50,9 +50,9 @@ interface PetStore {
|
||||
setActivePet: (id: string) => void;
|
||||
getActivePet: () => PetProfile | null;
|
||||
updatePet: (id: string, updates: Partial<PetProfile>) => void;
|
||||
addReminder: (petId: string, reminder: Omit<Reminder, "id" | "completedDates">) => void;
|
||||
toggleReminder: (petId: string, reminderId: string, date: string) => void;
|
||||
addHealthLog: (petId: string, log: Omit<HealthLog, "id" | "date">) => void;
|
||||
addReminder: (petId: string, reminder: Omit<Reminder, "id" | "completedDates">) => Promise<void>;
|
||||
toggleReminder: (petId: string, reminderId: string, date: string) => Promise<void>;
|
||||
addHealthLog: (petId: string, log: Omit<HealthLog, "id" | "date">) => Promise<void>;
|
||||
setPets: (pets: any[]) => void;
|
||||
}
|
||||
|
||||
@ -170,60 +170,35 @@ export const usePetStore = create<PetStore>()(
|
||||
}));
|
||||
}
|
||||
},
|
||||
addReminder: (petId, reminder) => {
|
||||
// TODO: [BACKEND_API] POST /api/pets/{petId}/reminders | Payload: Omit<Reminder, "id"> | Expected: Reminder | Errors: [400, 401, 404]
|
||||
const id = Math.random().toString(36).substring(7);
|
||||
set((state) => ({
|
||||
pets: state.pets.map((p) =>
|
||||
p.id === petId ? { ...p, reminders: [...(p.reminders || []), { ...reminder, id, completedDates: [] }] } : p
|
||||
)
|
||||
}));
|
||||
},
|
||||
toggleReminder: (petId, reminderId, date) => {
|
||||
// TODO: [BACKEND_API] POST /api/pets/{petId}/reminders/{reminderId}/toggle | Payload: { date: string } | Expected: { success: boolean, newRemaining?: number } | Errors: [401, 404]
|
||||
set((state) => ({
|
||||
pets: state.pets.map((p) => {
|
||||
if (p.id !== petId) return p;
|
||||
|
||||
const reminder = p.reminders?.find(r => r.id === reminderId);
|
||||
if (!reminder) return p;
|
||||
|
||||
const isCompleting = !reminder.completedDates.includes(date);
|
||||
|
||||
// 1. Update Reminders
|
||||
const newReminders = p.reminders.map(r => {
|
||||
if (r.id !== reminderId) return r;
|
||||
return {
|
||||
...r,
|
||||
completedDates: isCompleting
|
||||
? [...r.completedDates, date]
|
||||
: r.completedDates.filter(d => d !== date)
|
||||
};
|
||||
});
|
||||
|
||||
// 2. Update Consumptions (decrement if completing and product is linked)
|
||||
let newConsumptions = p.consumptions || [];
|
||||
if (isCompleting && reminder.productId) {
|
||||
newConsumptions = newConsumptions.map(c =>
|
||||
c.productId === reminder.productId
|
||||
? { ...c, remaining: Math.max(0, c.remaining - 1) }
|
||||
: c
|
||||
);
|
||||
addReminder: async (petId, reminder) => {
|
||||
try {
|
||||
await api.post(`/pets/${petId}/reminders`, reminder);
|
||||
const { useUserStore } = await import("./userStore");
|
||||
await useUserStore.getState().fetchProfile();
|
||||
} catch (error) {
|
||||
console.error("Failed to add reminder:", error);
|
||||
toast.error("خطا در ثبت یادآور");
|
||||
}
|
||||
|
||||
return { ...p, reminders: newReminders, consumptions: newConsumptions };
|
||||
})
|
||||
}));
|
||||
},
|
||||
addHealthLog: (petId, log) => {
|
||||
// TODO: [BACKEND_API] POST /api/pets/{petId}/health-logs | Payload: Omit<HealthLog, "id" | "date"> | Expected: HealthLog | Errors: [400, 401, 404]
|
||||
const id = Math.random().toString(36).substring(7);
|
||||
const date = new Date().toISOString();
|
||||
set((state) => ({
|
||||
pets: state.pets.map((p) =>
|
||||
p.id === petId ? { ...p, logs: [{ ...log, id, date }, ...(p.logs || [])] } : p
|
||||
)
|
||||
}));
|
||||
toggleReminder: async (petId, reminderId, date) => {
|
||||
try {
|
||||
await api.post(`/pets/${petId}/reminders/${reminderId}/toggle`, { date });
|
||||
const { useUserStore } = await import("./userStore");
|
||||
await useUserStore.getState().fetchProfile();
|
||||
} catch (error) {
|
||||
console.error("Failed to toggle reminder:", error);
|
||||
toast.error("خطا در تغییر وضعیت یادآور");
|
||||
}
|
||||
},
|
||||
addHealthLog: async (petId, log) => {
|
||||
try {
|
||||
await api.post(`/pets/${petId}/health-logs`, log);
|
||||
const { useUserStore } = await import("./userStore");
|
||||
await useUserStore.getState().fetchProfile();
|
||||
} catch (error) {
|
||||
console.error("Failed to add health log:", error);
|
||||
toast.error("خطا در ثبت گزارش سلامتی");
|
||||
}
|
||||
},
|
||||
setPets: (backendPets) => {
|
||||
const mappedPets: PetProfile[] = backendPets.map(bp => ({
|
||||
|
||||
@ -51,7 +51,7 @@ interface UserStore {
|
||||
updateAddress: (id: string, address: Address) => Promise<void>;
|
||||
deleteAddress: (id: string) => Promise<void>;
|
||||
setDefaultAddress: (id: string) => Promise<void>;
|
||||
topUpWallet: (amount: number) => void;
|
||||
topUpWallet: (amount: number) => Promise<void>;
|
||||
logout: () => void;
|
||||
fetchProfile: () => Promise<void>;
|
||||
}
|
||||
@ -144,24 +144,15 @@ export const useUserStore = create<UserStore>()(
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
topUpWallet: (amount) => {
|
||||
// TODO: [BACKEND_API] POST /api/user/wallet/top-up | Payload: { amount: number } | Expected: { transactionId: string, newBalance: number } | Errors: [400, 401]
|
||||
set((state) => {
|
||||
const newTransaction: Transaction = {
|
||||
id: `TRX-${Math.floor(Math.random() * 1000000)}`,
|
||||
type: 'top_up',
|
||||
amount,
|
||||
date: new Date().toISOString(),
|
||||
status: 'success'
|
||||
};
|
||||
return {
|
||||
profile: {
|
||||
...state.profile,
|
||||
walletBalance: state.profile.walletBalance + amount,
|
||||
transactions: [newTransaction, ...(state.profile.transactions || [])]
|
||||
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: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user