@codesinger0/shared-components 1.1.84 → 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 (50) hide show
  1. package/dist/components/LargeItemCard.jsx +35 -11
  2. package/dist/components 2/AccessibilityMenu.jsx +474 -0
  3. package/dist/components 2/AdvantagesList.jsx +89 -0
  4. package/dist/components 2/ArticlesList.jsx +269 -0
  5. package/dist/components 2/DualTextCard.jsx +73 -0
  6. package/dist/components 2/FloatingWhatsAppButton.jsx +180 -0
  7. package/dist/components 2/FullscreenCarousel.jsx +292 -0
  8. package/dist/components 2/Hero.jsx +198 -0
  9. package/dist/components 2/IconGrid.jsx +144 -0
  10. package/dist/components 2/IntroSection.jsx +74 -0
  11. package/dist/components 2/LargeItemCard.jsx +267 -0
  12. package/dist/components 2/MasonryItemCard.jsx +247 -0
  13. package/dist/components 2/Menu.d.ts +26 -0
  14. package/dist/components 2/Menu.jsx +268 -0
  15. package/dist/components 2/MyOrdersDisplay.jsx +311 -0
  16. package/dist/components 2/QAAccordion.jsx +212 -0
  17. package/dist/components 2/SmallItemCard.jsx +152 -0
  18. package/dist/components 2/SmallItemsGrid.jsx +313 -0
  19. package/dist/components 2/TextListCards.jsx +107 -0
  20. package/dist/components 2/ToastProvider.jsx +38 -0
  21. package/dist/components 2/UnderConstruction.jsx +76 -0
  22. package/dist/components 2/VideoCard.jsx +88 -0
  23. package/dist/components 2/cart/CartItem.jsx +101 -0
  24. package/dist/components 2/cart/FloatingCartButton.jsx +49 -0
  25. package/dist/components 2/cart/OrderForm.jsx +960 -0
  26. package/dist/components 2/cart/ShoppingCartModal.jsx +229 -0
  27. package/dist/components 2/clubMembership/ClubMembershipModal.jsx +289 -0
  28. package/dist/components 2/clubMembership/ClubPromoModal.jsx +108 -0
  29. package/dist/components 2/elements/CTAButton.jsx +17 -0
  30. package/dist/components 2/elements/FixedWidthHeroVideo.jsx +92 -0
  31. package/dist/components 2/elements/ImageLightbox.jsx +112 -0
  32. package/dist/components 2/elements/RoundButton.jsx +44 -0
  33. package/dist/components 2/elements/SmallButton.jsx +35 -0
  34. package/dist/components 2/elements/Toast.jsx +37 -0
  35. package/dist/components 2/elements/VideoLightbox.jsx +76 -0
  36. package/dist/components 2/modals/ItemDetailsModal.jsx +192 -0
  37. package/dist/components 2/products/CategoryList.jsx +24 -0
  38. package/dist/components 2/products/PriceRangeSlider.jsx +162 -0
  39. package/dist/components 2/products/ProductsDisplay.jsx +40 -0
  40. package/dist/components 2/products/ProductsSidebar.jsx +46 -0
  41. package/dist/components 2/products/SubcategorySection.jsx +37 -0
  42. package/dist/context 2/CartContext.jsx +165 -0
  43. package/dist/context 2/ItemModalContext.jsx +40 -0
  44. package/dist/hooks 2/useScrollLock.js +52 -0
  45. package/dist/index 2.js +45 -0
  46. package/dist/integrations 2/emailService.js +167 -0
  47. package/dist/styles 2/shared-components.css +29 -0
  48. package/dist/utils 2/ScrollManager.jsx +85 -0
  49. package/dist/utils 2/ScrollToTop.jsx +14 -0
  50. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ import React, { useState, useEffect } from 'react';
2
+
3
+ const FixedWidthHeroVideo = ({
4
+ youtubeVideoId = "dQw4w9WgXcQ",
5
+ useYoutube = false,
6
+ videoUrl = "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
7
+ }) => {
8
+ const [viewportSize, setViewportSize] = useState({ width: 0, height: 0 });
9
+
10
+ useEffect(() => {
11
+ const updateViewportSize = () => {
12
+ setViewportSize({
13
+ width: window.innerWidth,
14
+ height: window.innerHeight
15
+ });
16
+ };
17
+
18
+ updateViewportSize();
19
+ window.addEventListener('resize', updateViewportSize);
20
+ return () => window.removeEventListener('resize', updateViewportSize);
21
+ }, []);
22
+
23
+
24
+ const youtubeEmbedUrl = `https://www.youtube.com/embed/${youtubeVideoId}?autoplay=1&mute=1&loop=1&playlist=${youtubeVideoId}&controls=0&showinfo=0&rel=0&iv_load_policy=3&modestbranding=1`;
25
+
26
+ return (
27
+ <div className="w-full">
28
+
29
+ {/* Hero Section */}
30
+ <section className="relative w-full h-screen overflow-hidden flex items-center justify-center">
31
+ {useYoutube ? (
32
+ /* YouTube Embed */
33
+ <iframe
34
+ className="absolute top-[35%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-0 pointer-events-none"
35
+ style={{
36
+ minWidth: '1200px',
37
+ width: 'max(100vw, 1200px)',
38
+ aspectRatio: '16/9',
39
+ minHeight: '120%',
40
+ border: 'none'
41
+ }}
42
+ src={youtubeEmbedUrl}
43
+ title="YouTube video player"
44
+ frameBorder="0"
45
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
46
+ allowFullScreen
47
+ />
48
+ ) : (
49
+ /* Direct Video */
50
+ <video
51
+ className="absolute top-[35%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-0 pointer-events-none"
52
+ style={{
53
+ minWidth: '1200px',
54
+ width: 'max(100vw, 1200px)',
55
+ aspectRatio: '16/9',
56
+ minHeight: '120%',
57
+ border: 'none'
58
+ }}
59
+ autoPlay
60
+ muted
61
+ loop
62
+ playsInline
63
+ >
64
+ <source src={videoUrl} type="video/mp4" />
65
+ Your browser does not support the video tag.
66
+ </video>
67
+ )}
68
+ </section>
69
+
70
+ {/* CSS Animation Styles */}
71
+ <style>{`
72
+ @keyframes fadeInUp {
73
+ from {
74
+ opacity: 0;
75
+ transform: translateY(30px);
76
+ }
77
+ to {
78
+ opacity: 1;
79
+ transform: translateY(0);
80
+ }
81
+ }
82
+ `}</style>
83
+ </div>
84
+ );
85
+ };
86
+
87
+ // Usage examples:
88
+ // <FixedWidthHeroVideo useYoutube={false} />
89
+ // <FixedWidthHeroVideo useYoutube={false} videoUrl="path/to/your/video.mp4" />
90
+ // <FixedWidthHeroVideo useYoutube={true} youtubeVideoId="dQw4w9WgXcQ" />
91
+
92
+ export default FixedWidthHeroVideo;
@@ -0,0 +1,112 @@
1
+ import React from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, ChevronLeft, ChevronRight } from 'lucide-react';
4
+ import useScrollLock from '../../hooks/useScrollLock';
5
+
6
+ const ImageLightbox = ({
7
+ images,
8
+ currentIndex,
9
+ isOpen,
10
+ onClose,
11
+ onNavigate
12
+ }) => {
13
+ const currentImage = images[currentIndex];
14
+
15
+ useScrollLock(isOpen);
16
+
17
+ if (!currentImage) return null;
18
+
19
+ // Handle both object format {src, alt, title} and string format
20
+ const imageSrc = typeof currentImage === 'string' ? currentImage : currentImage.src;
21
+ const imageAlt = typeof currentImage === 'string' ? 'Gallery image' : (currentImage.alt || currentImage.title || 'Gallery image');
22
+ const imageTitle = typeof currentImage === 'string' ? null : currentImage.title;
23
+
24
+ return (
25
+ <AnimatePresence>
26
+ {isOpen && (
27
+ <div className="fixed inset-0 z-50 flex items-center justify-center supports-[height:100dvh]:h-[100dvh]">
28
+ {/* Backdrop */}
29
+ <motion.div
30
+ key="backdrop"
31
+ initial={{ opacity: 0 }}
32
+ animate={{ opacity: 1 }}
33
+ exit={{ opacity: 0 }}
34
+ transition={{ duration: 0.2 }}
35
+ className="absolute inset-0 bg-black bg-opacity-90"
36
+ onClick={onClose}
37
+ />
38
+
39
+ {/* Image Container */}
40
+ <motion.div
41
+ key="image-container"
42
+ initial={{ opacity: 0, scale: 0.8 }}
43
+ animate={{ opacity: 1, scale: 1 }}
44
+ exit={{ opacity: 0, scale: 0.8 }}
45
+ transition={{ type: "spring", stiffness: 300, damping: 25 }}
46
+ className="relative max-w-[90vw] max-h-[90vh] z-10"
47
+ onClick={(e) => e.stopPropagation()}
48
+ >
49
+ {/* Main Image */}
50
+ <img
51
+ src={imageSrc}
52
+ alt={imageAlt}
53
+ className="w-screen h-screen object-contain rounded-lg shadow-2xl"
54
+ />
55
+
56
+ {/* Close Button */}
57
+ <button
58
+ onClick={onClose}
59
+ className="absolute top-4 right-4 bg-black bg-opacity-50 hover:bg-opacity-70 text-white p-2 rounded-full transition-all duration-200"
60
+ aria-label="Close lightbox"
61
+ >
62
+ <X size={24} />
63
+ </button>
64
+
65
+ {/* Navigation Arrows */}
66
+ {images.length > 1 && (
67
+ <>
68
+ {currentIndex > 0 && (
69
+ <button
70
+ onClick={() => onNavigate(currentIndex - 1)}
71
+ className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-black bg-opacity-50 hover:bg-opacity-70 text-white p-3 rounded-full transition-all duration-200"
72
+ aria-label="Previous image"
73
+ >
74
+ <ChevronLeft size={24} />
75
+ </button>
76
+ )}
77
+
78
+ {currentIndex < images.length - 1 && (
79
+ <button
80
+ onClick={() => onNavigate(currentIndex + 1)}
81
+ className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-black bg-opacity-50 hover:bg-opacity-70 text-white p-3 rounded-full transition-all duration-200"
82
+ aria-label="Next image"
83
+ >
84
+ <ChevronRight size={24} />
85
+ </button>
86
+ )}
87
+ </>
88
+ )}
89
+
90
+ {/* Image Counter */}
91
+ {images.length > 1 && (
92
+ <div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-50 text-white px-3 py-1 rounded-full text-sm">
93
+ {currentIndex + 1} / {images.length}
94
+ </div>
95
+ )}
96
+
97
+ {/* Image Title */}
98
+ {imageTitle && (
99
+ <div className="absolute bottom-4 right-4 bg-black bg-opacity-50 text-white px-3 py-2 rounded-lg max-w-xs">
100
+ <div className="text-sm font-medium" dir="rtl">
101
+ {imageTitle}
102
+ </div>
103
+ </div>
104
+ )}
105
+ </motion.div>
106
+ </div>
107
+ )}
108
+ </AnimatePresence>
109
+ );
110
+ };
111
+
112
+ export default ImageLightbox;
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+
3
+ const RoundButton = ({
4
+ children,
5
+ onClick,
6
+ variant = 'primary',
7
+ size = 'medium',
8
+ className = '',
9
+ ...props
10
+ }) => {
11
+ const baseClasses = 'rounded-full font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2';
12
+
13
+ const variants = {
14
+ primary: 'btn-primary',
15
+ secondary: 'btn-secondary',
16
+ outline: 'border border-gray-300 text-main hover:ring-2 hover:ring-gray-500 hover:opacity-80'
17
+ };
18
+
19
+ const sizes = {
20
+ xs: 'px-3 py-1.5 text-sm',
21
+ small: 'px-4 py-2 text-sm',
22
+ medium: 'px-6 py-3 text-base',
23
+ large: 'px-8 py-4 text-lg'
24
+ };
25
+
26
+ const buttonClasses = `${baseClasses} ${variants[variant]} ${sizes[size]} ${className}`;
27
+
28
+ return (
29
+ <button
30
+ className={buttonClasses}
31
+ onClick={(e) => {
32
+ onClick?.(e);
33
+ // remove focus immediately after click
34
+ e.currentTarget.blur();
35
+ }}
36
+ dir="rtl"
37
+ {...props}
38
+ >
39
+ {children}
40
+ </button>
41
+ );
42
+ };
43
+
44
+ export default RoundButton;
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+
3
+ const SmallButton = ({
4
+ children,
5
+ onClick,
6
+ disabled = false,
7
+ className = '',
8
+ ...props
9
+ }) => {
10
+ return (
11
+ <button
12
+ className={`
13
+ btn-primary content-text px-3 py-1
14
+ rounded-md font-medium
15
+ transition-all duration-200
16
+ focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2
17
+ hover:brightness-90
18
+ disabled:opacity-50 disabled:cursor-not-allowed
19
+ ${className}
20
+ `}
21
+ onClick={(e) => {
22
+ onClick?.(e);
23
+ // remove focus right after click so the ring disappears
24
+ e.currentTarget.blur();
25
+ }}
26
+ disabled={disabled}
27
+ dir="rtl"
28
+ {...props}
29
+ >
30
+ {children}
31
+ </button>
32
+ );
33
+ };
34
+
35
+ export default SmallButton;
@@ -0,0 +1,37 @@
1
+ import { useEffect, useState } from 'react';
2
+
3
+ const variantStyles = {
4
+ success: 'bg-green-500 text-white',
5
+ error: 'bg-red-500 text-white',
6
+ info: 'bg-blue-500 text-white',
7
+ warning: 'bg-yellow-400 text-black',
8
+ };
9
+
10
+ const Toast = ({ id, message, onClose, duration = 3000, variant = 'info' }) => {
11
+ const [show, setShow] = useState(true);
12
+
13
+ useEffect(() => {
14
+ const timer = setTimeout(() => {
15
+ setShow(false);
16
+ setTimeout(() => onClose(id), 300); // match transition duration
17
+ }, duration);
18
+
19
+ return () => clearTimeout(timer);
20
+ }, [id, onClose, duration]);
21
+
22
+ return (
23
+ <div
24
+ className={`fixed bottom-5 left-1/2 transform -translate-x-1/2
25
+ px-4 py-2 rounded-lg shadow-lg
26
+ text-sm sm:text-base md:text-lg
27
+ max-w-xs sm:max-w-sm md:max-w-md
28
+ transition-all duration-300
29
+ ${variantStyles[variant]}
30
+ ${show ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-5'}`}
31
+ >
32
+ {message}
33
+ </div>
34
+ );
35
+ };
36
+
37
+ export default Toast;
@@ -0,0 +1,76 @@
1
+ import React from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X } from 'lucide-react';
4
+ import useScrollLock from '../../hooks/useScrollLock';
5
+
6
+ const VideoLightbox = ({
7
+ videoUrl,
8
+ isOpen,
9
+ onClose,
10
+ title
11
+ }) => {
12
+ useScrollLock(isOpen);
13
+
14
+ if (!videoUrl) return null;
15
+
16
+ return (
17
+ <AnimatePresence>
18
+ {isOpen && (
19
+ <div className="fixed inset-0 z-50 flex items-center justify-center supports-[height:100dvh]:h-[100dvh]">
20
+ {/* Backdrop */}
21
+ <motion.div
22
+ key="backdrop"
23
+ initial={{ opacity: 0 }}
24
+ animate={{ opacity: 1 }}
25
+ exit={{ opacity: 0 }}
26
+ transition={{ duration: 0.2 }}
27
+ className="absolute inset-0 bg-black bg-opacity-90"
28
+ onClick={onClose}
29
+ />
30
+
31
+ {/* Video Container */}
32
+ <motion.div
33
+ key="video-container"
34
+ initial={{ opacity: 0, scale: 0.8 }}
35
+ animate={{ opacity: 1, scale: 1 }}
36
+ exit={{ opacity: 0, scale: 0.8 }}
37
+ transition={{ type: "spring", stiffness: 300, damping: 25 }}
38
+ className="relative max-w-[90vw] max-h-[90vh] z-10"
39
+ onClick={(e) => e.stopPropagation()}
40
+ >
41
+ {/* Main Video */}
42
+ <video
43
+ src={videoUrl}
44
+ controls
45
+ autoPlay
46
+ className="w-screen h-screen object-contain rounded-lg shadow-2xl"
47
+ style={{ maxWidth: '90vw', maxHeight: '90vh' }}
48
+ >
49
+ Your browser does not support the video tag.
50
+ </video>
51
+
52
+ {/* Close Button */}
53
+ <button
54
+ onClick={onClose}
55
+ className="absolute top-4 right-4 bg-black bg-opacity-50 hover:bg-opacity-70 text-white p-2 rounded-full transition-all duration-200"
56
+ aria-label="Close lightbox"
57
+ >
58
+ <X size={24} />
59
+ </button>
60
+
61
+ {/* Video Title */}
62
+ {title && (
63
+ <div className="absolute bottom-4 right-4 bg-black bg-opacity-50 text-white px-3 py-2 rounded-lg max-w-xs">
64
+ <div className="text-sm font-medium" dir="rtl">
65
+ {title}
66
+ </div>
67
+ </div>
68
+ )}
69
+ </motion.div>
70
+ </div>
71
+ )}
72
+ </AnimatePresence>
73
+ );
74
+ };
75
+
76
+ export default VideoLightbox;
@@ -0,0 +1,192 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { motion, AnimatePresence } from 'framer-motion';
3
+ import { X, ShoppingCart } from 'lucide-react';
4
+ import SmallButton from '../elements/SmallButton';
5
+ import useScrollLock from '../../hooks/useScrollLock';
6
+
7
+ const ItemDetailsModal = ({ item, isOpen, onClose, onAddToCart }) => {
8
+ const [localOpen, setLocalOpen] = useState(Boolean(isOpen));
9
+ const closingRequestedRef = useRef(false);
10
+
11
+ // Sync with external isOpen state
12
+ useEffect(() => {
13
+ if (isOpen) {
14
+ setLocalOpen(true);
15
+ closingRequestedRef.current = false;
16
+ } else {
17
+ setLocalOpen(false);
18
+ }
19
+ }, [isOpen]);
20
+
21
+ useScrollLock(isOpen);
22
+
23
+ // Handle modal close request
24
+ const requestClose = () => {
25
+ closingRequestedRef.current = true;
26
+ setLocalOpen(false);
27
+ };
28
+
29
+ // Handle exit complete
30
+ const handleExitComplete = () => {
31
+ if (closingRequestedRef.current) {
32
+ onClose();
33
+ closingRequestedRef.current = false;
34
+ }
35
+ };
36
+
37
+ // Handle add to cart
38
+ const handleAddToCart = () => {
39
+ onAddToCart(item);
40
+ requestClose();
41
+ };
42
+
43
+ if (!item) return null;
44
+
45
+ // Calculate discount info
46
+ const hasDiscount = item.discountPrice && item.discountPrice < item.price;
47
+ const discountPercentage = hasDiscount
48
+ ? Math.round(((item.price - item.discountPrice) / item.price) * 100)
49
+ : 0;
50
+
51
+ return (
52
+ <div className={`fixed inset-0 z-50 flex items-center justify-center ${isOpen ? '' : 'pointer-events-none'} supports-[height:100dvh]:h-[100dvh]`}>
53
+ <AnimatePresence initial={false} mode="wait" onExitComplete={handleExitComplete}>
54
+ {localOpen && (
55
+ <>
56
+ {/* Backdrop */}
57
+ <motion.div
58
+ key="backdrop"
59
+ initial={{ opacity: 0 }}
60
+ animate={{ opacity: 1 }}
61
+ exit={{ opacity: 0 }}
62
+ transition={{ duration: 0.18 }}
63
+ className="absolute inset-0 bg-black bg-opacity-30 backdrop-blur-sm pointer-events-auto"
64
+ onClick={requestClose}
65
+ aria-hidden="true"
66
+ />
67
+
68
+ {/* Modal Panel */}
69
+ <motion.div
70
+ key="modal-panel"
71
+ initial={{ opacity: 0, scale: 0.9, y: 20 }}
72
+ animate={{ opacity: 1, scale: 1, y: 0 }}
73
+ exit={{ opacity: 0, scale: 0.9, y: 20 }}
74
+ transition={{
75
+ type: "spring",
76
+ stiffness: 300,
77
+ damping: 30
78
+ }}
79
+ className="relative w-full max-w-2xl mx-4 max-h-[90vh] glass-card overflow-hidden pointer-events-auto"
80
+ dir="rtl"
81
+ role="dialog"
82
+ aria-modal="true"
83
+ onClick={(e) => e.stopPropagation()}
84
+ >
85
+ {/* Header */}
86
+ <div className="flex justify-between items-center p-6 border-b border-white/20">
87
+ <h2 className="subtitle font-bold text-right">{item.name || item.label}</h2>
88
+ <button
89
+ onClick={requestClose}
90
+ className="content-text hover:text-primary hover:bg-gray-100 p-2 rounded-md transition-colors duration-200"
91
+ aria-label="סגור"
92
+ >
93
+ <X size={20} />
94
+ </button>
95
+ </div>
96
+
97
+ {/* Content */}
98
+ <div className="p-6 overflow-y-auto max-h-[calc(90vh-140px)]">
99
+ <div className="grid md:grid-cols-2 gap-6">
100
+ {/* Image Section */}
101
+ <div className="relative">
102
+ <div className="relative w-full h-64 md:h-80 overflow-hidden rounded-xl bg-gray-100">
103
+ {item.imageUrl ? (
104
+ <img
105
+ src={item.imageUrl}
106
+ alt={item.name || item.label}
107
+ className="w-full h-full object-cover"
108
+ onError={(e) => {
109
+ e.target.style.display = 'none';
110
+ e.target.nextElementSibling.style.display = 'flex';
111
+ }}
112
+ />
113
+ ) : null}
114
+
115
+ {/* Image Fallback */}
116
+ <div
117
+ className="w-full h-full bg-gradient-to-br from-gray-100 to-gray-200 flex items-center justify-center"
118
+ style={{ display: item.imageUrl ? 'none' : 'flex' }}
119
+ >
120
+ <div className="text-center text-gray-400">
121
+ <div className="text-4xl mb-2">🍰</div>
122
+ <div className="text-sm font-medium">תמונה</div>
123
+ </div>
124
+ </div>
125
+
126
+ {/* Discount Badge */}
127
+ {hasDiscount && (
128
+ <div className="absolute top-3 left-3 bg-red-500 text-white px-3 py-1 rounded-full text-sm font-bold shadow-lg">
129
+ -{discountPercentage}%
130
+ </div>
131
+ )}
132
+ </div>
133
+ </div>
134
+
135
+ {/* Details Section */}
136
+ <div className="space-y-4">
137
+ {/* Description */}
138
+ {item.description && (
139
+ <div>
140
+ <h3 className="content-text font-medium mb-2">תיאור</h3>
141
+ <p className="caption-text leading-relaxed">
142
+ {item.description}
143
+ </p>
144
+ </div>
145
+ )}
146
+
147
+ {/* Price Section */}
148
+ <div className="space-y-2">
149
+ <h3 className="content-text font-medium">מחיר</h3>
150
+ {hasDiscount ? (
151
+ <div className="space-y-2">
152
+ <div className="flex items-center gap-3">
153
+ <span className="text-2xl font-bold text-price">
154
+ ₪{item.discountPrice}
155
+ </span>
156
+ <span className="text-sm text-gray-400 line-through">
157
+ ₪{item.price}
158
+ </span>
159
+ </div>
160
+ <div className="text-sm text-price font-medium">
161
+ חיסכון ₪{item.price - item.discountPrice}
162
+ </div>
163
+ </div>
164
+ ) : (
165
+ <div className="text-2xl font-bold text-price">
166
+ ₪{item.price}
167
+ </div>
168
+ )}
169
+ </div>
170
+
171
+ {/* Add to Cart Button */}
172
+ <div className="pt-4">
173
+ <SmallButton
174
+ onClick={handleAddToCart}
175
+ className="w-full flex items-center justify-center gap-2"
176
+ >
177
+ <ShoppingCart size={16} />
178
+ הוסף לעגלה
179
+ </SmallButton>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </motion.div>
185
+ </>
186
+ )}
187
+ </AnimatePresence>
188
+ </div>
189
+ );
190
+ };
191
+
192
+ export default ItemDetailsModal;
@@ -0,0 +1,24 @@
1
+
2
+ const CategoryList = ({ categories, selectedCategory, onCategorySelect, className = '' }) => (
3
+ <div className={className}>
4
+ <h3 className="subtitle font-semibold mb-4">קטגוריות</h3>
5
+ <div className="space-y-2">
6
+ {categories.map((category, index) => (
7
+ <button
8
+ key={index}
9
+ onClick={() => onCategorySelect(category)}
10
+ className={`w-full text-right p-3 rounded-lg transition-all duration-200 ${selectedCategory === category
11
+ ? 'text-white'
12
+ : 'hover:opacity-80'
13
+ }`}
14
+ style={selectedCategory === category ? { backgroundColor: 'var(--primary)' } : {}}
15
+ dir="rtl"
16
+ >
17
+ <span className="content-text">{category}</span>
18
+ </button>
19
+ ))}
20
+ </div>
21
+ </div>
22
+ );
23
+
24
+ export default CategoryList