hey-pharmacist-ecommerce 1.0.0
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/README.md +269 -0
- package/dist/index.d.mts +564 -0
- package/dist/index.d.ts +564 -0
- package/dist/index.js +7541 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +7485 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +70 -0
- package/src/components/CartItem.tsx +103 -0
- package/src/components/EmptyState.tsx +27 -0
- package/src/components/Footer.tsx +147 -0
- package/src/components/Header.tsx +151 -0
- package/src/components/OrderCard.tsx +98 -0
- package/src/components/ProductCard.tsx +122 -0
- package/src/components/ui/Badge.tsx +31 -0
- package/src/components/ui/Button.tsx +61 -0
- package/src/components/ui/Input.tsx +45 -0
- package/src/components/ui/Modal.tsx +79 -0
- package/src/components/ui/Skeleton.tsx +46 -0
- package/src/hooks/useOrders.ts +98 -0
- package/src/hooks/useProducts.ts +125 -0
- package/src/index.ts +71 -0
- package/src/lib/Apis/api.ts +46 -0
- package/src/lib/Apis/apis/addresses-api.ts +1461 -0
- package/src/lib/Apis/apis/auth-api.ts +945 -0
- package/src/lib/Apis/apis/blogs-api.ts +582 -0
- package/src/lib/Apis/apis/cart-api.ts +456 -0
- package/src/lib/Apis/apis/categories-api.ts +725 -0
- package/src/lib/Apis/apis/chats-api.ts +1101 -0
- package/src/lib/Apis/apis/contact-us-api.ts +394 -0
- package/src/lib/Apis/apis/discounts-api.ts +763 -0
- package/src/lib/Apis/apis/drafts-api.ts +448 -0
- package/src/lib/Apis/apis/events-api.ts +1311 -0
- package/src/lib/Apis/apis/file-proccesor-api.ts +293 -0
- package/src/lib/Apis/apis/health-api.ts +119 -0
- package/src/lib/Apis/apis/images-api.ts +271 -0
- package/src/lib/Apis/apis/inventory-api.ts +375 -0
- package/src/lib/Apis/apis/marketing-api.ts +3099 -0
- package/src/lib/Apis/apis/notifications-api.ts +843 -0
- package/src/lib/Apis/apis/open-aiapi.ts +513 -0
- package/src/lib/Apis/apis/orders-api.ts +1343 -0
- package/src/lib/Apis/apis/payment-methods-api.ts +411 -0
- package/src/lib/Apis/apis/payments-api.ts +469 -0
- package/src/lib/Apis/apis/product-attributes-api.ts +538 -0
- package/src/lib/Apis/apis/product-favorite-list-api.ts +321 -0
- package/src/lib/Apis/apis/product-variants-api.ts +648 -0
- package/src/lib/Apis/apis/products-api.ts +1442 -0
- package/src/lib/Apis/apis/review-api.ts +1383 -0
- package/src/lib/Apis/apis/roles-api.ts +614 -0
- package/src/lib/Apis/apis/shipping-api.ts +703 -0
- package/src/lib/Apis/apis/statistics-api.ts +234 -0
- package/src/lib/Apis/apis/stores-api.ts +1519 -0
- package/src/lib/Apis/apis/sub-categories-api.ts +1208 -0
- package/src/lib/Apis/apis/user-groups-api.ts +1198 -0
- package/src/lib/Apis/apis/users-api.ts +1403 -0
- package/src/lib/Apis/apis/web-hooks-api.ts +198 -0
- package/src/lib/Apis/base.ts +70 -0
- package/src/lib/Apis/configuration.ts +75 -0
- package/src/lib/Apis/index.ts +17 -0
- package/src/lib/Apis/models/add-contact-to-list-dto.ts +33 -0
- package/src/lib/Apis/models/add-message-dto.ts +56 -0
- package/src/lib/Apis/models/address-created-request.ts +134 -0
- package/src/lib/Apis/models/address.ts +164 -0
- package/src/lib/Apis/models/allow-user-credit-dto.ts +27 -0
- package/src/lib/Apis/models/appointment.ts +75 -0
- package/src/lib/Apis/models/available-dates-dto.ts +33 -0
- package/src/lib/Apis/models/available-suggested-dates-dto.ts +33 -0
- package/src/lib/Apis/models/blog.ts +75 -0
- package/src/lib/Apis/models/browser-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/bulk-move-subcategories-dto.ts +33 -0
- package/src/lib/Apis/models/bulk-unassign-subcategories-dto.ts +27 -0
- package/src/lib/Apis/models/campaign-content-response-dto.ts +40 -0
- package/src/lib/Apis/models/campaign-draft-dto.ts +175 -0
- package/src/lib/Apis/models/campaign-draft-response-dto.ts +40 -0
- package/src/lib/Apis/models/campaign-draft-schedule-dto.ts +49 -0
- package/src/lib/Apis/models/campaign-draft-schedule-response-dto.ts +40 -0
- package/src/lib/Apis/models/campaign-draft-sending-dto.ts +43 -0
- package/src/lib/Apis/models/campaign-draft-sending-response-dto.ts +40 -0
- package/src/lib/Apis/models/cart-body-dto.ts +40 -0
- package/src/lib/Apis/models/cart-body-populated.ts +47 -0
- package/src/lib/Apis/models/cart-item-populated.ts +41 -0
- package/src/lib/Apis/models/cart-item.ts +33 -0
- package/src/lib/Apis/models/cart-response-dto.ts +70 -0
- package/src/lib/Apis/models/categories-paginated-response-dto.ts +52 -0
- package/src/lib/Apis/models/category-filters.ts +40 -0
- package/src/lib/Apis/models/category-populated.ts +106 -0
- package/src/lib/Apis/models/category-sub-category-populated.ts +51 -0
- package/src/lib/Apis/models/category.ts +99 -0
- package/src/lib/Apis/models/categorys-headlines-response-dto.ts +40 -0
- package/src/lib/Apis/models/change-user-email-dto.ts +27 -0
- package/src/lib/Apis/models/chat.ts +33 -0
- package/src/lib/Apis/models/check-notifications-response-dto.ts +33 -0
- package/src/lib/Apis/models/contact-aggregated-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/contact-full-dto.ts +93 -0
- package/src/lib/Apis/models/contact-full-response-dto.ts +40 -0
- package/src/lib/Apis/models/contact-list-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/contact-lists-response-dto.ts +40 -0
- package/src/lib/Apis/models/contact-us.ts +81 -0
- package/src/lib/Apis/models/country-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/create-address-dto.ts +134 -0
- package/src/lib/Apis/models/create-blog-dto.ts +45 -0
- package/src/lib/Apis/models/create-category-dto.ts +45 -0
- package/src/lib/Apis/models/create-chat-dto.ts +39 -0
- package/src/lib/Apis/models/create-contact-dto.ts +39 -0
- package/src/lib/Apis/models/create-contact-list-dto.ts +27 -0
- package/src/lib/Apis/models/create-discount-dto.ts +208 -0
- package/src/lib/Apis/models/create-draft-dto.ts +67 -0
- package/src/lib/Apis/models/create-email-template-dto.ts +51 -0
- package/src/lib/Apis/models/create-event-dto.ts +52 -0
- package/src/lib/Apis/models/create-marketing-campaign-dto.ts +81 -0
- package/src/lib/Apis/models/create-message-dto.ts +57 -0
- package/src/lib/Apis/models/create-notification-dto.ts +75 -0
- package/src/lib/Apis/models/create-product-attribute-dto.ts +33 -0
- package/src/lib/Apis/models/create-product-dto.ts +94 -0
- package/src/lib/Apis/models/create-review-dto.ts +63 -0
- package/src/lib/Apis/models/create-role-dto.ts +57 -0
- package/src/lib/Apis/models/create-single-variant-product-dto.ts +155 -0
- package/src/lib/Apis/models/create-store-address-dto.ts +134 -0
- package/src/lib/Apis/models/create-store-dto.ts +105 -0
- package/src/lib/Apis/models/create-sub-category-dto.ts +45 -0
- package/src/lib/Apis/models/create-user-dto.ts +89 -0
- package/src/lib/Apis/models/create-user-group-dto.ts +39 -0
- package/src/lib/Apis/models/create-variant-dto.ts +119 -0
- package/src/lib/Apis/models/create-zone-dto.ts +82 -0
- package/src/lib/Apis/models/custom-product-dto.ts +63 -0
- package/src/lib/Apis/models/default-payment-method-request-dto.ts +27 -0
- package/src/lib/Apis/models/delete-file-dto.ts +27 -0
- package/src/lib/Apis/models/delete-many-files-dto.ts +27 -0
- package/src/lib/Apis/models/discount-paginated-response.ts +52 -0
- package/src/lib/Apis/models/discount.ts +245 -0
- package/src/lib/Apis/models/discounts-insights-dto.ts +57 -0
- package/src/lib/Apis/models/draft.ts +79 -0
- package/src/lib/Apis/models/email-invoice-dto.ts +45 -0
- package/src/lib/Apis/models/email-template-response-dto.ts +117 -0
- package/src/lib/Apis/models/event.ts +76 -0
- package/src/lib/Apis/models/extended-product-dto.ts +204 -0
- package/src/lib/Apis/models/fileproccesor-upload-body.ts +27 -0
- package/src/lib/Apis/models/forget-password.ts +27 -0
- package/src/lib/Apis/models/frequently-bought-product-dto.ts +71 -0
- package/src/lib/Apis/models/general-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/generate-day-slots-dto.ts +51 -0
- package/src/lib/Apis/models/generate-month-slots-dto.ts +57 -0
- package/src/lib/Apis/models/generate-week-slots-dto.ts +57 -0
- package/src/lib/Apis/models/google-analytics-request-dto.ts +55 -0
- package/src/lib/Apis/models/google-analytics-response-dto.ts +39 -0
- package/src/lib/Apis/models/group-with-no-users-dto.ts +75 -0
- package/src/lib/Apis/models/group-with-users-dto.ts +76 -0
- package/src/lib/Apis/models/images-upload-body.ts +27 -0
- package/src/lib/Apis/models/index.ts +197 -0
- package/src/lib/Apis/models/inventory-paginated-response.ts +75 -0
- package/src/lib/Apis/models/link-stats-response-dto.ts +40 -0
- package/src/lib/Apis/models/login-dto.ts +33 -0
- package/src/lib/Apis/models/manual-discount.ts +49 -0
- package/src/lib/Apis/models/manual-order-dto.ts +133 -0
- package/src/lib/Apis/models/manual-shipping-dto.ts +45 -0
- package/src/lib/Apis/models/marketing-campaign-content-dto.ts +27 -0
- package/src/lib/Apis/models/marketing-list-contact-dto.ts +51 -0
- package/src/lib/Apis/models/move-subcategory-dto.ts +27 -0
- package/src/lib/Apis/models/my-favorite-list-dto.ts +52 -0
- package/src/lib/Apis/models/notification.ts +93 -0
- package/src/lib/Apis/models/object-id.ts +21 -0
- package/src/lib/Apis/models/open-api.ts +33 -0
- package/src/lib/Apis/models/order-paginated-response.ts +52 -0
- package/src/lib/Apis/models/order.ts +214 -0
- package/src/lib/Apis/models/orders-insights-dto.ts +69 -0
- package/src/lib/Apis/models/paginated-products-dto.ts +52 -0
- package/src/lib/Apis/models/payment-method-data.ts +34 -0
- package/src/lib/Apis/models/payment-method.ts +51 -0
- package/src/lib/Apis/models/payment-time-line-dto.ts +56 -0
- package/src/lib/Apis/models/payment.ts +182 -0
- package/src/lib/Apis/models/payments-insights-dto.ts +69 -0
- package/src/lib/Apis/models/payments-paginated-response.ts +52 -0
- package/src/lib/Apis/models/pick-type-class.ts +51 -0
- package/src/lib/Apis/models/populated-chat-dto.ts +95 -0
- package/src/lib/Apis/models/populated-discount.ts +246 -0
- package/src/lib/Apis/models/populated-order.ts +209 -0
- package/src/lib/Apis/models/prefered-pick-or-delivery-time-dto.ts +33 -0
- package/src/lib/Apis/models/price-range.ts +33 -0
- package/src/lib/Apis/models/product-attribute.ts +57 -0
- package/src/lib/Apis/models/product-variant.ts +167 -0
- package/src/lib/Apis/models/product.ts +136 -0
- package/src/lib/Apis/models/products-insights-dto.ts +45 -0
- package/src/lib/Apis/models/rate-dto.ts +123 -0
- package/src/lib/Apis/models/refill-request-dto.ts +75 -0
- package/src/lib/Apis/models/refill-request.ts +105 -0
- package/src/lib/Apis/models/register-or-login-with-gmail.ts +27 -0
- package/src/lib/Apis/models/reserve-appointment.ts +261 -0
- package/src/lib/Apis/models/review.ts +93 -0
- package/src/lib/Apis/models/role.ts +69 -0
- package/src/lib/Apis/models/schedule-campaign-draft-dto.ts +27 -0
- package/src/lib/Apis/models/send-test-email-dto.ts +28 -0
- package/src/lib/Apis/models/shallow-parent-category-dto.ts +33 -0
- package/src/lib/Apis/models/shipment-details-dto.ts +88 -0
- package/src/lib/Apis/models/shipment-status-dto.ts +117 -0
- package/src/lib/Apis/models/shipment-with-order.ts +105 -0
- package/src/lib/Apis/models/shipment.ts +104 -0
- package/src/lib/Apis/models/shipping-info.ts +88 -0
- package/src/lib/Apis/models/single-browser-stats-dto.ts +45 -0
- package/src/lib/Apis/models/single-contact-aggregated-stats-dto.ts +129 -0
- package/src/lib/Apis/models/single-contact-list-stats-dto.ts +117 -0
- package/src/lib/Apis/models/single-country-stats-dto.ts +39 -0
- package/src/lib/Apis/models/single-general-stats.ts +153 -0
- package/src/lib/Apis/models/single-link-stats-dto.ts +39 -0
- package/src/lib/Apis/models/single-message-populated.ts +59 -0
- package/src/lib/Apis/models/single-notification-dto.ts +99 -0
- package/src/lib/Apis/models/single-product-media.ts +74 -0
- package/src/lib/Apis/models/single-recipient-dto.ts +33 -0
- package/src/lib/Apis/models/single-suggest-attribute.ts +33 -0
- package/src/lib/Apis/models/statistic-dto.ts +171 -0
- package/src/lib/Apis/models/store-entity.ts +117 -0
- package/src/lib/Apis/models/store.ts +135 -0
- package/src/lib/Apis/models/sub-category-headlines-only-response-dto.ts +39 -0
- package/src/lib/Apis/models/sub-category.ts +93 -0
- package/src/lib/Apis/models/suggest-attributes.ts +28 -0
- package/src/lib/Apis/models/suggested-slot.ts +33 -0
- package/src/lib/Apis/models/table-cell-dto.ts +33 -0
- package/src/lib/Apis/models/table-dto.ts +34 -0
- package/src/lib/Apis/models/tadmin-session-data.ts +47 -0
- package/src/lib/Apis/models/track-dto.ts +94 -0
- package/src/lib/Apis/models/tracking-status-location-base.ts +45 -0
- package/src/lib/Apis/models/tracking-status-substatus.ts +39 -0
- package/src/lib/Apis/models/tracking-status.ts +71 -0
- package/src/lib/Apis/models/transfere-patient-request.ts +123 -0
- package/src/lib/Apis/models/transfere-patients-request-dto.ts +99 -0
- package/src/lib/Apis/models/tuser-session-data.ts +34 -0
- package/src/lib/Apis/models/update-address-dto.ts +134 -0
- package/src/lib/Apis/models/update-blog-dto.ts +45 -0
- package/src/lib/Apis/models/update-campaign-draft-content-dto.ts +27 -0
- package/src/lib/Apis/models/update-category-dto.ts +45 -0
- package/src/lib/Apis/models/update-discount-dto.ts +208 -0
- package/src/lib/Apis/models/update-event-dto.ts +52 -0
- package/src/lib/Apis/models/update-items-order-dto.ts +27 -0
- package/src/lib/Apis/models/update-marketing-camp-draft-dto.ts +81 -0
- package/src/lib/Apis/models/update-message-dto.ts +57 -0
- package/src/lib/Apis/models/update-product-attribute-dto.ts +33 -0
- package/src/lib/Apis/models/update-product-dto.ts +96 -0
- package/src/lib/Apis/models/update-product-variant-dto.ts +119 -0
- package/src/lib/Apis/models/update-refill-request-dto.ts +75 -0
- package/src/lib/Apis/models/update-review-dto.ts +63 -0
- package/src/lib/Apis/models/update-role-dto.ts +57 -0
- package/src/lib/Apis/models/update-store-dto.ts +105 -0
- package/src/lib/Apis/models/update-sub-category-dto.ts +45 -0
- package/src/lib/Apis/models/update-transfere-patients-request-dto.ts +99 -0
- package/src/lib/Apis/models/update-user-dto.ts +239 -0
- package/src/lib/Apis/models/update-user-group-dto.ts +39 -0
- package/src/lib/Apis/models/update-zone-dto.ts +82 -0
- package/src/lib/Apis/models/upload-pdf-body.ts +27 -0
- package/src/lib/Apis/models/used-by.ts +87 -0
- package/src/lib/Apis/models/user-entity.ts +220 -0
- package/src/lib/Apis/models/user-group.ts +75 -0
- package/src/lib/Apis/models/user-insights-dto.ts +39 -0
- package/src/lib/Apis/models/user-with-no-id.ts +226 -0
- package/src/lib/Apis/models/user.ts +232 -0
- package/src/lib/Apis/models/users-paginated-response.ts +52 -0
- package/src/lib/Apis/models/verify-email-dto.ts +33 -0
- package/src/lib/Apis/models/zone-single-size.ts +51 -0
- package/src/lib/Apis/models/zone.ts +106 -0
- package/src/lib/Apis/wrapper.ts +37 -0
- package/src/lib/api/auth.ts +81 -0
- package/src/lib/api/cart.ts +42 -0
- package/src/lib/api/client.ts +118 -0
- package/src/lib/api/orders.ts +53 -0
- package/src/lib/api/products.ts +51 -0
- package/src/lib/api-adapter/auth-adapter.ts +196 -0
- package/src/lib/api-adapter/cart-adapter.ts +193 -0
- package/src/lib/api-adapter/config.ts +76 -0
- package/src/lib/api-adapter/index.ts +13 -0
- package/src/lib/api-adapter/mappers.ts +147 -0
- package/src/lib/api-adapter/orders-adapter.ts +195 -0
- package/src/lib/api-adapter/products-adapter.ts +194 -0
- package/src/lib/types/index.ts +152 -0
- package/src/lib/utils/colors.ts +51 -0
- package/src/lib/utils/format.ts +48 -0
- package/src/providers/AuthProvider.tsx +117 -0
- package/src/providers/CartProvider.tsx +131 -0
- package/src/providers/EcommerceProvider.tsx +34 -0
- package/src/providers/ThemeProvider.tsx +57 -0
- package/src/screens/CartScreen.tsx +140 -0
- package/src/screens/CheckoutScreen.tsx +340 -0
- package/src/screens/CurrentOrdersScreen.tsx +85 -0
- package/src/screens/LoginScreen.tsx +149 -0
- package/src/screens/OrdersScreen.tsx +86 -0
- package/src/screens/ProductDetailScreen.tsx +255 -0
- package/src/screens/ProfileScreen.tsx +211 -0
- package/src/screens/RegisterScreen.tsx +200 -0
- package/src/screens/ShopScreen.tsx +233 -0
- package/src/styles/globals.css +51 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import { ShoppingCart, Heart } from 'lucide-react';
|
|
6
|
+
import { Product } from '@/lib/types';
|
|
7
|
+
import { formatPrice } from '@/lib/utils/format';
|
|
8
|
+
import { useCart } from '@/providers/CartProvider';
|
|
9
|
+
import Image from 'next/image';
|
|
10
|
+
|
|
11
|
+
interface ProductCardProps {
|
|
12
|
+
product: Product;
|
|
13
|
+
onClickProduct?: (product: Product) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ProductCard({ product, onClickProduct }: ProductCardProps) {
|
|
17
|
+
const { addToCart } = useCart();
|
|
18
|
+
const [isAdding, setIsAdding] = useState(false);
|
|
19
|
+
const [isFavorited, setIsFavorited] = useState(false);
|
|
20
|
+
|
|
21
|
+
const handleAddToCart = async (e: React.MouseEvent) => {
|
|
22
|
+
e.stopPropagation();
|
|
23
|
+
setIsAdding(true);
|
|
24
|
+
try {
|
|
25
|
+
await addToCart(product.id);
|
|
26
|
+
} finally {
|
|
27
|
+
setIsAdding(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const handleFavorite = (e: React.MouseEvent) => {
|
|
32
|
+
e.stopPropagation();
|
|
33
|
+
setIsFavorited(!isFavorited);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const discount = product.compareAtPrice
|
|
37
|
+
? Math.round(((product.compareAtPrice - product.price) / product.compareAtPrice) * 100)
|
|
38
|
+
: 0;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<motion.div
|
|
42
|
+
whileHover={{ y: -8 }}
|
|
43
|
+
className="group bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-2xl transition-all duration-300 cursor-pointer"
|
|
44
|
+
onClick={() => onClickProduct?.(product)}
|
|
45
|
+
>
|
|
46
|
+
{/* Image Container */}
|
|
47
|
+
<div className="relative aspect-square overflow-hidden bg-gray-100">
|
|
48
|
+
<Image
|
|
49
|
+
src={product.images[0] || '/placeholder-product.jpg'}
|
|
50
|
+
alt={product.name}
|
|
51
|
+
fill
|
|
52
|
+
className="object-cover group-hover:scale-110 transition-transform duration-500"
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
{/* Badges */}
|
|
56
|
+
<div className="absolute top-4 left-4 flex flex-col gap-2">
|
|
57
|
+
{discount > 0 && (
|
|
58
|
+
<span className="bg-red-500 text-white px-3 py-1 rounded-full text-sm font-bold">
|
|
59
|
+
-{discount}%
|
|
60
|
+
</span>
|
|
61
|
+
)}
|
|
62
|
+
{!product.inStock && (
|
|
63
|
+
<span className="bg-gray-900 text-white px-3 py-1 rounded-full text-sm font-bold">
|
|
64
|
+
Out of Stock
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Favorite Button */}
|
|
70
|
+
<button
|
|
71
|
+
onClick={handleFavorite}
|
|
72
|
+
className="absolute top-4 right-4 p-2 bg-white/90 backdrop-blur-sm rounded-full shadow-lg opacity-0 group-hover:opacity-100 transition-all duration-300 hover:scale-110"
|
|
73
|
+
>
|
|
74
|
+
<Heart
|
|
75
|
+
className={`w-5 h-5 ${isFavorited ? 'fill-red-500 text-red-500' : 'text-gray-700'}`}
|
|
76
|
+
/>
|
|
77
|
+
</button>
|
|
78
|
+
|
|
79
|
+
{/* Quick Add Button - Shows on hover */}
|
|
80
|
+
{product.inStock && (
|
|
81
|
+
<motion.button
|
|
82
|
+
onClick={handleAddToCart}
|
|
83
|
+
disabled={isAdding}
|
|
84
|
+
className="absolute bottom-4 left-4 right-4 bg-primary-600 text-white py-3 rounded-xl font-medium opacity-0 group-hover:opacity-100 transition-all duration-300 hover:bg-primary-700 flex items-center justify-center gap-2 shadow-lg"
|
|
85
|
+
whileTap={{ scale: 0.95 }}
|
|
86
|
+
>
|
|
87
|
+
<ShoppingCart className="w-5 h-5" />
|
|
88
|
+
{isAdding ? 'Adding...' : 'Quick Add'}
|
|
89
|
+
</motion.button>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
{/* Content */}
|
|
94
|
+
<div className="p-4">
|
|
95
|
+
{/* Category */}
|
|
96
|
+
{product.category && (
|
|
97
|
+
<p className="text-xs text-gray-500 uppercase tracking-wider mb-2">
|
|
98
|
+
{product.category}
|
|
99
|
+
</p>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
{/* Product Name */}
|
|
103
|
+
<h3 className="text-lg font-semibold text-gray-900 mb-2 line-clamp-2 group-hover:text-primary-600 transition-colors">
|
|
104
|
+
{product.name}
|
|
105
|
+
</h3>
|
|
106
|
+
|
|
107
|
+
{/* Price */}
|
|
108
|
+
<div className="flex items-baseline gap-2">
|
|
109
|
+
<span className="text-2xl font-bold text-gray-900">
|
|
110
|
+
{formatPrice(product.price)}
|
|
111
|
+
</span>
|
|
112
|
+
{product.compareAtPrice && (
|
|
113
|
+
<span className="text-sm text-gray-500 line-through">
|
|
114
|
+
{formatPrice(product.compareAtPrice)}
|
|
115
|
+
</span>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</motion.div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface BadgeProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'gray';
|
|
6
|
+
size?: 'sm' | 'md';
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Badge({ children, variant = 'primary', size = 'md', className = '' }: BadgeProps) {
|
|
11
|
+
const variants = {
|
|
12
|
+
primary: 'bg-primary-100 text-primary-700 border-primary-200',
|
|
13
|
+
secondary: 'bg-secondary-100 text-secondary-700 border-secondary-200',
|
|
14
|
+
success: 'bg-green-100 text-green-700 border-green-200',
|
|
15
|
+
warning: 'bg-yellow-100 text-yellow-700 border-yellow-200',
|
|
16
|
+
danger: 'bg-red-100 text-red-700 border-red-200',
|
|
17
|
+
gray: 'bg-gray-100 text-gray-700 border-gray-200',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const sizes = {
|
|
21
|
+
sm: 'px-2 py-1 text-xs',
|
|
22
|
+
md: 'px-3 py-1 text-sm',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<span className={`inline-flex items-center font-medium rounded-full border ${variants[variant]} ${sizes[size]} ${className}`}>
|
|
27
|
+
{children}
|
|
28
|
+
</span>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { motion } from 'framer-motion';
|
|
3
|
+
|
|
4
|
+
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
5
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
isLoading?: boolean;
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function Button({
|
|
12
|
+
variant = 'primary',
|
|
13
|
+
size = 'md',
|
|
14
|
+
isLoading = false,
|
|
15
|
+
disabled,
|
|
16
|
+
className = '',
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}: ButtonProps) {
|
|
20
|
+
const baseStyles = 'font-medium rounded-lg transition-all duration-200 inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed';
|
|
21
|
+
|
|
22
|
+
const variants = {
|
|
23
|
+
primary: 'bg-primary-600 text-white hover:bg-primary-700 shadow-lg shadow-primary-500/30 hover:shadow-xl hover:shadow-primary-500/40',
|
|
24
|
+
secondary: 'bg-secondary-600 text-white hover:bg-secondary-700 shadow-lg shadow-secondary-500/30 hover:shadow-xl hover:shadow-secondary-500/40',
|
|
25
|
+
outline: 'border-2 border-primary-600 text-primary-600 hover:bg-primary-50',
|
|
26
|
+
ghost: 'text-gray-700 hover:bg-gray-100',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const sizes = {
|
|
30
|
+
sm: 'px-4 py-2 text-sm',
|
|
31
|
+
md: 'px-6 py-3 text-base',
|
|
32
|
+
lg: 'px-8 py-4 text-lg',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<motion.div
|
|
37
|
+
whileHover={{ scale: 1.02 }}
|
|
38
|
+
whileTap={{ scale: 0.98 }}
|
|
39
|
+
className="inline-block"
|
|
40
|
+
>
|
|
41
|
+
<button
|
|
42
|
+
className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
|
|
43
|
+
disabled={disabled || isLoading}
|
|
44
|
+
{...props}
|
|
45
|
+
>
|
|
46
|
+
{isLoading ? (
|
|
47
|
+
<>
|
|
48
|
+
<svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
49
|
+
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
50
|
+
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
51
|
+
</svg>
|
|
52
|
+
Loading...
|
|
53
|
+
</>
|
|
54
|
+
) : (
|
|
55
|
+
children
|
|
56
|
+
)}
|
|
57
|
+
</button>
|
|
58
|
+
</motion.div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
|
|
3
|
+
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
+
label?: string;
|
|
5
|
+
error?: string;
|
|
6
|
+
helperText?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
10
|
+
({ label, error, helperText, className = '', ...props }, ref) => {
|
|
11
|
+
return (
|
|
12
|
+
<div className="w-full">
|
|
13
|
+
{label && (
|
|
14
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
15
|
+
{label}
|
|
16
|
+
</label>
|
|
17
|
+
)}
|
|
18
|
+
<input
|
|
19
|
+
ref={ref}
|
|
20
|
+
className={`
|
|
21
|
+
w-full px-4 py-3 rounded-lg border-2 transition-all duration-200
|
|
22
|
+
${error
|
|
23
|
+
? 'border-red-500 focus:border-red-600 focus:ring-red-500/20'
|
|
24
|
+
: 'border-gray-200 focus:border-primary-500 focus:ring-primary-500/20'
|
|
25
|
+
}
|
|
26
|
+
focus:outline-none focus:ring-4
|
|
27
|
+
placeholder:text-gray-400
|
|
28
|
+
disabled:bg-gray-50 disabled:cursor-not-allowed
|
|
29
|
+
${className}
|
|
30
|
+
`}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
{error && (
|
|
34
|
+
<p className="mt-2 text-sm text-red-600">{error}</p>
|
|
35
|
+
)}
|
|
36
|
+
{helperText && !error && (
|
|
37
|
+
<p className="mt-2 text-sm text-gray-500">{helperText}</p>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
Input.displayName = 'Input';
|
|
45
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useEffect } from 'react';
|
|
4
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
5
|
+
import { X } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
interface ModalProps {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
title?: string;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function Modal({ isOpen, onClose, title, children, size = 'md' }: ModalProps) {
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (isOpen) {
|
|
18
|
+
document.body.style.overflow = 'hidden';
|
|
19
|
+
} else {
|
|
20
|
+
document.body.style.overflow = 'unset';
|
|
21
|
+
}
|
|
22
|
+
return () => {
|
|
23
|
+
document.body.style.overflow = 'unset';
|
|
24
|
+
};
|
|
25
|
+
}, [isOpen]);
|
|
26
|
+
|
|
27
|
+
const sizes = {
|
|
28
|
+
sm: 'max-w-md',
|
|
29
|
+
md: 'max-w-lg',
|
|
30
|
+
lg: 'max-w-2xl',
|
|
31
|
+
xl: 'max-w-4xl',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<AnimatePresence>
|
|
36
|
+
{isOpen && (
|
|
37
|
+
<>
|
|
38
|
+
{/* Backdrop */}
|
|
39
|
+
<motion.div
|
|
40
|
+
initial={{ opacity: 0 }}
|
|
41
|
+
animate={{ opacity: 1 }}
|
|
42
|
+
exit={{ opacity: 0 }}
|
|
43
|
+
onClick={onClose}
|
|
44
|
+
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
{/* Modal */}
|
|
48
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
49
|
+
<motion.div
|
|
50
|
+
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
51
|
+
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
52
|
+
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
53
|
+
className={`bg-white rounded-2xl shadow-2xl w-full ${sizes[size]} max-h-[90vh] overflow-hidden flex flex-col`}
|
|
54
|
+
>
|
|
55
|
+
{/* Header */}
|
|
56
|
+
{title && (
|
|
57
|
+
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
|
58
|
+
<h2 className="text-2xl font-bold text-gray-900">{title}</h2>
|
|
59
|
+
<button
|
|
60
|
+
onClick={onClose}
|
|
61
|
+
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
62
|
+
>
|
|
63
|
+
<X className="w-5 h-5" />
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
{/* Content */}
|
|
69
|
+
<div className="flex-1 overflow-y-auto p-6">
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
</motion.div>
|
|
73
|
+
</div>
|
|
74
|
+
</>
|
|
75
|
+
)}
|
|
76
|
+
</AnimatePresence>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface SkeletonProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function Skeleton({ className = '' }: SkeletonProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className={`animate-pulse bg-gradient-to-r from-gray-200 via-gray-300 to-gray-200 bg-[length:200%_100%] rounded ${className}`} />
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ProductCardSkeleton() {
|
|
14
|
+
return (
|
|
15
|
+
<div className="bg-white rounded-2xl overflow-hidden shadow-sm">
|
|
16
|
+
<Skeleton className="h-64 w-full" />
|
|
17
|
+
<div className="p-4 space-y-3">
|
|
18
|
+
<Skeleton className="h-6 w-3/4" />
|
|
19
|
+
<Skeleton className="h-4 w-1/2" />
|
|
20
|
+
<div className="flex justify-between items-center pt-2">
|
|
21
|
+
<Skeleton className="h-8 w-24" />
|
|
22
|
+
<Skeleton className="h-10 w-10 rounded-full" />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function OrderCardSkeleton() {
|
|
30
|
+
return (
|
|
31
|
+
<div className="bg-white rounded-2xl p-6 shadow-sm">
|
|
32
|
+
<div className="flex justify-between items-start mb-4">
|
|
33
|
+
<div className="space-y-2 flex-1">
|
|
34
|
+
<Skeleton className="h-6 w-32" />
|
|
35
|
+
<Skeleton className="h-4 w-48" />
|
|
36
|
+
</div>
|
|
37
|
+
<Skeleton className="h-6 w-20 rounded-full" />
|
|
38
|
+
</div>
|
|
39
|
+
<div className="space-y-3">
|
|
40
|
+
<Skeleton className="h-4 w-full" />
|
|
41
|
+
<Skeleton className="h-4 w-2/3" />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { Order } from '@/lib/types';
|
|
3
|
+
import { ordersApi } from '@/lib/api/orders';
|
|
4
|
+
|
|
5
|
+
export function useOrders(page: number = 1, limit: number = 10) {
|
|
6
|
+
const [orders, setOrders] = useState<Order[]>([]);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
8
|
+
const [error, setError] = useState<Error | null>(null);
|
|
9
|
+
const [pagination, setPagination] = useState({
|
|
10
|
+
page: 1,
|
|
11
|
+
limit: 10,
|
|
12
|
+
total: 0,
|
|
13
|
+
totalPages: 0,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const fetchOrders = useCallback(async () => {
|
|
17
|
+
setIsLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
const response = await ordersApi.getOrders(page, limit);
|
|
21
|
+
setOrders(response.data);
|
|
22
|
+
setPagination(response.pagination);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
setError(err as Error);
|
|
25
|
+
} finally {
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
}
|
|
28
|
+
}, [page, limit]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fetchOrders();
|
|
32
|
+
}, [fetchOrders]);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
orders,
|
|
36
|
+
isLoading,
|
|
37
|
+
error,
|
|
38
|
+
pagination,
|
|
39
|
+
refetch: fetchOrders,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useOrder(id: string) {
|
|
44
|
+
const [order, setOrder] = useState<Order | null>(null);
|
|
45
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
46
|
+
const [error, setError] = useState<Error | null>(null);
|
|
47
|
+
|
|
48
|
+
const fetchOrder = useCallback(async () => {
|
|
49
|
+
setIsLoading(true);
|
|
50
|
+
setError(null);
|
|
51
|
+
try {
|
|
52
|
+
const response = await ordersApi.getOrder(id);
|
|
53
|
+
if (response.success) {
|
|
54
|
+
setOrder(response.data);
|
|
55
|
+
}
|
|
56
|
+
} catch (err) {
|
|
57
|
+
setError(err as Error);
|
|
58
|
+
} finally {
|
|
59
|
+
setIsLoading(false);
|
|
60
|
+
}
|
|
61
|
+
}, [id]);
|
|
62
|
+
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (id) {
|
|
65
|
+
fetchOrder();
|
|
66
|
+
}
|
|
67
|
+
}, [id, fetchOrder]);
|
|
68
|
+
|
|
69
|
+
return { order, isLoading, error, refetch: fetchOrder };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useCurrentOrders() {
|
|
73
|
+
const [orders, setOrders] = useState<Order[]>([]);
|
|
74
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
75
|
+
const [error, setError] = useState<Error | null>(null);
|
|
76
|
+
|
|
77
|
+
const fetchCurrentOrders = useCallback(async () => {
|
|
78
|
+
setIsLoading(true);
|
|
79
|
+
setError(null);
|
|
80
|
+
try {
|
|
81
|
+
const response = await ordersApi.getCurrentOrders();
|
|
82
|
+
if (response.success) {
|
|
83
|
+
setOrders(response.data);
|
|
84
|
+
}
|
|
85
|
+
} catch (err) {
|
|
86
|
+
setError(err as Error);
|
|
87
|
+
} finally {
|
|
88
|
+
setIsLoading(false);
|
|
89
|
+
}
|
|
90
|
+
}, []);
|
|
91
|
+
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
fetchCurrentOrders();
|
|
94
|
+
}, [fetchCurrentOrders]);
|
|
95
|
+
|
|
96
|
+
return { orders, isLoading, error, refetch: fetchCurrentOrders };
|
|
97
|
+
}
|
|
98
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { Product, ProductFilters, Category } from '@/lib/types';
|
|
3
|
+
import { productsApi } from '@/lib/api/products';
|
|
4
|
+
|
|
5
|
+
export function useProducts(filters?: ProductFilters, page: number = 1, limit: number = 20) {
|
|
6
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
7
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
8
|
+
const [error, setError] = useState<Error | null>(null);
|
|
9
|
+
const [pagination, setPagination] = useState({
|
|
10
|
+
page: 1,
|
|
11
|
+
limit: 20,
|
|
12
|
+
total: 0,
|
|
13
|
+
totalPages: 0,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const fetchProducts = useCallback(async () => {
|
|
17
|
+
setIsLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
try {
|
|
20
|
+
const response = await productsApi.getProducts(filters, page, limit);
|
|
21
|
+
setProducts(response.data);
|
|
22
|
+
setPagination(response.pagination);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
setError(err as Error);
|
|
25
|
+
} finally {
|
|
26
|
+
setIsLoading(false);
|
|
27
|
+
}
|
|
28
|
+
}, [filters, page, limit]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
fetchProducts();
|
|
32
|
+
}, [fetchProducts]);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
products,
|
|
36
|
+
isLoading,
|
|
37
|
+
error,
|
|
38
|
+
pagination,
|
|
39
|
+
refetch: fetchProducts,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useProduct(id: string) {
|
|
44
|
+
const [product, setProduct] = useState<Product | null>(null);
|
|
45
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
46
|
+
const [error, setError] = useState<Error | null>(null);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const fetchProduct = async () => {
|
|
50
|
+
setIsLoading(true);
|
|
51
|
+
setError(null);
|
|
52
|
+
try {
|
|
53
|
+
const response = await productsApi.getProduct(id);
|
|
54
|
+
if (response.success) {
|
|
55
|
+
setProduct(response.data);
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
setError(err as Error);
|
|
59
|
+
} finally {
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (id) {
|
|
65
|
+
fetchProduct();
|
|
66
|
+
}
|
|
67
|
+
}, [id]);
|
|
68
|
+
|
|
69
|
+
return { product, isLoading, error };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useFeaturedProducts(limit: number = 8) {
|
|
73
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
74
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
75
|
+
const [error, setError] = useState<Error | null>(null);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const fetchFeaturedProducts = async () => {
|
|
79
|
+
setIsLoading(true);
|
|
80
|
+
setError(null);
|
|
81
|
+
try {
|
|
82
|
+
const response = await productsApi.getFeaturedProducts(limit);
|
|
83
|
+
if (response.success) {
|
|
84
|
+
setProducts(response.data);
|
|
85
|
+
}
|
|
86
|
+
} catch (err) {
|
|
87
|
+
setError(err as Error);
|
|
88
|
+
} finally {
|
|
89
|
+
setIsLoading(false);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
fetchFeaturedProducts();
|
|
94
|
+
}, [limit]);
|
|
95
|
+
|
|
96
|
+
return { products, isLoading, error };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function useCategories() {
|
|
100
|
+
const [categories, setCategories] = useState<Category[]>([]);
|
|
101
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
102
|
+
const [error, setError] = useState<Error | null>(null);
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
const fetchCategories = async () => {
|
|
106
|
+
setIsLoading(true);
|
|
107
|
+
setError(null);
|
|
108
|
+
try {
|
|
109
|
+
const response = await productsApi.getCategories();
|
|
110
|
+
if (response.success) {
|
|
111
|
+
setCategories(response.data);
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
setError(err as Error);
|
|
115
|
+
} finally {
|
|
116
|
+
setIsLoading(false);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
fetchCategories();
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
return { categories, isLoading, error };
|
|
124
|
+
}
|
|
125
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Main Provider
|
|
2
|
+
export { EcommerceProvider } from './providers/EcommerceProvider';
|
|
3
|
+
|
|
4
|
+
// Individual Providers
|
|
5
|
+
export { ThemeProvider, useTheme } from './providers/ThemeProvider';
|
|
6
|
+
export { AuthProvider, useAuth } from './providers/AuthProvider';
|
|
7
|
+
export { CartProvider, useCart } from './providers/CartProvider';
|
|
8
|
+
|
|
9
|
+
// Screens
|
|
10
|
+
export { ShopScreen } from './screens/ShopScreen';
|
|
11
|
+
export { ProductDetailScreen } from './screens/ProductDetailScreen';
|
|
12
|
+
export { CartScreen } from './screens/CartScreen';
|
|
13
|
+
export { CheckoutScreen } from './screens/CheckoutScreen';
|
|
14
|
+
export { LoginScreen } from './screens/LoginScreen';
|
|
15
|
+
export { RegisterScreen } from './screens/RegisterScreen';
|
|
16
|
+
export { ProfileScreen } from './screens/ProfileScreen';
|
|
17
|
+
export { OrdersScreen } from './screens/OrdersScreen';
|
|
18
|
+
export { CurrentOrdersScreen } from './screens/CurrentOrdersScreen';
|
|
19
|
+
|
|
20
|
+
// Components
|
|
21
|
+
export { Header } from './components/Header';
|
|
22
|
+
export { Footer } from './components/Footer';
|
|
23
|
+
export { ProductCard } from './components/ProductCard';
|
|
24
|
+
export { CartItem } from './components/CartItem';
|
|
25
|
+
export { OrderCard } from './components/OrderCard';
|
|
26
|
+
export { EmptyState } from './components/EmptyState';
|
|
27
|
+
|
|
28
|
+
// UI Components
|
|
29
|
+
export { Button } from './components/ui/Button';
|
|
30
|
+
export { Input } from './components/ui/Input';
|
|
31
|
+
export { Badge } from './components/ui/Badge';
|
|
32
|
+
export { Modal } from './components/ui/Modal';
|
|
33
|
+
export { Skeleton, ProductCardSkeleton, OrderCardSkeleton } from './components/ui/Skeleton';
|
|
34
|
+
|
|
35
|
+
// Hooks
|
|
36
|
+
export { useProducts, useProduct, useFeaturedProducts, useCategories } from './hooks/useProducts';
|
|
37
|
+
export { useOrders, useOrder, useCurrentOrders } from './hooks/useOrders';
|
|
38
|
+
|
|
39
|
+
// API
|
|
40
|
+
export { productsApi } from './lib/api/products';
|
|
41
|
+
export { authApi } from './lib/api/auth';
|
|
42
|
+
export { cartApi } from './lib/api/cart';
|
|
43
|
+
export { ordersApi } from './lib/api/orders';
|
|
44
|
+
|
|
45
|
+
// API Adapter (for advanced usage)
|
|
46
|
+
export { initializeApiAdapter, getApiConfiguration } from './lib/api-adapter';
|
|
47
|
+
|
|
48
|
+
// Types
|
|
49
|
+
export type {
|
|
50
|
+
EcommerceConfig,
|
|
51
|
+
Product,
|
|
52
|
+
ProductFilters,
|
|
53
|
+
Category,
|
|
54
|
+
CartItem as CartItemType,
|
|
55
|
+
Cart,
|
|
56
|
+
Order,
|
|
57
|
+
OrderItem,
|
|
58
|
+
OrderStatus,
|
|
59
|
+
Address,
|
|
60
|
+
User,
|
|
61
|
+
AuthTokens,
|
|
62
|
+
LoginCredentials,
|
|
63
|
+
RegisterData,
|
|
64
|
+
ApiResponse,
|
|
65
|
+
PaginatedResponse,
|
|
66
|
+
} from './lib/types';
|
|
67
|
+
|
|
68
|
+
// Utils
|
|
69
|
+
export { formatPrice, formatDate, truncate, getInitials } from './lib/utils/format';
|
|
70
|
+
export { generateColorShades, hexToRgb } from './lib/utils/colors';
|
|
71
|
+
|