@codesinger0/shared-components 1.1.38 → 1.1.40

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,33 @@ const OrderForm = ({
83
84
  const [isSubmitting, setIsSubmitting] = useState(false);
84
85
  const [errors, setErrors] = useState({});
85
86
 
87
+ const localScrollContainerRef = useRef(null);
88
+ const activeScrollContainerRef = scrollContainerRef || localScrollContainerRef;
89
+
90
+ const scrollToAnchor = (fieldName) => {
91
+ // Small delay to ensure error messages are rendered
92
+ setTimeout(() => {
93
+ const element = document.getElementById(`${fieldName}`);
94
+ const container = activeScrollContainerRef.current;
95
+
96
+ if (element && container) {
97
+ // Get element position relative to container
98
+ const elementTop = element.offsetTop;
99
+ const containerHeight = container.clientHeight;
100
+ const elementHeight = element.offsetHeight;
101
+
102
+ // Calculate scroll position to center the element
103
+ const scrollTo = elementTop - (containerHeight / 2) + (elementHeight / 2);
104
+
105
+ // Smooth scroll within the container
106
+ container.scrollTo({
107
+ top: Math.max(0, scrollTo),
108
+ behavior: 'smooth'
109
+ });
110
+ }
111
+ }, 100);
112
+ };
113
+
86
114
  const minDeliveryAmount = settings.minDeliveryAmount;
87
115
  const deliveryFee = settings.deliveryFee;
88
116
 
@@ -199,26 +227,33 @@ const OrderForm = ({
199
227
 
200
228
  const validateForm = () => {
201
229
  const newErrors = {};
230
+ let firstErrorField = null;
202
231
 
203
232
  if (!formData.name.trim()) {
204
233
  newErrors.name = 'שם מלא חובה';
234
+ if (!firstErrorField) firstErrorField = 'name';
205
235
  } else {
206
236
  const words = formData.name.trim().split(/\s+/);
207
237
  if (words.length < 2) {
208
238
  newErrors.name = 'יש להזין שם פרטי ומשפחה';
239
+ if (!firstErrorField) firstErrorField = 'name';
209
240
  }
210
241
  }
211
242
 
212
243
  if (!formData.email.trim()) {
213
244
  newErrors.email = 'כתובת אימייל חובה';
245
+ if (!firstErrorField) firstErrorField = 'email';
214
246
  } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
215
247
  newErrors.email = 'כתובת אימייל לא תקינה';
248
+ if (!firstErrorField) firstErrorField = 'email';
216
249
  }
217
250
 
218
251
  if (!formData.phone.trim()) {
219
252
  newErrors.phone = 'מספר טלפון חובה';
253
+ if (!firstErrorField) firstErrorField = 'phone';
220
254
  } else if (!/^(05\d{8}|5\d{8})$/.test(formData.phone.replace(/\D/g, ''))) {
221
255
  newErrors.phone = 'מספר טלפון לא תקין';
256
+ if (!firstErrorField) firstErrorField = 'phone';
222
257
  }
223
258
 
224
259
  // Only validate delivery for physical delivery method
@@ -229,17 +264,23 @@ const OrderForm = ({
229
264
 
230
265
  if (currentTotal < minDeliveryAmount) {
231
266
  newErrors.delivery = `מינימום הזמנה למשלוח הוא ₪${minDeliveryAmount}`;
267
+ if (!firstErrorField) firstErrorField = 'delivery';
232
268
  }
233
269
  if (!formData.deliveryCity) {
234
270
  newErrors.deliveryCity = 'בחירת עיר חובה';
271
+ if (!firstErrorField) firstErrorField = 'deliveryCity';
235
272
  }
236
273
  if (!formData.deliveryAddress.trim()) {
237
274
  newErrors.deliveryAddress = 'כתובת משלוח חובה';
275
+ if (!firstErrorField) firstErrorField = 'deliveryAddress';
238
276
  }
239
277
  }
240
278
 
241
279
  setErrors(newErrors);
242
- return Object.keys(newErrors).length === 0;
280
+ return {
281
+ isValid: Object.keys(newErrors).length === 0,
282
+ firstErrorField
283
+ };
243
284
  };
244
285
 
245
286
  const grantCourseAccess = async (userId, courseIds) => {
@@ -297,11 +338,12 @@ const OrderForm = ({
297
338
 
298
339
  const handleSubmit = async (e) => {
299
340
  e.preventDefault();
300
-
341
+ debugger
301
342
  // Validate name before submission
302
343
  const nameValidationError = validateFullName(formData.name);
303
344
  if (nameValidationError) {
304
345
  setNameError(nameValidationError);
346
+ scrollToAnchor('name-field');
305
347
  return;
306
348
  }
307
349
 
@@ -309,7 +351,14 @@ const OrderForm = ({
309
351
  setNameError('');
310
352
  setDeliveryError('');
311
353
 
312
- if (!validateForm()) return;
354
+ const validation = validateForm();
355
+ if (!validation.isValid) {
356
+ // Scroll to the first field with an error
357
+ if (validation.firstErrorField) {
358
+ scrollToAnchor(`${validation.firstErrorField}-field`);
359
+ }
360
+ return;
361
+ }
313
362
 
314
363
  const subtotal = (isCoursePurchase || isWorkshopPurchase) ? itemsTotal : totalPrice;
315
364
  const clubDiscount = isUserClubMember ? (subtotal * (clubDiscountPercentage / 100)) : 0;
@@ -452,7 +501,11 @@ const OrderForm = ({
452
501
  const grandTotal = discountedSubtotal + deliveryCost;
453
502
 
454
503
  return (
455
- <div className="min-h-screen bg-main py-12 px-4" dir="rtl">
504
+ <div
505
+ ref={scrollContainerRef ? null : localScrollContainerRef}
506
+ className={scrollContainerRef ? "min-h-screen bg-main py-12 px-4" : "h-screen overflow-y-auto bg-main py-12 px-4"}
507
+ dir="rtl"
508
+ >
456
509
  <div className="max-w-2xl mx-auto">
457
510
 
458
511
  {/* Back Button */}
@@ -498,7 +551,7 @@ const OrderForm = ({
498
551
  <h2 className="subtitle font-semibold">פרטים אישיים</h2>
499
552
 
500
553
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
501
- <div>
554
+ <div id="name-field">
502
555
  <label className="block subtitle font-medium mb-2">שם מלא *</label>
503
556
  <input
504
557
  type="text"
@@ -522,7 +575,7 @@ const OrderForm = ({
522
575
  )}
523
576
  </div>
524
577
 
525
- <div>
578
+ <div id="phone-field">
526
579
  <label className="block subtitle font-medium mb-2">טלפון *</label>
527
580
  <input
528
581
  type="tel"
@@ -542,7 +595,7 @@ const OrderForm = ({
542
595
  </div>
543
596
  </div>
544
597
 
545
- <div>
598
+ <div id="email-field">
546
599
  <label className="block subtitle font-medium mb-2">אימייל *</label>
547
600
  <input
548
601
  type="email"
@@ -631,7 +684,7 @@ const OrderForm = ({
631
684
  <h2 className="subtitle font-semibold">כתובת</h2>
632
685
 
633
686
  <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
634
- <div>
687
+ <div id="deliveryCity-field">
635
688
  <label className="block subtitle font-medium mb-2">עיר *</label>
636
689
  {(isCoursePurchase || isWorkshopPurchase) ? (
637
690
  <input
@@ -663,7 +716,7 @@ const OrderForm = ({
663
716
  )}
664
717
  </div>
665
718
 
666
- <div>
719
+ <div id="deliveryAddress-field">
667
720
  <label className="block subtitle font-medium mb-2">רחוב ומספר בית *</label>
668
721
  <input
669
722
  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.38",
3
+ "version": "1.1.40",
4
4
  "description": "Shared React components for customer projects",
5
5
  "main": "dist/index.js",
6
6
  "files": [