@codesinger0/shared-components 1.1.85 → 1.1.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/dist/components 2/AccessibilityMenu.jsx +0 -474
- package/dist/components 2/AdvantagesList.jsx +0 -89
- package/dist/components 2/ArticlesList.jsx +0 -269
- package/dist/components 2/DualTextCard.jsx +0 -73
- package/dist/components 2/FloatingWhatsAppButton.jsx +0 -180
- package/dist/components 2/FullscreenCarousel.jsx +0 -292
- package/dist/components 2/Hero.jsx +0 -198
- package/dist/components 2/IconGrid.jsx +0 -144
- package/dist/components 2/IntroSection.jsx +0 -74
- package/dist/components 2/LargeItemCard.jsx +0 -267
- package/dist/components 2/MasonryItemCard.jsx +0 -247
- package/dist/components 2/Menu.d.ts +0 -26
- package/dist/components 2/Menu.jsx +0 -268
- package/dist/components 2/MyOrdersDisplay.jsx +0 -311
- package/dist/components 2/QAAccordion.jsx +0 -212
- package/dist/components 2/SmallItemCard.jsx +0 -152
- package/dist/components 2/SmallItemsGrid.jsx +0 -313
- package/dist/components 2/TextListCards.jsx +0 -107
- package/dist/components 2/ToastProvider.jsx +0 -38
- package/dist/components 2/UnderConstruction.jsx +0 -76
- package/dist/components 2/VideoCard.jsx +0 -88
- package/dist/components 2/cart/CartItem.jsx +0 -101
- package/dist/components 2/cart/FloatingCartButton.jsx +0 -49
- package/dist/components 2/cart/OrderForm.jsx +0 -960
- package/dist/components 2/cart/ShoppingCartModal.jsx +0 -229
- package/dist/components 2/clubMembership/ClubMembershipModal.jsx +0 -289
- package/dist/components 2/clubMembership/ClubPromoModal.jsx +0 -108
- package/dist/components 2/elements/CTAButton.jsx +0 -17
- package/dist/components 2/elements/FixedWidthHeroVideo.jsx +0 -92
- package/dist/components 2/elements/ImageLightbox.jsx +0 -112
- package/dist/components 2/elements/RoundButton.jsx +0 -44
- package/dist/components 2/elements/SmallButton.jsx +0 -35
- package/dist/components 2/elements/Toast.jsx +0 -37
- package/dist/components 2/elements/VideoLightbox.jsx +0 -76
- package/dist/components 2/modals/ItemDetailsModal.jsx +0 -192
- package/dist/components 2/products/CategoryList.jsx +0 -24
- package/dist/components 2/products/PriceRangeSlider.jsx +0 -162
- package/dist/components 2/products/ProductsDisplay.jsx +0 -40
- package/dist/components 2/products/ProductsSidebar.jsx +0 -46
- package/dist/components 2/products/SubcategorySection.jsx +0 -37
- package/dist/context 2/CartContext.jsx +0 -165
- package/dist/context 2/ItemModalContext.jsx +0 -40
- package/dist/hooks 2/useScrollLock.js +0 -52
- package/dist/index 2.js +0 -45
- package/dist/integrations 2/emailService.js +0 -167
- package/dist/styles 2/shared-components.css +0 -29
- package/dist/utils 2/ScrollManager.jsx +0 -85
- package/dist/utils 2/ScrollToTop.jsx +0 -14
|
@@ -1,960 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import ClubMembershipModal from '../clubMembership/ClubMembershipModal'
|
|
3
|
-
import ClubPromoModal from '../clubMembership/ClubPromoModal'
|
|
4
|
-
import { ArrowRight, Truck, Store, Crown, AlertCircle, Loader2, Calendar } from 'lucide-react';
|
|
5
|
-
import { BookOpen } from 'lucide-react';
|
|
6
|
-
|
|
7
|
-
const AVAILABLE_CITIES = [
|
|
8
|
-
'פתח תקווה',
|
|
9
|
-
'רמת גן',
|
|
10
|
-
'גבעתיים',
|
|
11
|
-
'בני ברק',
|
|
12
|
-
'תל אביב',
|
|
13
|
-
'ראש העין',
|
|
14
|
-
'קרית אונו',
|
|
15
|
-
'אור יהודה',
|
|
16
|
-
'יהוד מונסון'
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
const validateFullName = (name) => {
|
|
20
|
-
if (!name || !name.trim()) {
|
|
21
|
-
return 'שם מלא חובה';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const words = name.trim().split(/\s+/);
|
|
25
|
-
if (words.length < 2) {
|
|
26
|
-
return 'יש להזין שם פרטי ומשפחה';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return null;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const OrderForm = ({
|
|
33
|
-
onBack,
|
|
34
|
-
courseData,
|
|
35
|
-
workshopData,
|
|
36
|
-
PaymentModalComponent,
|
|
37
|
-
isAuthenticated, // useAuth hook provides this
|
|
38
|
-
currentUserData, updateUserProfile, updateClubMembership, //useUserContext provides this
|
|
39
|
-
createOrder, updateOrder, // useOrders hook provides this
|
|
40
|
-
settings, settingsLoading, // useSettings hook provides this
|
|
41
|
-
onNavigateToLogin,
|
|
42
|
-
addToast, // Inject toast function
|
|
43
|
-
cartItems,
|
|
44
|
-
totalPrice,
|
|
45
|
-
businessName,
|
|
46
|
-
paymentLinkCreationURL,
|
|
47
|
-
scrollContainerRef
|
|
48
|
-
}) => {
|
|
49
|
-
|
|
50
|
-
const [nameError, setNameError] = useState('');
|
|
51
|
-
const [deliveryError, setDeliveryError] = useState('');
|
|
52
|
-
const [clubPromoModalOpen, setClubPromoModalOpen] = useState(false);
|
|
53
|
-
const [clubMembershipModalOpen, setClubMembershipModalOpen] = useState(false);
|
|
54
|
-
const [clubInfoVisible, setClubInfoVisible] = useState(false);
|
|
55
|
-
|
|
56
|
-
// Course and Workshop detection
|
|
57
|
-
const isCoursePurchase = !!courseData;
|
|
58
|
-
const isWorkshopPurchase = !!workshopData;
|
|
59
|
-
const isDigitalPurchase = isCoursePurchase; // Only courses are digital
|
|
60
|
-
const isUserClubMember = currentUserData?.isClubMember || false;
|
|
61
|
-
const shouldShowClubMembership = !isUserClubMember;
|
|
62
|
-
const clubDiscountPercentage = settings?.clubDiscountPercentage || 10;
|
|
63
|
-
|
|
64
|
-
const items = isCoursePurchase ? [courseData] : isWorkshopPurchase ? [workshopData] : cartItems;
|
|
65
|
-
const itemsTotal = isCoursePurchase
|
|
66
|
-
? (courseData.discountPrice || courseData.price)
|
|
67
|
-
: isWorkshopPurchase
|
|
68
|
-
? (workshopData.discountPrice || workshopData.price)
|
|
69
|
-
: totalPrice;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const [paymentModalOpen, setPaymentModalOpen] = useState(false);
|
|
73
|
-
const [paymentData, setPaymentData] = useState(null);
|
|
74
|
-
const [createdOrderId, setCreatedOrderId] = useState(null);
|
|
75
|
-
const [submitError, setSubmitError] = useState('');
|
|
76
|
-
const [formData, setFormData] = useState({
|
|
77
|
-
name: '',
|
|
78
|
-
email: '',
|
|
79
|
-
phone: '',
|
|
80
|
-
deliveryMethod: isDigitalPurchase || isWorkshopPurchase ? 'digital' : 'delivery',
|
|
81
|
-
deliveryCity: '',
|
|
82
|
-
deliveryAddress: ''
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
86
|
-
const [errors, setErrors] = useState({});
|
|
87
|
-
|
|
88
|
-
const localScrollContainerRef = useRef(null);
|
|
89
|
-
const activeScrollContainerRef = scrollContainerRef || localScrollContainerRef;
|
|
90
|
-
|
|
91
|
-
const scrollToAnchor = (fieldName) => {
|
|
92
|
-
// Small delay to ensure error messages are rendered
|
|
93
|
-
setTimeout(() => {
|
|
94
|
-
const element = document.getElementById(`${fieldName}`);
|
|
95
|
-
const container = activeScrollContainerRef.current;
|
|
96
|
-
|
|
97
|
-
if (element && container) {
|
|
98
|
-
// Get element position relative to container
|
|
99
|
-
const elementTop = element.offsetTop;
|
|
100
|
-
const containerHeight = container.clientHeight;
|
|
101
|
-
const elementHeight = element.offsetHeight;
|
|
102
|
-
|
|
103
|
-
// Calculate scroll position to center the element
|
|
104
|
-
const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
|
|
105
|
-
|
|
106
|
-
// Smooth scroll within the container
|
|
107
|
-
container.scrollTo({
|
|
108
|
-
top: Math.max(0, scrollTo),
|
|
109
|
-
behavior: 'smooth'
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}, 100);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const minDeliveryAmount = settings.minDeliveryAmount;
|
|
116
|
-
const deliveryFee = settings.deliveryFee;
|
|
117
|
-
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
if (currentUserData) {
|
|
120
|
-
setFormData(prev => ({
|
|
121
|
-
...prev,
|
|
122
|
-
name: currentUserData.getFullName() || '',
|
|
123
|
-
email: currentUserData.email || '',
|
|
124
|
-
phone: currentUserData.phone || '',
|
|
125
|
-
deliveryCity: currentUserData.address ? '' : prev.deliveryCity,
|
|
126
|
-
deliveryMethod: isCoursePurchase ? 'digital' : prev.deliveryMethod
|
|
127
|
-
}));
|
|
128
|
-
}
|
|
129
|
-
}, [currentUserData, isCoursePurchase]);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const generateSimpleOrderNumber = () => {
|
|
133
|
-
return Math.floor(Math.random() * 9000000) + 1000000; // 7-digit number
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
const createPaymentLink = async (orderData) => {
|
|
137
|
-
const finalTotal = orderData.totalAmount;
|
|
138
|
-
|
|
139
|
-
// Map cart items to products data format
|
|
140
|
-
const productsData = orderData.items.map((item) => ({
|
|
141
|
-
businessName,
|
|
142
|
-
catalogNumber: item.id,
|
|
143
|
-
name: item.name,
|
|
144
|
-
price: isUserClubMember ? item.price * (1 - clubDiscountPercentage / 100) : item.price,
|
|
145
|
-
quantity: item.quantity,
|
|
146
|
-
minQuantity: item.quantity,
|
|
147
|
-
...(item.image_url && { url: encodeURIComponent(item.image_url) })
|
|
148
|
-
}));
|
|
149
|
-
|
|
150
|
-
// Add delivery fee as separate product if applicable
|
|
151
|
-
if (orderData.deliveryMethod === 'delivery' && deliveryFee > 0) {
|
|
152
|
-
productsData.push({
|
|
153
|
-
catalogNumber: 'delivery_fee',
|
|
154
|
-
name: 'דמי משלוח',
|
|
155
|
-
price: orderData.deliveryFee,
|
|
156
|
-
quantity: 1,
|
|
157
|
-
minQuantity: 1
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const response = await fetch(paymentLinkCreationURL, {
|
|
162
|
-
method: 'POST',
|
|
163
|
-
headers: {
|
|
164
|
-
'Content-Type': 'application/json'
|
|
165
|
-
},
|
|
166
|
-
body: JSON.stringify({
|
|
167
|
-
businessName: 'confectioneryShop',
|
|
168
|
-
operation: 'createPaymentLink',
|
|
169
|
-
customerName: orderData.customerName,
|
|
170
|
-
customerPhone: orderData.customerPhone,
|
|
171
|
-
price: finalTotal,
|
|
172
|
-
orderNumber: orderData.simpleOrderNumber,
|
|
173
|
-
products: {
|
|
174
|
-
data: productsData
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const result = await response.json();
|
|
180
|
-
|
|
181
|
-
if (result.success) {
|
|
182
|
-
return {
|
|
183
|
-
success: true,
|
|
184
|
-
data: {
|
|
185
|
-
paymentUrl: result.data.paymentUrl,
|
|
186
|
-
paymentId: result.data.paymentId,
|
|
187
|
-
expiresAt: result.data.expiresAt,
|
|
188
|
-
simpleOrderNumber: orderData.simpleOrderNumber
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
} else {
|
|
192
|
-
return {
|
|
193
|
-
success: false,
|
|
194
|
-
error: result.error || 'שגיאה ביצירת קישור התשלום'
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
const simulatePaymentLinkCreation = async (simpleOrderNumber) => {
|
|
200
|
-
// 50/50 success/failure simulation
|
|
201
|
-
const isSuccess = Math.random() > 0.01;
|
|
202
|
-
|
|
203
|
-
// Simulate API delay
|
|
204
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
205
|
-
|
|
206
|
-
if (isSuccess) {
|
|
207
|
-
return {
|
|
208
|
-
success: true,
|
|
209
|
-
data: {
|
|
210
|
-
paymentLink: `https://mock-payment-link.com/pay/${simpleOrderNumber}`,
|
|
211
|
-
paymentId: `pay_${Date.now()}`,
|
|
212
|
-
simpleOrderNumber,
|
|
213
|
-
expiresAt: new Date(Date.now() + 5 * 60 * 1000) // 5 minutes
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
} else {
|
|
217
|
-
const errors = [
|
|
218
|
-
"שגיאת רשת - אנא נסו שוב",
|
|
219
|
-
"פרטי התשלום לא תקינים",
|
|
220
|
-
"השרת אינו זמין כרגע",
|
|
221
|
-
"שגיאה באימות הנתונים",
|
|
222
|
-
"תקלה זמנית - אנא נסו שוב מאוחר יותר"
|
|
223
|
-
];
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
error: errors[Math.floor(Math.random() * errors.length)]
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const validateForm = () => {
|
|
232
|
-
const newErrors = {};
|
|
233
|
-
let firstErrorField = null;
|
|
234
|
-
|
|
235
|
-
if (!formData.name.trim()) {
|
|
236
|
-
newErrors.name = 'שם מלא חובה';
|
|
237
|
-
if (!firstErrorField) firstErrorField = 'name';
|
|
238
|
-
} else {
|
|
239
|
-
const words = formData.name.trim().split(/\s+/);
|
|
240
|
-
if (words.length < 2) {
|
|
241
|
-
newErrors.name = 'יש להזין שם פרטי ומשפחה';
|
|
242
|
-
if (!firstErrorField) firstErrorField = 'name';
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (!formData.email.trim()) {
|
|
247
|
-
newErrors.email = 'כתובת אימייל חובה';
|
|
248
|
-
if (!firstErrorField) firstErrorField = 'email';
|
|
249
|
-
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
|
|
250
|
-
newErrors.email = 'כתובת אימייל לא תקינה';
|
|
251
|
-
if (!firstErrorField) firstErrorField = 'email';
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (!formData.phone.trim()) {
|
|
255
|
-
newErrors.phone = 'מספר טלפון חובה';
|
|
256
|
-
if (!firstErrorField) firstErrorField = 'phone';
|
|
257
|
-
} else if (!/^(05\d{8}|5\d{8})$/.test(formData.phone.replace(/\D/g, ''))) {
|
|
258
|
-
newErrors.phone = 'מספר טלפון לא תקין';
|
|
259
|
-
if (!firstErrorField) firstErrorField = 'phone';
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Only validate delivery for physical delivery method
|
|
263
|
-
if (formData.deliveryMethod === 'delivery') {
|
|
264
|
-
const subtotal = (isCoursePurchase || isWorkshopPurchase) ? itemsTotal : totalPrice;
|
|
265
|
-
const clubDiscount = isUserClubMember ? (subtotal * (clubDiscountPercentage / 100)) : 0;
|
|
266
|
-
const currentTotal = subtotal - clubDiscount;
|
|
267
|
-
|
|
268
|
-
if (currentTotal < minDeliveryAmount) {
|
|
269
|
-
newErrors.delivery = `מינימום הזמנה למשלוח הוא ₪${minDeliveryAmount}`;
|
|
270
|
-
if (!firstErrorField) firstErrorField = 'delivery';
|
|
271
|
-
}
|
|
272
|
-
if (!formData.deliveryCity) {
|
|
273
|
-
newErrors.deliveryCity = 'בחירת עיר חובה';
|
|
274
|
-
if (!firstErrorField) firstErrorField = 'deliveryCity';
|
|
275
|
-
}
|
|
276
|
-
if (!formData.deliveryAddress.trim()) {
|
|
277
|
-
newErrors.deliveryAddress = 'כתובת משלוח חובה';
|
|
278
|
-
if (!firstErrorField) firstErrorField = 'deliveryAddress';
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
setErrors(newErrors);
|
|
283
|
-
return {
|
|
284
|
-
isValid: Object.keys(newErrors).length === 0,
|
|
285
|
-
firstErrorField
|
|
286
|
-
};
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
const grantCourseAccess = async (userId, courseIds) => {
|
|
290
|
-
try {
|
|
291
|
-
if (!currentUserData) return false;
|
|
292
|
-
const existingAccess = currentUserData.coursesAccess || [];
|
|
293
|
-
const newAccess = [...new Set([...existingAccess, ...courseIds])];
|
|
294
|
-
await updateUserProfile(currentUserData, { coursesAccess: newAccess });
|
|
295
|
-
return true;
|
|
296
|
-
} catch (error) {
|
|
297
|
-
console.error('Error granting course access:', error);
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const handleInputChange = (field, value) => {
|
|
303
|
-
if (field === 'deliveryMethod' && value === 'pickup') {
|
|
304
|
-
setDeliveryError('')
|
|
305
|
-
setErrors({})
|
|
306
|
-
}
|
|
307
|
-
setFormData(prev => ({ ...prev, [field]: value }));
|
|
308
|
-
if (errors[field]) {
|
|
309
|
-
setErrors(prev => ({ ...prev, [field]: '' }));
|
|
310
|
-
}
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
const handleClubButtonClick = () => {
|
|
314
|
-
if (!isAuthenticated()) {
|
|
315
|
-
setClubInfoVisible(true);
|
|
316
|
-
setClubPromoModalOpen(true);
|
|
317
|
-
addToast('יש להתחבר כדי להצטרף למועדון הלקוחות', 'info');
|
|
318
|
-
} else if (!isUserClubMember) {
|
|
319
|
-
setClubMembershipModalOpen(true);
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const handleLoginRedirect = () => {
|
|
324
|
-
setClubPromoModalOpen(false);
|
|
325
|
-
setClubMembershipModalOpen(false);
|
|
326
|
-
setClubInfoVisible(false);
|
|
327
|
-
if (onBack) {
|
|
328
|
-
onBack(true)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
onNavigateToLogin();
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const handleOnBack = () => {
|
|
335
|
-
onBack();
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const handleMembershipComplete = () => {
|
|
339
|
-
// Component will re-render with updated user data
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
const handleSubmit = async (e) => {
|
|
343
|
-
e.preventDefault();
|
|
344
|
-
debugger
|
|
345
|
-
// Validate name before submission
|
|
346
|
-
const nameValidationError = validateFullName(formData.name);
|
|
347
|
-
if (nameValidationError) {
|
|
348
|
-
setNameError(nameValidationError);
|
|
349
|
-
scrollToAnchor('name-field');
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Clear previous errors
|
|
354
|
-
setNameError('');
|
|
355
|
-
setDeliveryError('');
|
|
356
|
-
|
|
357
|
-
const validation = validateForm();
|
|
358
|
-
if (!validation.isValid) {
|
|
359
|
-
// Scroll to the first field with an error
|
|
360
|
-
if (validation.firstErrorField) {
|
|
361
|
-
scrollToAnchor(`${validation.firstErrorField}-field`);
|
|
362
|
-
}
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const subtotal = (isCoursePurchase || isWorkshopPurchase) ? itemsTotal : totalPrice;
|
|
367
|
-
const clubDiscount = isUserClubMember ? (subtotal * (clubDiscountPercentage / 100)) : 0;
|
|
368
|
-
const currentTotal = subtotal - clubDiscount;
|
|
369
|
-
|
|
370
|
-
// Check minimum delivery amount
|
|
371
|
-
if (formData.deliveryMethod === 'delivery') {
|
|
372
|
-
if (currentTotal < minDeliveryAmount) {
|
|
373
|
-
setDeliveryError(`מינימום הזמנה למשלוח הוא ₪${minDeliveryAmount}`);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
setIsSubmitting(true);
|
|
379
|
-
setSubmitError('');
|
|
380
|
-
|
|
381
|
-
// Calculate delivery cost - only charge if order meets minimum
|
|
382
|
-
const discountedSubtotal = subtotal - clubDiscount;
|
|
383
|
-
const deliveryCost = formData.deliveryMethod === 'delivery' && discountedSubtotal >= minDeliveryAmount ? deliveryFee : 0;
|
|
384
|
-
const grandTotal = discountedSubtotal + deliveryCost;
|
|
385
|
-
|
|
386
|
-
try {
|
|
387
|
-
const orderItems = isCoursePurchase || isWorkshopPurchase ? items : cartItems.map(item => ({
|
|
388
|
-
id: item.id,
|
|
389
|
-
name: item.name || item.title,
|
|
390
|
-
price: item.price,
|
|
391
|
-
quantity: item.quantity,
|
|
392
|
-
totalPrice: item.price * item.quantity,
|
|
393
|
-
}));
|
|
394
|
-
|
|
395
|
-
// Create order with payment_pending status
|
|
396
|
-
const orderData = {
|
|
397
|
-
simpleOrderNumber: generateSimpleOrderNumber(),
|
|
398
|
-
customerName: formData.name,
|
|
399
|
-
customerEmail: formData.email,
|
|
400
|
-
customerPhone: formData.phone,
|
|
401
|
-
deliveryMethod: formData.deliveryMethod,
|
|
402
|
-
deliveryCity: formData.deliveryMethod === 'delivery' ? formData.deliveryCity : null,
|
|
403
|
-
deliveryAddress: formData.deliveryMethod === 'delivery' ? formData.deliveryAddress : null,
|
|
404
|
-
items: isCoursePurchase ? [{
|
|
405
|
-
id: courseData.id,
|
|
406
|
-
name: courseData.title,
|
|
407
|
-
price: courseData.discountPrice || courseData.price,
|
|
408
|
-
quantity: 1,
|
|
409
|
-
totalPrice: courseData.discountPrice || courseData.price,
|
|
410
|
-
type: 'course'
|
|
411
|
-
}] : isWorkshopPurchase ? [{
|
|
412
|
-
id: workshopData.id,
|
|
413
|
-
name: workshopData.title,
|
|
414
|
-
price: workshopData.discountPrice || workshopData.price,
|
|
415
|
-
quantity: 1,
|
|
416
|
-
totalPrice: workshopData.discountPrice || workshopData.price,
|
|
417
|
-
type: 'workshop',
|
|
418
|
-
date: workshopData.date,
|
|
419
|
-
time: workshopData.time,
|
|
420
|
-
duration: workshopData.duration
|
|
421
|
-
}] : orderItems,
|
|
422
|
-
deliveryFee: formData.deliveryMethod === 'delivery' ? deliveryFee : 0,
|
|
423
|
-
totalAmount: grandTotal,
|
|
424
|
-
paymentStatus: 'payment_pending',
|
|
425
|
-
orderType: isCoursePurchase ? 'course' : isWorkshopPurchase ? 'workshop' : 'product'
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
// Create payment link
|
|
429
|
-
const paymentResponse = await createPaymentLink(orderData);
|
|
430
|
-
// const paymentResponse = await simulatePaymentLinkCreation(orderData);
|
|
431
|
-
|
|
432
|
-
if (paymentResponse.success) {
|
|
433
|
-
// Create order items in the format expected by the database
|
|
434
|
-
const orderItemsForDB = orderData.items.map((item) => ({
|
|
435
|
-
product_id: item.id,
|
|
436
|
-
product_name: item.name,
|
|
437
|
-
quantity: item.quantity,
|
|
438
|
-
unit_price: item.price,
|
|
439
|
-
total_price: item.price * item.quantity
|
|
440
|
-
}));
|
|
441
|
-
|
|
442
|
-
// Add complete order data with payment info
|
|
443
|
-
const completeOrderData = {
|
|
444
|
-
...orderData,
|
|
445
|
-
items: orderItemsForDB,
|
|
446
|
-
paymentId: paymentResponse.data.paymentId,
|
|
447
|
-
paymentLink: paymentResponse.data.paymentUrl,
|
|
448
|
-
paymentExpiresAt: paymentResponse.data.expiresAt,
|
|
449
|
-
paymentStatus: 'link_created'
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const newOrder = await createOrder(completeOrderData);
|
|
453
|
-
setCreatedOrderId(newOrder.id);
|
|
454
|
-
|
|
455
|
-
setPaymentData({
|
|
456
|
-
paymentLink: paymentResponse.data.paymentUrl,
|
|
457
|
-
paymentId: paymentResponse.data.paymentId,
|
|
458
|
-
expiresAt: paymentResponse.data.expiresAt,
|
|
459
|
-
simpleOrderNumber: paymentResponse.data.simpleOrderNumber
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
setPaymentModalOpen(true);
|
|
463
|
-
|
|
464
|
-
// Add course access for authenticated users
|
|
465
|
-
if (isCoursePurchase && isAuthenticated()) {
|
|
466
|
-
await grantCourseAccess(currentUserData.id, [courseData.id]);
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
setSubmitError(paymentResponse.error);
|
|
470
|
-
}
|
|
471
|
-
} catch (error) {
|
|
472
|
-
console.error('Order submission error:', error);
|
|
473
|
-
setSubmitError('שגיאה ביצירת ההזמנה או קישור התשלום. אנא נסו שוב.');
|
|
474
|
-
} finally {
|
|
475
|
-
setIsSubmitting(false);
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
|
-
|
|
479
|
-
if ((!cartItems || cartItems.length === 0) && !isCoursePurchase && !isWorkshopPurchase) {
|
|
480
|
-
return (
|
|
481
|
-
<div className="text-center py-12">
|
|
482
|
-
<h2 className="title mb-4">העגלה ריקה</h2>
|
|
483
|
-
<p className="subtitle">אין פריטים בעגלת הקניות</p>
|
|
484
|
-
</div>
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (settingsLoading) {
|
|
489
|
-
return (
|
|
490
|
-
<div className="min-h-screen bg-main flex items-center justify-center">
|
|
491
|
-
<div className="text-center">
|
|
492
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto mb-4"></div>
|
|
493
|
-
<p className="content-text">טוען הגדרות...</p>
|
|
494
|
-
</div>
|
|
495
|
-
</div>
|
|
496
|
-
);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const subtotal = (isCoursePurchase || isWorkshopPurchase) ? itemsTotal : totalPrice;
|
|
500
|
-
const clubDiscount = isUserClubMember ? (subtotal * (clubDiscountPercentage / 100)) : 0;
|
|
501
|
-
const discountedSubtotal = subtotal - clubDiscount;
|
|
502
|
-
const deliveryCost = formData.deliveryMethod === 'delivery' ?
|
|
503
|
-
(discountedSubtotal >= minDeliveryAmount ? deliveryFee : 0) : 0;
|
|
504
|
-
const grandTotal = discountedSubtotal + deliveryCost;
|
|
505
|
-
|
|
506
|
-
return (
|
|
507
|
-
<div
|
|
508
|
-
ref={scrollContainerRef ? null : localScrollContainerRef}
|
|
509
|
-
className={scrollContainerRef ? "min-h-screen bg-main py-12 px-4" : "h-screen overflow-y-auto bg-main py-12 px-4"}
|
|
510
|
-
dir="rtl"
|
|
511
|
-
>
|
|
512
|
-
<div className="max-w-2xl mx-auto">
|
|
513
|
-
|
|
514
|
-
{/* Back Button */}
|
|
515
|
-
<button
|
|
516
|
-
onClick={handleOnBack}
|
|
517
|
-
className="flex items-center gap-2 mb-6 content-text hover:text-primary transition-colors duration-200"
|
|
518
|
-
>
|
|
519
|
-
<ArrowRight className="w-4 h-4" />
|
|
520
|
-
חזור
|
|
521
|
-
</button>
|
|
522
|
-
|
|
523
|
-
<div className="glass-card p-8">
|
|
524
|
-
<h1 className="title text-center mb-8">
|
|
525
|
-
{isCoursePurchase ? 'רכישת קורס' : isWorkshopPurchase ? 'הרשמה לסדנה' : 'פרטים להזמנה'}
|
|
526
|
-
</h1>
|
|
527
|
-
|
|
528
|
-
{isCoursePurchase && (
|
|
529
|
-
<div className="glass-card p-4 border border-blue-200 mb-6">
|
|
530
|
-
<div className="flex items-center gap-2 text-blue-800">
|
|
531
|
-
<BookOpen className="w-5 h-5" />
|
|
532
|
-
<p className="content-text font-medium">
|
|
533
|
-
אתם רוכשים גישה דיגיטלית לקורס. לאחר השלמת התשלום תוכלו לגשת לקורס מיד.
|
|
534
|
-
</p>
|
|
535
|
-
</div>
|
|
536
|
-
</div>
|
|
537
|
-
)}
|
|
538
|
-
|
|
539
|
-
{isWorkshopPurchase && (
|
|
540
|
-
<div className="glass-card p-4 border border-green-200 mb-6">
|
|
541
|
-
<div className="flex items-center gap-2 text-green-800">
|
|
542
|
-
<Calendar className="w-5 h-5" />
|
|
543
|
-
<p className="content-text font-medium">
|
|
544
|
-
אתם נרשמים לסדנה פיזית. פרטי הסדנה יישלחו אליכם לאחר אישור ההרשמה.
|
|
545
|
-
</p>
|
|
546
|
-
</div>
|
|
547
|
-
</div>
|
|
548
|
-
)}
|
|
549
|
-
|
|
550
|
-
<div className={`space-y-6 ${isSubmitting ? 'pointer-events-none opacity-60' : ''}`}>
|
|
551
|
-
|
|
552
|
-
{/* Customer Details */}
|
|
553
|
-
<div className="space-y-4">
|
|
554
|
-
<h2 className="subtitle font-semibold">פרטים אישיים</h2>
|
|
555
|
-
|
|
556
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
557
|
-
<div id="name-field">
|
|
558
|
-
<label className="block subtitle font-medium mb-2">שם מלא *</label>
|
|
559
|
-
<input
|
|
560
|
-
type="text"
|
|
561
|
-
value={formData.name}
|
|
562
|
-
onChange={(e) => handleInputChange('name', e.target.value)}
|
|
563
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.name ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
564
|
-
}`}
|
|
565
|
-
placeholder="שם פרטי ומשפחה"
|
|
566
|
-
/>
|
|
567
|
-
{errors.name && (
|
|
568
|
-
<div className="flex items-center gap-2 mt-2 text-sm text-red-600">
|
|
569
|
-
<AlertCircle className="w-4 h-4" />
|
|
570
|
-
<span>{errors.name}</span>
|
|
571
|
-
</div>
|
|
572
|
-
)}
|
|
573
|
-
{nameError && (
|
|
574
|
-
<div className="flex items-center gap-2 mt-2 p-2 bg-red-50 border border-red-200 rounded">
|
|
575
|
-
<AlertCircle className="w-4 h-4 text-red-600" />
|
|
576
|
-
<span className="content-text text-red-700">{nameError}</span>
|
|
577
|
-
</div>
|
|
578
|
-
)}
|
|
579
|
-
</div>
|
|
580
|
-
|
|
581
|
-
<div id="phone-field">
|
|
582
|
-
<label className="block subtitle font-medium mb-2">טלפון *</label>
|
|
583
|
-
<input
|
|
584
|
-
type="tel"
|
|
585
|
-
value={formData.phone}
|
|
586
|
-
onChange={(e) => handleInputChange('phone', e.target.value)}
|
|
587
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.phone ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
588
|
-
}`}
|
|
589
|
-
placeholder="050-1234567"
|
|
590
|
-
dir="ltr"
|
|
591
|
-
/>
|
|
592
|
-
{errors.phone && (
|
|
593
|
-
<div className="flex items-center gap-2 mt-2 text-sm text-red-600">
|
|
594
|
-
<AlertCircle className="w-4 h-4" />
|
|
595
|
-
<span>{errors.phone}</span>
|
|
596
|
-
</div>
|
|
597
|
-
)}
|
|
598
|
-
</div>
|
|
599
|
-
</div>
|
|
600
|
-
|
|
601
|
-
<div id="email-field">
|
|
602
|
-
<label className="block subtitle font-medium mb-2">אימייל *</label>
|
|
603
|
-
<input
|
|
604
|
-
type="email"
|
|
605
|
-
value={formData.email}
|
|
606
|
-
onChange={(e) => handleInputChange('email', e.target.value)}
|
|
607
|
-
disabled={!!currentUserData?.email}
|
|
608
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.email ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
609
|
-
} ${currentUserData?.email ? 'bg-gray-100' : ''}`}
|
|
610
|
-
placeholder="example@email.com"
|
|
611
|
-
dir="ltr"
|
|
612
|
-
/>
|
|
613
|
-
{errors.email && (
|
|
614
|
-
<div className="flex items-center gap-2 mt-2 text-sm text-red-600">
|
|
615
|
-
<AlertCircle className="w-4 h-4" />
|
|
616
|
-
<span>{errors.email}</span>
|
|
617
|
-
</div>
|
|
618
|
-
)}
|
|
619
|
-
</div>
|
|
620
|
-
</div>
|
|
621
|
-
|
|
622
|
-
{/* Delivery Method */}
|
|
623
|
-
{!isCoursePurchase && !isWorkshopPurchase && (<div className="space-y-4">
|
|
624
|
-
<h2 className="subtitle font-semibold">שיטת קבלת ההזמנה</h2>
|
|
625
|
-
|
|
626
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
627
|
-
<label className={`glass-card p-4 cursor-pointer border-2 transition-colors duration-200 ${formData.deliveryMethod === 'delivery'
|
|
628
|
-
? 'border-gray-300 bg-opacity-20'
|
|
629
|
-
: 'border-transparent hover:border-gray-100'
|
|
630
|
-
}`}>
|
|
631
|
-
<input
|
|
632
|
-
type="radio"
|
|
633
|
-
name="deliveryMethod"
|
|
634
|
-
value="delivery"
|
|
635
|
-
checked={formData.deliveryMethod === 'delivery'}
|
|
636
|
-
onChange={(e) => handleInputChange('deliveryMethod', e.target.value)}
|
|
637
|
-
className="sr-only"
|
|
638
|
-
/>
|
|
639
|
-
<div className="flex items-center gap-3">
|
|
640
|
-
<Truck className="w-5 h-5 text-primary" />
|
|
641
|
-
<div>
|
|
642
|
-
<div className="subtitle font-medium">משלוח עד הבית</div>
|
|
643
|
-
<div className="content-text">מינימום ₪{minDeliveryAmount} • דמי משלוח ₪{deliveryFee}</div>
|
|
644
|
-
</div>
|
|
645
|
-
</div>
|
|
646
|
-
</label>
|
|
647
|
-
|
|
648
|
-
<label className={`glass-card p-4 cursor-pointer border-2 transition-colors duration-200 ${formData.deliveryMethod === 'pickup'
|
|
649
|
-
? 'border-gray-300 bg-opacity-20'
|
|
650
|
-
: 'border-transparent hover:border-gray-100'
|
|
651
|
-
}`}>
|
|
652
|
-
<input
|
|
653
|
-
type="radio"
|
|
654
|
-
name="deliveryMethod"
|
|
655
|
-
value="pickup"
|
|
656
|
-
checked={formData.deliveryMethod === 'pickup'}
|
|
657
|
-
onChange={(e) => handleInputChange('deliveryMethod', e.target.value)}
|
|
658
|
-
className="sr-only"
|
|
659
|
-
/>
|
|
660
|
-
<div className="flex items-center gap-3">
|
|
661
|
-
<Store className="w-5 h-5 text-primary" />
|
|
662
|
-
<div>
|
|
663
|
-
<div className="subtitle font-medium">איסוף עצמי</div>
|
|
664
|
-
<div className="content-text">ללא דמי משלוח</div>
|
|
665
|
-
</div>
|
|
666
|
-
</div>
|
|
667
|
-
</label>
|
|
668
|
-
</div>
|
|
669
|
-
|
|
670
|
-
{errors.delivery && (
|
|
671
|
-
<div className="flex items-center gap-2 text-sm text-red-600">
|
|
672
|
-
<AlertCircle className="w-4 h-4" />
|
|
673
|
-
<span>{errors.delivery}</span>
|
|
674
|
-
</div>
|
|
675
|
-
)}
|
|
676
|
-
{deliveryError && (
|
|
677
|
-
<div className="flex items-center gap-2 mt-2 p-2 bg-red-50 border border-red-200 rounded">
|
|
678
|
-
<AlertCircle className="w-4 h-4 text-red-600" />
|
|
679
|
-
<span className="content-text text-red-700">{deliveryError}</span>
|
|
680
|
-
</div>
|
|
681
|
-
)}
|
|
682
|
-
</div>)}
|
|
683
|
-
|
|
684
|
-
{/* Address */}
|
|
685
|
-
{(formData.deliveryMethod === 'delivery' && !(isCoursePurchase || isWorkshopPurchase)) && (
|
|
686
|
-
<div className="space-y-4 border-t border-gray-200 pt-6">
|
|
687
|
-
<h2 className="subtitle font-semibold">כתובת</h2>
|
|
688
|
-
|
|
689
|
-
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
690
|
-
<div id="deliveryCity-field">
|
|
691
|
-
<label className="block subtitle font-medium mb-2">עיר *</label>
|
|
692
|
-
{(isCoursePurchase || isWorkshopPurchase) ? (
|
|
693
|
-
<input
|
|
694
|
-
type="text"
|
|
695
|
-
value={formData.deliveryCity}
|
|
696
|
-
onChange={(e) => handleInputChange('deliveryCity', e.target.value)}
|
|
697
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.deliveryCity ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
698
|
-
}`}
|
|
699
|
-
placeholder="הזן עיר"
|
|
700
|
-
/>
|
|
701
|
-
) : (
|
|
702
|
-
<select
|
|
703
|
-
value={formData.deliveryCity}
|
|
704
|
-
onChange={(e) => handleInputChange('deliveryCity', e.target.value)}
|
|
705
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.deliveryCity ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
706
|
-
}`}
|
|
707
|
-
>
|
|
708
|
-
<option value="">בחר עיר</option>
|
|
709
|
-
{AVAILABLE_CITIES.map((city) => (
|
|
710
|
-
<option key={city} value={city}>{city}</option>
|
|
711
|
-
))}
|
|
712
|
-
</select>
|
|
713
|
-
)}
|
|
714
|
-
{errors.deliveryCity && (
|
|
715
|
-
<div className="flex items-center gap-2 mt-2 text-sm text-red-600">
|
|
716
|
-
<AlertCircle className="w-4 h-4" />
|
|
717
|
-
<span>{errors.deliveryCity}</span>
|
|
718
|
-
</div>
|
|
719
|
-
)}
|
|
720
|
-
</div>
|
|
721
|
-
|
|
722
|
-
<div id="deliveryAddress-field">
|
|
723
|
-
<label className="block subtitle font-medium mb-2">רחוב ומספר בית *</label>
|
|
724
|
-
<input
|
|
725
|
-
type="text"
|
|
726
|
-
value={formData.deliveryAddress}
|
|
727
|
-
onChange={(e) => handleInputChange('deliveryAddress', e.target.value)}
|
|
728
|
-
className={`w-full p-3 border rounded-lg focus:ring-2 focus:ring-primary transition-colors duration-200 ${errors.deliveryAddress ? 'border-red-500' : 'border-gray-300 focus:border-primary'
|
|
729
|
-
}`}
|
|
730
|
-
placeholder="רחוב 123, דירה 4"
|
|
731
|
-
/>
|
|
732
|
-
{errors.deliveryAddress && (
|
|
733
|
-
<div className="flex items-center gap-2 mt-2 text-sm text-red-600">
|
|
734
|
-
<AlertCircle className="w-4 h-4" />
|
|
735
|
-
<span>{errors.deliveryAddress}</span>
|
|
736
|
-
</div>
|
|
737
|
-
)}
|
|
738
|
-
</div>
|
|
739
|
-
</div>
|
|
740
|
-
|
|
741
|
-
{formData.deliveryMethod === 'delivery' && (
|
|
742
|
-
<div className="glass-card p-4 bg-yellow-50 border border-yellow-200">
|
|
743
|
-
<p className="content-text text-yellow-800">
|
|
744
|
-
<strong>שים לב:</strong> אנחנו מבצעים משלוחים רק לערים המופיעות ברשימה.
|
|
745
|
-
</p>
|
|
746
|
-
</div>
|
|
747
|
-
)}
|
|
748
|
-
</div>
|
|
749
|
-
)}
|
|
750
|
-
|
|
751
|
-
{/* Order Summary */}
|
|
752
|
-
<div className="glass-card p-6 bg-opacity-50 border-t border-gray-200">
|
|
753
|
-
<h2 className="subtitle font-semibold mb-4">סיכום ההזמנה</h2>
|
|
754
|
-
|
|
755
|
-
<div className="space-y-3 mb-4">
|
|
756
|
-
{isCoursePurchase ? (
|
|
757
|
-
// Course Purchase Summary
|
|
758
|
-
<div className="flex justify-between items-start content-text py-3 border-b border-gray-200">
|
|
759
|
-
<div className="flex-1">
|
|
760
|
-
<div className="flex items-center gap-2 mb-1">
|
|
761
|
-
<BookOpen className="w-4 h-4 text-primary" />
|
|
762
|
-
<span className="content-text font-medium">{courseData.title}</span>
|
|
763
|
-
</div>
|
|
764
|
-
<p className="text-xs mt-1 line-clamp-2">
|
|
765
|
-
{courseData.description}
|
|
766
|
-
</p>
|
|
767
|
-
<div className="text-xs text-primary mt-2 bg-blue-50 px-2 py-1 rounded inline-block">
|
|
768
|
-
גישה דיגיטלית מיידית
|
|
769
|
-
</div>
|
|
770
|
-
</div>
|
|
771
|
-
<div className="text-left ml-4">
|
|
772
|
-
{courseData.discountPrice && courseData.discountPrice < courseData.price ? (
|
|
773
|
-
<div>
|
|
774
|
-
<div className="text-sm line-through">
|
|
775
|
-
₪{courseData.price.toFixed(2)}
|
|
776
|
-
</div>
|
|
777
|
-
<div className="font-medium text-primary">
|
|
778
|
-
₪{courseData.discountPrice.toFixed(2)}
|
|
779
|
-
</div>
|
|
780
|
-
</div>
|
|
781
|
-
) : (
|
|
782
|
-
<div className="font-medium">₪{courseData.price.toFixed(2)}</div>
|
|
783
|
-
)}
|
|
784
|
-
</div>
|
|
785
|
-
</div>
|
|
786
|
-
) : isWorkshopPurchase ? (
|
|
787
|
-
// Workshop Purchase Summary
|
|
788
|
-
<div className="flex justify-between items-start content-text py-3 border-b border-gray-200">
|
|
789
|
-
<div className="flex-1">
|
|
790
|
-
<div className="flex items-center gap-2 mb-1">
|
|
791
|
-
<Calendar className="w-4 h-4 text-primary" />
|
|
792
|
-
<span className="content-text font-medium">{workshopData.title}</span>
|
|
793
|
-
</div>
|
|
794
|
-
<p className="text-xs mt-1 line-clamp-2">
|
|
795
|
-
{workshopData.description}
|
|
796
|
-
</p>
|
|
797
|
-
<div className="flex items-center gap-4 mt-2 text-xs">
|
|
798
|
-
<div className="flex items-center gap-1">
|
|
799
|
-
<Calendar className="w-3 h-3" />
|
|
800
|
-
<span>{workshopData.date}</span>
|
|
801
|
-
</div>
|
|
802
|
-
<div className="flex items-center gap-1">
|
|
803
|
-
<span>{workshopData.time}</span>
|
|
804
|
-
</div>
|
|
805
|
-
<div className="flex items-center gap-1">
|
|
806
|
-
<span>({workshopData.duration})</span>
|
|
807
|
-
</div>
|
|
808
|
-
</div>
|
|
809
|
-
{workshopData.location && (
|
|
810
|
-
<div className="flex items-center gap-1 mt-1 text-xs">
|
|
811
|
-
<MapPin className="w-3 h-3" />
|
|
812
|
-
<span>{workshopData.location}</span>
|
|
813
|
-
</div>
|
|
814
|
-
)}
|
|
815
|
-
</div>
|
|
816
|
-
<div className="text-left ml-4">
|
|
817
|
-
{workshopData.discountPrice && workshopData.discountPrice < workshopData.price ? (
|
|
818
|
-
<div>
|
|
819
|
-
<div className="text-sm line-through">
|
|
820
|
-
₪{workshopData.price.toFixed(2)}
|
|
821
|
-
</div>
|
|
822
|
-
<div className="font-medium text-primary">
|
|
823
|
-
₪{workshopData.discountPrice.toFixed(2)}
|
|
824
|
-
</div>
|
|
825
|
-
</div>
|
|
826
|
-
) : (
|
|
827
|
-
<div className="font-medium">₪{workshopData.price.toFixed(2)}</div>
|
|
828
|
-
)}
|
|
829
|
-
</div>
|
|
830
|
-
</div>
|
|
831
|
-
) : (
|
|
832
|
-
// Regular Cart Items Summary
|
|
833
|
-
cartItems.map((item) => (
|
|
834
|
-
<div key={item.id} className="flex justify-between items-center content-text py-2 border-b border-gray-200 last:border-b-0">
|
|
835
|
-
<div className="flex items-center gap-2">
|
|
836
|
-
<span className="content-text font-medium">{item.name}</span>
|
|
837
|
-
<span className="content-text">× {item.quantity}</span>
|
|
838
|
-
</div>
|
|
839
|
-
<span className="font-medium">₪{(item.price * item.quantity).toFixed(2)}</span>
|
|
840
|
-
</div>
|
|
841
|
-
))
|
|
842
|
-
)}
|
|
843
|
-
</div>
|
|
844
|
-
|
|
845
|
-
<div className="p-4 rounded-lg space-y-2">
|
|
846
|
-
<div className="flex justify-between content-text">
|
|
847
|
-
<span>סה״כ מוצרים:</span>
|
|
848
|
-
<span>₪{subtotal.toFixed(2)}</span>
|
|
849
|
-
</div>
|
|
850
|
-
|
|
851
|
-
{clubDiscount > 0 && (
|
|
852
|
-
<div className="flex justify-between content-text text-green-600">
|
|
853
|
-
<span>הנחת מועדון לקוחות ({clubDiscountPercentage}%):</span>
|
|
854
|
-
<span>-₪{clubDiscount.toFixed(2)}</span>
|
|
855
|
-
</div>
|
|
856
|
-
)}
|
|
857
|
-
|
|
858
|
-
{formData.deliveryMethod === 'delivery' && (
|
|
859
|
-
<div className="flex justify-between content-text">
|
|
860
|
-
<span>דמי משלוח:</span>
|
|
861
|
-
<span>₪{deliveryCost.toFixed(2)}</span>
|
|
862
|
-
</div>
|
|
863
|
-
)}
|
|
864
|
-
|
|
865
|
-
<div className="flex justify-between subtitle font-semibold text-lg border-t pt-2">
|
|
866
|
-
<span>סה״כ לתשלום:</span>
|
|
867
|
-
<span className="text-primary">₪{grandTotal.toFixed(2)}</span>
|
|
868
|
-
</div>
|
|
869
|
-
</div>
|
|
870
|
-
</div>
|
|
871
|
-
|
|
872
|
-
{shouldShowClubMembership && (
|
|
873
|
-
<>
|
|
874
|
-
<div className="glass-card p-4 bg-gradient-to-r from-primary/10 to-primary-dark/20 border border-primary/30">
|
|
875
|
-
<div className="flex items-center gap-3 mb-3">
|
|
876
|
-
<Crown className="w-6 h-6 text-yellow-600" />
|
|
877
|
-
<h3 className="subtitle font-bold">הצטרף למועדון הלקוחות וחסוך {clubDiscountPercentage}%!</h3>
|
|
878
|
-
</div>
|
|
879
|
-
<p className="content-text mb-3">
|
|
880
|
-
חברי מועדון הלקוחות זוכים ב-{clubDiscountPercentage}% הנחה על כל הזמנה + עדכונים על מבצעים מיוחדים
|
|
881
|
-
</p>
|
|
882
|
-
<button
|
|
883
|
-
type="button"
|
|
884
|
-
className="btn-secondary w-full flex items-center justify-center gap-2"
|
|
885
|
-
onClick={handleClubButtonClick}
|
|
886
|
-
>
|
|
887
|
-
<Crown className="w-4 h-4" />
|
|
888
|
-
{!isAuthenticated() ? 'התחבר והירשם למועדון' : 'הירשם למועדון הלקוחות'}
|
|
889
|
-
</button>
|
|
890
|
-
</div>
|
|
891
|
-
{clubInfoVisible && !isAuthenticated() && (
|
|
892
|
-
<div className="mt-2 p-2 bg-blue-50 border border-blue-200 rounded text-sm text-blue-700">
|
|
893
|
-
יש להתחבר כדי להצטרף למועדון הלקוחות
|
|
894
|
-
</div>
|
|
895
|
-
)}
|
|
896
|
-
</>
|
|
897
|
-
)}
|
|
898
|
-
|
|
899
|
-
{/* Submit Button */}
|
|
900
|
-
<button
|
|
901
|
-
type="button"
|
|
902
|
-
onClick={handleSubmit}
|
|
903
|
-
disabled={isSubmitting || Object.keys(errors).some(key => errors[key])}
|
|
904
|
-
className="w-full btn-primary py-4 text-big font-medium transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
905
|
-
>
|
|
906
|
-
{isSubmitting ? (
|
|
907
|
-
<div className="flex items-center justify-center gap-2">
|
|
908
|
-
<Loader2 className="w-5 h-5 animate-spin" />
|
|
909
|
-
יוצר הזמנה...
|
|
910
|
-
</div>
|
|
911
|
-
) : (
|
|
912
|
-
isSubmitting ? 'יוצר קישור תשלום...' : 'שלח הזמנה'
|
|
913
|
-
)}
|
|
914
|
-
</button>
|
|
915
|
-
|
|
916
|
-
{submitError && (
|
|
917
|
-
<div className="flex items-center gap-2 mt-4 p-3 bg-red-50 border border-red-200 rounded-lg">
|
|
918
|
-
<AlertCircle className="w-5 h-5 text-red-600" />
|
|
919
|
-
<span className="content-text text-red-700">{submitError}</span>
|
|
920
|
-
</div>
|
|
921
|
-
)}
|
|
922
|
-
|
|
923
|
-
{!isDigitalPurchase && (<p className="content-text text-center text-gray-500">
|
|
924
|
-
{isWorkshopPurchase
|
|
925
|
-
? 'לאחר שליחת ההרשמה נחזור אליכם לאישור ההשתתפות ופרטי הסדנה'
|
|
926
|
-
: 'לאחר שליחת ההזמנה נחזור אליכם תוך 24 שעות לאישור הזמינות ותיאום המשלוח/איסוף'
|
|
927
|
-
}
|
|
928
|
-
</p>)}
|
|
929
|
-
</div>
|
|
930
|
-
</div>
|
|
931
|
-
</div>
|
|
932
|
-
|
|
933
|
-
<PaymentModalComponent
|
|
934
|
-
isOpen={paymentModalOpen}
|
|
935
|
-
onClose={() => setPaymentModalOpen(false)}
|
|
936
|
-
paymentUrl={paymentData?.paymentLink}
|
|
937
|
-
paymentId={paymentData?.paymentId}
|
|
938
|
-
orderId={createdOrderId}
|
|
939
|
-
simpleOrderNumber={paymentData?.simpleOrderNumber}
|
|
940
|
-
updateOrder={updateOrder}
|
|
941
|
-
/>
|
|
942
|
-
|
|
943
|
-
<ClubPromoModal
|
|
944
|
-
isOpen={clubPromoModalOpen}
|
|
945
|
-
onClose={() => setClubPromoModalOpen(false)}
|
|
946
|
-
onLoginRedirect={handleLoginRedirect}
|
|
947
|
-
/>
|
|
948
|
-
|
|
949
|
-
<ClubMembershipModal
|
|
950
|
-
isOpen={clubMembershipModalOpen}
|
|
951
|
-
onClose={() => setClubMembershipModalOpen(false)}
|
|
952
|
-
onMembershipComplete={handleMembershipComplete}
|
|
953
|
-
userEmail={currentUserData?.email}
|
|
954
|
-
updateClubMembership={updateClubMembership}
|
|
955
|
-
/>
|
|
956
|
-
</div>
|
|
957
|
-
);
|
|
958
|
-
};
|
|
959
|
-
|
|
960
|
-
export default OrderForm;
|