@codesinger0/shared-components 1.1.37 → 1.1.39

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.
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import ClubMembershipModal from '../clubMembership/ClubMembershipModal'
3
3
  import ClubPromoModal from '../clubMembership/ClubPromoModal'
4
4
  import { ArrowRight, Truck, Store, Crown, AlertCircle, Loader2, Calendar } from 'lucide-react';
@@ -42,7 +42,8 @@ const OrderForm = ({
42
42
  addToast, // Inject toast function
43
43
  cartItems,
44
44
  totalPrice,
45
- paymentLinkCreationURL
45
+ paymentLinkCreationURL,
46
+ scrollContainerRef
46
47
  }) => {
47
48
 
48
49
  const [nameError, setNameError] = useState('');
@@ -83,6 +84,30 @@ const OrderForm = ({
83
84
  const [isSubmitting, setIsSubmitting] = useState(false);
84
85
  const [errors, setErrors] = useState({});
85
86
 
87
+ const scrollToAnchor = (fieldName) => {
88
+ // Small delay to ensure error messages are rendered
89
+ setTimeout(() => {
90
+ const element = document.getElementById(`${fieldName}`);
91
+ const container = scrollContainerRef.current;
92
+
93
+ if (element && container) {
94
+ // Get element position relative to container
95
+ const elementTop = element.offsetTop;
96
+ const containerHeight = container.clientHeight;
97
+ const elementHeight = element.offsetHeight;
98
+
99
+ // Calculate scroll position to center the element
100
+ const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
101
+
102
+ // Smooth scroll within the container
103
+ container.scrollTo({
104
+ top: Math.max(0, scrollTo),
105
+ behavior: 'smooth'
106
+ });
107
+ }
108
+ }, 100);
109
+ };
110
+
86
111
  const minDeliveryAmount = settings.minDeliveryAmount;
87
112
  const deliveryFee = settings.deliveryFee;
88
113
 
@@ -199,26 +224,33 @@ const OrderForm = ({
199
224
 
200
225
  const validateForm = () => {
201
226
  const newErrors = {};
227
+ let firstErrorField = null;
202
228
 
203
229
  if (!formData.name.trim()) {
204
230
  newErrors.name = 'שם מלא חובה';
231
+ if (!firstErrorField) firstErrorField = 'name';
205
232
  } else {
206
233
  const words = formData.name.trim().split(/\s+/);
207
234
  if (words.length < 2) {
208
235
  newErrors.name = 'יש להזין שם פרטי ומשפחה';
236
+ if (!firstErrorField) firstErrorField = 'name';
209
237
  }
210
238
  }
211
239
 
212
240
  if (!formData.email.trim()) {
213
241
  newErrors.email = 'כתובת אימייל חובה';
242
+ if (!firstErrorField) firstErrorField = 'email';
214
243
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
215
244
  newErrors.email = 'כתובת אימייל לא תקינה';
245
+ if (!firstErrorField) firstErrorField = 'email';
216
246
  }
217
247
 
218
248
  if (!formData.phone.trim()) {
219
249
  newErrors.phone = 'מספר טלפון חובה';
250
+ if (!firstErrorField) firstErrorField = 'phone';
220
251
  } else if (!/^(05\d{8}|5\d{8})$/.test(formData.phone.replace(/\D/g, ''))) {
221
252
  newErrors.phone = 'מספר טלפון לא תקין';
253
+ if (!firstErrorField) firstErrorField = 'phone';
222
254
  }
223
255
 
224
256
  // Only validate delivery for physical delivery method
@@ -229,17 +261,23 @@ const OrderForm = ({
229
261
 
230
262
  if (currentTotal < minDeliveryAmount) {
231
263
  newErrors.delivery = `מינימום הזמנה למשלוח הוא ₪${minDeliveryAmount}`;
264
+ if (!firstErrorField) firstErrorField = 'delivery';
232
265
  }
233
266
  if (!formData.deliveryCity) {
234
267
  newErrors.deliveryCity = 'בחירת עיר חובה';
268
+ if (!firstErrorField) firstErrorField = 'deliveryCity';
235
269
  }
236
270
  if (!formData.deliveryAddress.trim()) {
237
271
  newErrors.deliveryAddress = 'כתובת משלוח חובה';
272
+ if (!firstErrorField) firstErrorField = 'deliveryAddress';
238
273
  }
239
274
  }
240
275
 
241
276
  setErrors(newErrors);
242
- return Object.keys(newErrors).length === 0;
277
+ return {
278
+ isValid: Object.keys(newErrors).length === 0,
279
+ firstErrorField
280
+ };
243
281
  };
244
282
 
245
283
  const grantCourseAccess = async (userId, courseIds) => {
@@ -297,11 +335,12 @@ const OrderForm = ({
297
335
 
298
336
  const handleSubmit = async (e) => {
299
337
  e.preventDefault();
300
-
338
+ debugger
301
339
  // Validate name before submission
302
340
  const nameValidationError = validateFullName(formData.name);
303
341
  if (nameValidationError) {
304
342
  setNameError(nameValidationError);
343
+ scrollToAnchor('name-field');
305
344
  return;
306
345
  }
307
346
 
@@ -309,7 +348,14 @@ const OrderForm = ({
309
348
  setNameError('');
310
349
  setDeliveryError('');
311
350
 
312
- if (!validateForm()) return;
351
+ const validation = validateForm();
352
+ if (!validation.isValid) {
353
+ // Scroll to the first field with an error
354
+ if (validation.firstErrorField) {
355
+ scrollToAnchor(`${validation.firstErrorField}-field`);
356
+ }
357
+ return;
358
+ }
313
359
 
314
360
  const subtotal = (isCoursePurchase || isWorkshopPurchase) ? itemsTotal : totalPrice;
315
361
  const clubDiscount = isUserClubMember ? (subtotal * (clubDiscountPercentage / 100)) : 0;
@@ -498,7 +544,7 @@ const OrderForm = ({
498
544
  <h2 className="subtitle font-semibold">פרטים אישיים</h2>
499
545
 
500
546
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
501
- <div>
547
+ <div id="name-field">
502
548
  <label className="block subtitle font-medium mb-2">שם מלא *</label>
503
549
  <input
504
550
  type="text"
@@ -522,7 +568,7 @@ const OrderForm = ({
522
568
  )}
523
569
  </div>
524
570
 
525
- <div>
571
+ <div id="phone-field">
526
572
  <label className="block subtitle font-medium mb-2">טלפון *</label>
527
573
  <input
528
574
  type="tel"
@@ -542,7 +588,7 @@ const OrderForm = ({
542
588
  </div>
543
589
  </div>
544
590
 
545
- <div>
591
+ <div id="email-field">
546
592
  <label className="block subtitle font-medium mb-2">אימייל *</label>
547
593
  <input
548
594
  type="email"
@@ -569,8 +615,8 @@ const OrderForm = ({
569
615
 
570
616
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
571
617
  <label className={`glass-card p-4 cursor-pointer border-2 transition-colors duration-200 ${formData.deliveryMethod === 'delivery'
572
- ? 'border-primary bg-opacity-20'
573
- : 'border-transparent hover:border-gray-300'
618
+ ? 'border-gray-300 bg-opacity-20'
619
+ : 'border-transparent hover:border-gray-100'
574
620
  }`}>
575
621
  <input
576
622
  type="radio"
@@ -590,8 +636,8 @@ const OrderForm = ({
590
636
  </label>
591
637
 
592
638
  <label className={`glass-card p-4 cursor-pointer border-2 transition-colors duration-200 ${formData.deliveryMethod === 'pickup'
593
- ? 'border-primary bg-opacity-20'
594
- : 'border-transparent hover:border-gray-300'
639
+ ? 'border-gray-300 bg-opacity-20'
640
+ : 'border-transparent hover:border-gray-100'
595
641
  }`}>
596
642
  <input
597
643
  type="radio"
@@ -631,7 +677,7 @@ const OrderForm = ({
631
677
  <h2 className="subtitle font-semibold">כתובת</h2>
632
678
 
633
679
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
634
- <div>
680
+ <div id="deliveryCity-field">
635
681
  <label className="block subtitle font-medium mb-2">עיר *</label>
636
682
  {(isCoursePurchase || isWorkshopPurchase) ? (
637
683
  <input
@@ -663,7 +709,7 @@ const OrderForm = ({
663
709
  )}
664
710
  </div>
665
711
 
666
- <div>
712
+ <div id="deliveryAddress-field">
667
713
  <label className="block subtitle font-medium mb-2">רחוב ומספר בית *</label>
668
714
  <input
669
715
  type="text"
@@ -16,6 +16,8 @@ const ShoppingCartModal = ({ CheckoutComponent }) => {
16
16
  removeFromCart
17
17
  } = useCart();
18
18
 
19
+ const orderFormScrollContainerRef = useRef(null);
20
+
19
21
  // local visible state that controls AnimatePresence mounting
20
22
  const [localOpen, setLocalOpen] = useState(Boolean(isCartOpen));
21
23
  const [showOrderForm, setShowOrderForm] = useState(false);
@@ -97,12 +99,13 @@ const ShoppingCartModal = ({ CheckoutComponent }) => {
97
99
  >
98
100
  {showOrderForm ? (
99
101
  // Order Form View
100
- <div className="h-full overflow-y-auto">
102
+ <div ref={orderFormScrollContainerRef} className="h-full overflow-y-auto">
101
103
  {CheckoutComponent ? (
102
104
  <CheckoutComponent
103
105
  onBack={handleBackToCart}
104
106
  cartItems={cartItems}
105
107
  totalPrice={totalPrice}
108
+ scrollContainerRef={orderFormScrollContainerRef}
106
109
  />
107
110
  ) : (
108
111
  <div className="p-6">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesinger0/shared-components",
3
- "version": "1.1.37",
3
+ "version": "1.1.39",
4
4
  "description": "Shared React components for customer projects",
5
5
  "main": "dist/index.js",
6
6
  "files": [