@codesinger0/shared-components 1.1.83 → 1.1.85

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.
Files changed (51) hide show
  1. package/dist/components/FullscreenCarousel.jsx +43 -27
  2. package/dist/components/LargeItemCard.jsx +35 -11
  3. package/dist/components 2/AccessibilityMenu.jsx +474 -0
  4. package/dist/components 2/AdvantagesList.jsx +89 -0
  5. package/dist/components 2/ArticlesList.jsx +269 -0
  6. package/dist/components 2/DualTextCard.jsx +73 -0
  7. package/dist/components 2/FloatingWhatsAppButton.jsx +180 -0
  8. package/dist/components 2/FullscreenCarousel.jsx +292 -0
  9. package/dist/components 2/Hero.jsx +198 -0
  10. package/dist/components 2/IconGrid.jsx +144 -0
  11. package/dist/components 2/IntroSection.jsx +74 -0
  12. package/dist/components 2/LargeItemCard.jsx +267 -0
  13. package/dist/components 2/MasonryItemCard.jsx +247 -0
  14. package/dist/components 2/Menu.d.ts +26 -0
  15. package/dist/components 2/Menu.jsx +268 -0
  16. package/dist/components 2/MyOrdersDisplay.jsx +311 -0
  17. package/dist/components 2/QAAccordion.jsx +212 -0
  18. package/dist/components 2/SmallItemCard.jsx +152 -0
  19. package/dist/components 2/SmallItemsGrid.jsx +313 -0
  20. package/dist/components 2/TextListCards.jsx +107 -0
  21. package/dist/components 2/ToastProvider.jsx +38 -0
  22. package/dist/components 2/UnderConstruction.jsx +76 -0
  23. package/dist/components 2/VideoCard.jsx +88 -0
  24. package/dist/components 2/cart/CartItem.jsx +101 -0
  25. package/dist/components 2/cart/FloatingCartButton.jsx +49 -0
  26. package/dist/components 2/cart/OrderForm.jsx +960 -0
  27. package/dist/components 2/cart/ShoppingCartModal.jsx +229 -0
  28. package/dist/components 2/clubMembership/ClubMembershipModal.jsx +289 -0
  29. package/dist/components 2/clubMembership/ClubPromoModal.jsx +108 -0
  30. package/dist/components 2/elements/CTAButton.jsx +17 -0
  31. package/dist/components 2/elements/FixedWidthHeroVideo.jsx +92 -0
  32. package/dist/components 2/elements/ImageLightbox.jsx +112 -0
  33. package/dist/components 2/elements/RoundButton.jsx +44 -0
  34. package/dist/components 2/elements/SmallButton.jsx +35 -0
  35. package/dist/components 2/elements/Toast.jsx +37 -0
  36. package/dist/components 2/elements/VideoLightbox.jsx +76 -0
  37. package/dist/components 2/modals/ItemDetailsModal.jsx +192 -0
  38. package/dist/components 2/products/CategoryList.jsx +24 -0
  39. package/dist/components 2/products/PriceRangeSlider.jsx +162 -0
  40. package/dist/components 2/products/ProductsDisplay.jsx +40 -0
  41. package/dist/components 2/products/ProductsSidebar.jsx +46 -0
  42. package/dist/components 2/products/SubcategorySection.jsx +37 -0
  43. package/dist/context 2/CartContext.jsx +165 -0
  44. package/dist/context 2/ItemModalContext.jsx +40 -0
  45. package/dist/hooks 2/useScrollLock.js +52 -0
  46. package/dist/index 2.js +45 -0
  47. package/dist/integrations 2/emailService.js +167 -0
  48. package/dist/styles 2/shared-components.css +29 -0
  49. package/dist/utils 2/ScrollManager.jsx +85 -0
  50. package/dist/utils 2/ScrollToTop.jsx +14 -0
  51. package/package.json +1 -1
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import { CircleCheck } from 'lucide-react';
4
+
5
+ const TextListCards = ({
6
+ title,
7
+ subtitle,
8
+ items = [],
9
+ className = '',
10
+ ...props
11
+ }) => {
12
+ if (!items || items.length === 0) {
13
+ return (
14
+ <section className={`py-20 bg-main ${className}`} {...props}>
15
+ <div className="max-w-7xl mx-auto px-6 text-center" dir="rtl">
16
+ {title && <h2 className="title mb-4">{title}</h2>}
17
+ {subtitle && <p className="subtitle">{subtitle}</p>}
18
+ <p className="content-text mt-8">אין פריטים להצגה</p>
19
+ </div>
20
+ </section>
21
+ );
22
+ }
23
+
24
+ return (
25
+ <section className={`py-20 ${className}`} {...props}>
26
+ <div className="max-w-6xl mx-auto">
27
+ {/* Header Section */}
28
+ {(title || subtitle) && (
29
+ <motion.div
30
+ initial={{ opacity: 0, y: 20 }}
31
+ whileInView={{ opacity: 1, y: 0 }}
32
+ viewport={{ once: true }}
33
+ className="text-center mb-16"
34
+ dir="rtl"
35
+ >
36
+ {title && (
37
+ <h2 className="title mb-6">
38
+ {title}
39
+ </h2>
40
+ )}
41
+
42
+ {subtitle && (
43
+ <p className="subtitle max-w-3xl mx-auto mt-6 px-8">
44
+ {subtitle}
45
+ </p>
46
+ )}
47
+ </motion.div>
48
+ )}
49
+
50
+ {/* Cards Grid */}
51
+ <div className="grid lg:grid-cols-3 gap-6">
52
+ {items.map((item, index) => (
53
+ <motion.div
54
+ key={item.title || index}
55
+ initial={{ opacity: 0, y: 20 }}
56
+ whileInView={{ opacity: 1, y: 0 }}
57
+ viewport={{ once: true }}
58
+ transition={{ delay: index * 0.2 }}
59
+ className="h-full"
60
+ >
61
+ <div className="glass-card h-full p-6 text-center hover:scale-105 transition-all duration-300">
62
+ {/* Icon */}
63
+ {item.icon && (
64
+ <div className="w-16 h-16 bg-gradient-to-br from-primary to-primary-bright rounded-full flex items-center justify-center mx-auto mb-6">
65
+ <item.icon className="w-8 h-8 text-white" />
66
+ </div>
67
+ )}
68
+
69
+ {/* Title */}
70
+ <h3 className="subtitle font-bold mb-6" dir="rtl">
71
+ {item.title}
72
+ </h3>
73
+
74
+ {/* Points List */}
75
+ {item.points && item.points.length > 0 && (
76
+ <ul className="space-y-3" dir="rtl">
77
+ {item.points.map((point, i) => (
78
+ <li key={i} className="flex flex-col items-center md:flex-row md:items-start">
79
+ <CircleCheck className="w-5 h-5 text-primary mt-1 ml-2 flex-shrink-0" />
80
+ <span className="content-text text-center md:text-right">{point}</span>
81
+ </li>
82
+ ))}
83
+ </ul>
84
+ )}
85
+ </div>
86
+ </motion.div>
87
+ ))}
88
+ </div>
89
+ </div>
90
+
91
+ {/* Custom Styles */}
92
+ <style jsx>{`
93
+ .from-primary {
94
+ --tw-gradient-from: var(--primary);
95
+ --tw-gradient-to: rgb(0 153 255 / 0);
96
+ --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);
97
+ }
98
+
99
+ .to-primary-bright {
100
+ --tw-gradient-to: var(--primary-bright);
101
+ }
102
+ `}</style>
103
+ </section>
104
+ );
105
+ };
106
+
107
+ export default TextListCards;
@@ -0,0 +1,38 @@
1
+ // components/ToastProvider.jsx
2
+ import { createContext, useContext, useState, useCallback } from 'react';
3
+ import Toast from './elements/Toast';
4
+
5
+ const ToastContext = createContext();
6
+
7
+ export const useToast = () => useContext(ToastContext);
8
+
9
+ export const ToastProvider = ({ children }) => {
10
+ const [toasts, setToasts] = useState([]);
11
+
12
+ const addToast = useCallback((message, variant, duration) => {
13
+ const id = Date.now();
14
+ setToasts((prev) => [...prev, { id, message, duration, variant }]);
15
+ }, []);
16
+
17
+ const removeToast = useCallback((id) => {
18
+ setToasts((prev) => prev.filter((t) => t.id !== id));
19
+ }, []);
20
+
21
+ return (
22
+ <ToastContext.Provider value={addToast}>
23
+ {children}
24
+ <div className="fixed bottom-5 left-0 right-0 flex flex-col items-center space-y-2 z-50">
25
+ {toasts.map((toast) => (
26
+ <Toast
27
+ key={toast.id}
28
+ id={toast.id}
29
+ message={toast.message}
30
+ duration={toast.duration}
31
+ variant={toast.variant}
32
+ onClose={removeToast}
33
+ />
34
+ ))}
35
+ </div>
36
+ </ToastContext.Provider>
37
+ );
38
+ };
@@ -0,0 +1,76 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Hammer, Sparkles } from 'lucide-react';
3
+
4
+ export default function UnderConstruction() {
5
+ const [dots, setDots] = useState('');
6
+
7
+ useEffect(() => {
8
+ const interval = setInterval(() => {
9
+ setDots(prev => prev.length >= 3 ? '' : prev + '.');
10
+ }, 500);
11
+ return () => clearInterval(interval);
12
+ }, []);
13
+
14
+ return (
15
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50 flex items-center justify-center p-4" dir="rtl">
16
+ <div className="max-w-2xl w-full text-center">
17
+ {/* Animated Icon */}
18
+ <div className="mb-8 flex justify-center">
19
+ <div className="relative">
20
+ <div className="absolute inset-0 bg-blue-400 rounded-full blur-xl opacity-30 animate-pulse"></div>
21
+ <div className="relative bg-white rounded-full p-6 shadow-2xl">
22
+ <Hammer className="w-16 h-16 text-blue-600 animate-bounce" />
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ {/* Main Heading */}
28
+ <h1 className="text-4xl md:text-6xl font-bold text-gray-800 mb-4">
29
+ האתר בבניה
30
+ </h1>
31
+
32
+ {/* Subheading with animated dots */}
33
+ <div className="flex items-center justify-center gap-2 mb-8">
34
+ <Sparkles className="w-5 h-5 text-purple-600" />
35
+ <p className="text-xl md:text-2xl text-gray-600">
36
+ עדכונים בקרוב{dots}
37
+ </p>
38
+ <Sparkles className="w-5 h-5 text-purple-600" />
39
+ </div>
40
+
41
+ {/* Description */}
42
+ <p className="text-lg text-gray-600 mb-12 max-w-lg mx-auto leading-relaxed">
43
+ אנחנו עובדים קשה כדי להביא לכם חוויה מדהימה. תודה על הסבלנות!
44
+ </p>
45
+
46
+ {/* Progress Bar */}
47
+ <div className="max-w-md mx-auto mb-12">
48
+ <div className="h-3 bg-gray-200 rounded-full overflow-hidden">
49
+ <div className="h-full bg-gradient-to-r from-blue-500 to-purple-600 rounded-full animate-pulse" style={{ width: '65%' }}></div>
50
+ </div>
51
+ <p className="text-sm text-gray-500 mt-2">בתהליך פיתוח...</p>
52
+ </div>
53
+
54
+ {/* Social Links or Contact (Optional) */}
55
+ <div className="flex flex-col sm:flex-row gap-4 justify-center items-center">
56
+ <div className="bg-white px-6 py-3 rounded-lg shadow-md border border-gray-200">
57
+ <p className="text-gray-600">
58
+ <span className="font-semibold">נשמח לעדכן אתכם!</span>
59
+ </p>
60
+ </div>
61
+ </div>
62
+
63
+ {/* Decorative Elements */}
64
+ <div className="mt-16 flex justify-center gap-2">
65
+ {[1, 2, 3, 4, 5].map((i) => (
66
+ <div
67
+ key={i}
68
+ className="w-2 h-2 bg-blue-400 rounded-full animate-bounce"
69
+ style={{ animationDelay: `${i * 0.1}s` }}
70
+ ></div>
71
+ ))}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
@@ -0,0 +1,88 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { Play, RotateCcw } from 'lucide-react';
3
+ import { motion } from 'framer-motion';
4
+
5
+ export default function VideoCard({ videoUrl, title, description, index }) {
6
+ const videoRef = useRef(null);
7
+ const [isPlaying, setIsPlaying] = useState(false);
8
+ const [showControls, setShowControls] = useState(false);
9
+
10
+ const handleVideoClick = () => {
11
+ if (videoRef.current) {
12
+ videoRef.current.currentTime = 0;
13
+ videoRef.current.muted = false;
14
+ videoRef.current.play();
15
+ setIsPlaying(true);
16
+ }
17
+ };
18
+
19
+ const handleVideoEnd = () => {
20
+ setIsPlaying(false);
21
+ };
22
+
23
+ return (
24
+ <motion.div
25
+ initial={{ opacity: 0, y: 30 }}
26
+ animate={{ opacity: 1, y: 0 }}
27
+ transition={{ duration: 0.6, delay: index * 0.1 }}
28
+ className="group relative bg-white rounded-3xl overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-500 border border-purple-100 rounded-lg"
29
+ onMouseEnter={() => setShowControls(true)}
30
+ onMouseLeave={() => setShowControls(false)}
31
+ >
32
+ {/* Video Container */}
33
+ <div className="relative w-full aspect-video overflow-hidden">
34
+ <video
35
+ ref={videoRef}
36
+ autoPlay
37
+ muted
38
+ loop
39
+ playsInline
40
+ onEnded={handleVideoEnd}
41
+ className="w-full h-full object-cover cursor-pointer transition-transform duration-300 group-hover:scale-105"
42
+ onClick={handleVideoClick}
43
+ >
44
+ <source src={videoUrl} type="video/mp4" />
45
+ <div className="absolute inset-0 bg-gradient-to-br from-purple-500 to-green-500 flex items-center justify-center">
46
+ <Play className="w-16 h-16 text-white opacity-70" />
47
+ </div>
48
+ </video>
49
+
50
+ {/* Overlay Controls */}
51
+ <motion.div
52
+ initial={{ opacity: 0 }}
53
+ animate={{ opacity: showControls ? 1 : 0 }}
54
+ className="absolute inset-0 bg-black bg-opacity-30 flex items-center justify-center transition-all duration-300"
55
+ >
56
+ <motion.button
57
+ whileHover={{ scale: 1.1 }}
58
+ whileTap={{ scale: 0.95 }}
59
+ onClick={handleVideoClick}
60
+ className="bg-white bg-opacity-90 backdrop-blur-sm rounded-full p-4 shadow-lg hover:bg-white transition-all duration-300"
61
+ >
62
+ <RotateCcw className="w-6 h-6 text-purple-700" />
63
+ </motion.button>
64
+ </motion.div>
65
+
66
+ {/* Playing Indicator */}
67
+ {isPlaying && (
68
+ <div className="absolute top-4 right-4 bg-green-500 text-white px-3 py-1 rounded-full text-sm font-medium shadow-lg">
69
+ Playing
70
+ </div>
71
+ )}
72
+ </div>
73
+
74
+ {/* Content */}
75
+ <div className="p-6">
76
+ <h3 className="text-xl font-bold text-gray-900 mb-3 leading-tight">
77
+ {title}
78
+ </h3>
79
+ <p className="text-gray-600 leading-relaxed text-sm">
80
+ {description}
81
+ </p>
82
+ </div>
83
+
84
+ {/* Gradient Border Effect */}
85
+ <div className="absolute inset-0 rounded-3xl bg-gradient-to-r from-purple-500 to-green-500 opacity-0 group-hover:opacity-20 transition-opacity duration-300 pointer-events-none" />
86
+ </motion.div>
87
+ );
88
+ }
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import { motion } from 'framer-motion';
3
+ import { X, Plus, Minus } from 'lucide-react';
4
+
5
+ const CartItem = ({ item, onUpdateQuantity, onRemoveItem }) => {
6
+ const handleQuantityChange = (newQuantity) => {
7
+ if (newQuantity <= 0) {
8
+ onRemoveItem(item.id);
9
+ } else {
10
+ onUpdateQuantity(item.id, newQuantity);
11
+ }
12
+ };
13
+
14
+ const itemTotal = (item.price * item.quantity).toFixed(2);
15
+
16
+ return (
17
+ <motion.div
18
+ layout
19
+ initial={{ opacity: 0, scale: 0.8 }}
20
+ animate={{ opacity: 1, scale: 1 }}
21
+ exit={{ opacity: 0, scale: 0.8 }}
22
+ transition={{ type: "spring", stiffness: 300, damping: 25 }}
23
+ className="glass-card p-4 mb-3"
24
+ dir="rtl"
25
+ >
26
+ <div className="flex items-start gap-3">
27
+ {/* Product Image */}
28
+ <div className="w-16 h-16 rounded-lg overflow-hidden bg-gray-100 flex-shrink-0">
29
+ {item.imageUrl ? (
30
+ <img
31
+ src={item.imageUrl}
32
+ alt={item.name}
33
+ className="w-full h-full object-cover"
34
+ onError={(e) => {
35
+ e.target.style.display = 'none';
36
+ e.target.nextElementSibling.style.display = 'flex';
37
+ }}
38
+ />
39
+ ) : null}
40
+ <div
41
+ className="w-full h-full flex items-center justify-center content-text text-xs"
42
+ style={{ display: item.imageUrl ? 'none' : 'flex' }}
43
+ >
44
+ 🍰
45
+ </div>
46
+ </div>
47
+
48
+ {/* Product Info */}
49
+ <div className="flex-1">
50
+ <div className="flex justify-between items-start mb-2">
51
+ <div>
52
+ <h3 className="content-text font-medium line-clamp-2">{item.name}</h3>
53
+ <p className="text-sm text-price">₪{item.price} ליחידה</p>
54
+ </div>
55
+
56
+ {/* Remove Button */}
57
+ <button
58
+ onClick={() => onRemoveItem(item.id)}
59
+ className="text-red-500 hover:bg-red-50 p-1 rounded-md transition-colors duration-200"
60
+ title="הסר מהעגלה"
61
+ >
62
+ <X size={16} />
63
+ </button>
64
+ </div>
65
+
66
+ {/* Quantity Controls and Total */}
67
+ <div className="flex justify-between items-center">
68
+ {/* Quantity Controls */}
69
+ <div className="flex items-center gap-2">
70
+ <button
71
+ onClick={() => handleQuantityChange(item.quantity - 1)}
72
+ disabled={item.quantity <= 1}
73
+ className="bg-gray-200 hover:bg-gray-300 disabled:opacity-50 disabled:cursor-not-allowed w-7 h-7 rounded-md flex items-center justify-center transition-colors duration-200"
74
+ >
75
+ <Minus size={14} />
76
+ </button>
77
+
78
+ <span className="w-8 text-center content-text font-medium">
79
+ {item.quantity}
80
+ </span>
81
+
82
+ <button
83
+ onClick={() => handleQuantityChange(item.quantity + 1)}
84
+ className="bg-gray-200 hover:bg-gray-300 w-7 h-7 rounded-md flex items-center justify-center transition-colors duration-200"
85
+ >
86
+ <Plus size={14} />
87
+ </button>
88
+ </div>
89
+
90
+ {/* Item Total */}
91
+ <div className="content-text font-bold text-price">
92
+ ₪{itemTotal}
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </motion.div>
98
+ );
99
+ };
100
+
101
+ export default CartItem;
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { ShoppingCart } from 'lucide-react';
4
+ import { useCart } from '../../context/CartContext';
5
+
6
+ const FloatingCartButton = ({
7
+ absolute = true,
8
+ icon: CustomIcon = ShoppingCart,
9
+ bgColor = 'bg-orange-500',
10
+ hoverColor = 'hover:brightness-90'
11
+ }) => {
12
+ const { totalItems, toggleCart } = useCart();
13
+
14
+ return (
15
+ <div className={`${absolute ? 'fixed bottom-6 right-6' : 'relative'} z-30`}>
16
+ <motion.button
17
+ onClick={toggleCart}
18
+ className={`relative ${bgColor} text-white p-4 rounded-full shadow-lg ${hoverColor} transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50`}
19
+ style={{
20
+ transform: 'translate3d(0, 0, 0)',
21
+ WebkitTransform: 'translate3d(0, 0, 0)',
22
+ WebkitBackfaceVisibility: 'hidden'
23
+ }} whileHover={{ scale: 1.05 }}
24
+ whileTap={{ scale: 0.95 }}
25
+ initial={{ scale: 0 }}
26
+ animate={{ scale: 1 }}
27
+ transition={{ type: "spring", stiffness: 300, damping: 25 }}
28
+ >
29
+ <CustomIcon size={26} />
30
+ {/* Badge */}
31
+ <AnimatePresence>
32
+ {totalItems > 0 && (
33
+ <motion.div
34
+ initial={{ scale: 0, opacity: 0 }}
35
+ animate={{ scale: 1, opacity: 1 }}
36
+ exit={{ scale: 0, opacity: 0 }}
37
+ transition={{ type: "spring", stiffness: 400, damping: 25 }}
38
+ className="absolute -top-2 -right-2 bg-red-500 text-white text-xs font-bold rounded-full min-w-[1.5rem] h-6 flex items-center justify-center px-1"
39
+ >
40
+ {totalItems > 99 ? '99+' : totalItems}
41
+ </motion.div>
42
+ )}
43
+ </AnimatePresence>
44
+ </motion.button>
45
+ </div>
46
+ );
47
+ };
48
+
49
+ export default FloatingCartButton;