@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,107 +0,0 @@
|
|
|
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;
|
|
@@ -1,38 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,76 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,101 +0,0 @@
|
|
|
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;
|
|
@@ -1,49 +0,0 @@
|
|
|
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;
|