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,149 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import { useForm } from 'react-hook-form';
|
|
6
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { Lock, Mail, Eye, EyeOff } from 'lucide-react';
|
|
9
|
+
import { Button } from '@/components/ui/Button';
|
|
10
|
+
import { Input } from '@/components/ui/Input';
|
|
11
|
+
import { useAuth } from '@/providers/AuthProvider';
|
|
12
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
13
|
+
import { toast } from 'sonner';
|
|
14
|
+
import Link from 'next/link';
|
|
15
|
+
|
|
16
|
+
const loginSchema = z.object({
|
|
17
|
+
email: z.string().email('Invalid email address'),
|
|
18
|
+
password: z.string().min(6, 'Password must be at least 6 characters'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
type LoginFormData = z.infer<typeof loginSchema>;
|
|
22
|
+
|
|
23
|
+
export function LoginScreen() {
|
|
24
|
+
const router = useRouter();
|
|
25
|
+
const searchParams = useSearchParams();
|
|
26
|
+
const redirectUrl = searchParams?.get('redirect') || '/';
|
|
27
|
+
const { login } = useAuth();
|
|
28
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
29
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
30
|
+
|
|
31
|
+
const {
|
|
32
|
+
register,
|
|
33
|
+
handleSubmit,
|
|
34
|
+
formState: { errors },
|
|
35
|
+
} = useForm<LoginFormData>({
|
|
36
|
+
resolver: zodResolver(loginSchema),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const onSubmit = async (data: LoginFormData) => {
|
|
40
|
+
setIsSubmitting(true);
|
|
41
|
+
try {
|
|
42
|
+
await login(data);
|
|
43
|
+
toast.success('Welcome back!');
|
|
44
|
+
router.push(redirectUrl);
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
toast.error(error.response?.data?.message || 'Invalid credentials');
|
|
47
|
+
} finally {
|
|
48
|
+
setIsSubmitting(false);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className="min-h-screen bg-gradient-to-br from-primary-600 via-primary-700 to-secondary-600 flex items-center justify-center p-4">
|
|
54
|
+
<motion.div
|
|
55
|
+
initial={{ opacity: 0, y: 20 }}
|
|
56
|
+
animate={{ opacity: 1, y: 0 }}
|
|
57
|
+
className="w-full max-w-md"
|
|
58
|
+
>
|
|
59
|
+
<div className="bg-white rounded-3xl shadow-2xl p-8">
|
|
60
|
+
{/* Header */}
|
|
61
|
+
<div className="text-center mb-8">
|
|
62
|
+
<div className="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
63
|
+
<Lock className="w-8 h-8 text-primary-600" />
|
|
64
|
+
</div>
|
|
65
|
+
<h1 className="text-3xl font-bold text-gray-900 mb-2">Welcome Back</h1>
|
|
66
|
+
<p className="text-gray-600">Sign in to your account to continue</p>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* Form */}
|
|
70
|
+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
|
71
|
+
<div>
|
|
72
|
+
<Input
|
|
73
|
+
type="email"
|
|
74
|
+
label="Email Address"
|
|
75
|
+
placeholder="you@example.com"
|
|
76
|
+
{...register('email')}
|
|
77
|
+
error={errors.email?.message}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<div className="relative">
|
|
82
|
+
<Input
|
|
83
|
+
type={showPassword ? 'text' : 'password'}
|
|
84
|
+
label="Password"
|
|
85
|
+
placeholder="••••••••"
|
|
86
|
+
{...register('password')}
|
|
87
|
+
error={errors.password?.message}
|
|
88
|
+
/>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
92
|
+
className="absolute right-3 top-[42px] text-gray-500 hover:text-gray-700"
|
|
93
|
+
>
|
|
94
|
+
{showPassword ? (
|
|
95
|
+
<EyeOff className="w-5 h-5" />
|
|
96
|
+
) : (
|
|
97
|
+
<Eye className="w-5 h-5" />
|
|
98
|
+
)}
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div className="flex items-center justify-between">
|
|
103
|
+
<label className="flex items-center gap-2 cursor-pointer">
|
|
104
|
+
<input
|
|
105
|
+
type="checkbox"
|
|
106
|
+
className="w-4 h-4 text-primary-600 rounded"
|
|
107
|
+
/>
|
|
108
|
+
<span className="text-sm text-gray-700">Remember me</span>
|
|
109
|
+
</label>
|
|
110
|
+
<Link
|
|
111
|
+
href="/forgot-password"
|
|
112
|
+
className="text-sm text-primary-600 hover:text-primary-700 font-medium"
|
|
113
|
+
>
|
|
114
|
+
Forgot password?
|
|
115
|
+
</Link>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<Button
|
|
119
|
+
type="submit"
|
|
120
|
+
size="lg"
|
|
121
|
+
isLoading={isSubmitting}
|
|
122
|
+
className="w-full"
|
|
123
|
+
>
|
|
124
|
+
Sign In
|
|
125
|
+
</Button>
|
|
126
|
+
</form>
|
|
127
|
+
|
|
128
|
+
{/* Divider */}
|
|
129
|
+
<div className="relative my-8">
|
|
130
|
+
<div className="absolute inset-0 flex items-center">
|
|
131
|
+
<div className="w-full border-t border-gray-200"></div>
|
|
132
|
+
</div>
|
|
133
|
+
<div className="relative flex justify-center text-sm">
|
|
134
|
+
<span className="px-4 bg-white text-gray-500">New to our store?</span>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* Register Link */}
|
|
139
|
+
<Link href="/register">
|
|
140
|
+
<Button variant="outline" size="lg" className="w-full">
|
|
141
|
+
Create an Account
|
|
142
|
+
</Button>
|
|
143
|
+
</Link>
|
|
144
|
+
</div>
|
|
145
|
+
</motion.div>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import { Package, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
6
|
+
import { OrderCard } from '@/components/OrderCard';
|
|
7
|
+
import { OrderCardSkeleton } from '@/components/ui/Skeleton';
|
|
8
|
+
import { EmptyState } from '@/components/EmptyState';
|
|
9
|
+
import { Button } from '@/components/ui/Button';
|
|
10
|
+
import { useOrders } from '@/hooks/useOrders';
|
|
11
|
+
import { useRouter } from 'next/navigation';
|
|
12
|
+
|
|
13
|
+
export function OrdersScreen() {
|
|
14
|
+
const router = useRouter();
|
|
15
|
+
const [page, setPage] = useState(1);
|
|
16
|
+
const { orders, isLoading, pagination } = useOrders(page, 10);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="min-h-screen bg-gray-50 py-12">
|
|
20
|
+
<div className="container mx-auto px-4 max-w-5xl">
|
|
21
|
+
{/* Header */}
|
|
22
|
+
<motion.div
|
|
23
|
+
initial={{ opacity: 0, y: 20 }}
|
|
24
|
+
animate={{ opacity: 1, y: 0 }}
|
|
25
|
+
className="mb-8"
|
|
26
|
+
>
|
|
27
|
+
<h1 className="text-4xl font-bold text-gray-900 mb-2">Order History</h1>
|
|
28
|
+
<p className="text-gray-600">
|
|
29
|
+
View and manage all your past orders
|
|
30
|
+
</p>
|
|
31
|
+
</motion.div>
|
|
32
|
+
|
|
33
|
+
{/* Orders List */}
|
|
34
|
+
{isLoading ? (
|
|
35
|
+
<div className="space-y-4">
|
|
36
|
+
{Array.from({ length: 3 }).map((_, i) => (
|
|
37
|
+
<OrderCardSkeleton key={i} />
|
|
38
|
+
))}
|
|
39
|
+
</div>
|
|
40
|
+
) : orders.length > 0 ? (
|
|
41
|
+
<>
|
|
42
|
+
<div className="space-y-4 mb-8">
|
|
43
|
+
{orders.map((order) => (
|
|
44
|
+
<OrderCard key={order.id} order={order} />
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
{/* Pagination */}
|
|
49
|
+
{pagination.totalPages > 1 && (
|
|
50
|
+
<div className="flex justify-center items-center gap-4">
|
|
51
|
+
<Button
|
|
52
|
+
variant="outline"
|
|
53
|
+
onClick={() => setPage(page - 1)}
|
|
54
|
+
disabled={page === 1}
|
|
55
|
+
>
|
|
56
|
+
<ChevronLeft className="w-5 h-5" />
|
|
57
|
+
Previous
|
|
58
|
+
</Button>
|
|
59
|
+
<span className="text-gray-700">
|
|
60
|
+
Page {page} of {pagination.totalPages}
|
|
61
|
+
</span>
|
|
62
|
+
<Button
|
|
63
|
+
variant="outline"
|
|
64
|
+
onClick={() => setPage(page + 1)}
|
|
65
|
+
disabled={page === pagination.totalPages}
|
|
66
|
+
>
|
|
67
|
+
Next
|
|
68
|
+
<ChevronRight className="w-5 h-5" />
|
|
69
|
+
</Button>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
</>
|
|
73
|
+
) : (
|
|
74
|
+
<EmptyState
|
|
75
|
+
icon={Package}
|
|
76
|
+
title="No orders yet"
|
|
77
|
+
description="Start shopping and your orders will appear here"
|
|
78
|
+
actionLabel="Start Shopping"
|
|
79
|
+
onAction={() => router.push('/shop')}
|
|
80
|
+
/>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { motion } from 'framer-motion';
|
|
5
|
+
import { ShoppingCart, Heart, Minus, Plus, Star, Check, Truck, Shield } from 'lucide-react';
|
|
6
|
+
import { Button } from '@/components/ui/Button';
|
|
7
|
+
import { Badge } from '@/components/ui/Badge';
|
|
8
|
+
import { ProductCard } from '@/components/ProductCard';
|
|
9
|
+
import { useProduct } from '@/hooks/useProducts';
|
|
10
|
+
import { useCart } from '@/providers/CartProvider';
|
|
11
|
+
import { formatPrice } from '@/lib/utils/format';
|
|
12
|
+
import { productsApi } from '@/lib/api/products';
|
|
13
|
+
import Image from 'next/image';
|
|
14
|
+
import { toast } from 'sonner';
|
|
15
|
+
|
|
16
|
+
interface ProductDetailScreenProps {
|
|
17
|
+
productId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
21
|
+
const { product, isLoading } = useProduct(productId);
|
|
22
|
+
const { addToCart } = useCart();
|
|
23
|
+
const [selectedImage, setSelectedImage] = useState(0);
|
|
24
|
+
const [quantity, setQuantity] = useState(1);
|
|
25
|
+
const [isAddingToCart, setIsAddingToCart] = useState(false);
|
|
26
|
+
const [isFavorited, setIsFavorited] = useState(false);
|
|
27
|
+
const [relatedProducts, setRelatedProducts] = useState<any[]>([]);
|
|
28
|
+
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
if (product) {
|
|
31
|
+
productsApi.getRelatedProducts(product.id).then((res) => {
|
|
32
|
+
if (res.success) {
|
|
33
|
+
setRelatedProducts(res.data);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}, [product]);
|
|
38
|
+
|
|
39
|
+
const handleAddToCart = async () => {
|
|
40
|
+
if (!product) return;
|
|
41
|
+
setIsAddingToCart(true);
|
|
42
|
+
try {
|
|
43
|
+
await addToCart(product.id, quantity);
|
|
44
|
+
} finally {
|
|
45
|
+
setIsAddingToCart(false);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (isLoading) {
|
|
50
|
+
return <div className="container mx-auto px-4 py-12">Loading...</div>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!product) {
|
|
54
|
+
return <div className="container mx-auto px-4 py-12">Product not found</div>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const discount = product.compareAtPrice
|
|
58
|
+
? Math.round(((product.compareAtPrice - product.price) / product.compareAtPrice) * 100)
|
|
59
|
+
: 0;
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="min-h-screen bg-gray-50 py-12">
|
|
63
|
+
<div className="container mx-auto px-4">
|
|
64
|
+
{/* Breadcrumbs */}
|
|
65
|
+
<div className="flex items-center gap-2 text-sm text-gray-600 mb-8">
|
|
66
|
+
<a href="/" className="hover:text-primary-600">Home</a>
|
|
67
|
+
<span>/</span>
|
|
68
|
+
<a href="/shop" className="hover:text-primary-600">Shop</a>
|
|
69
|
+
<span>/</span>
|
|
70
|
+
<span className="text-gray-900">{product.name}</span>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Product Content */}
|
|
74
|
+
<div className="grid lg:grid-cols-2 gap-12 mb-16">
|
|
75
|
+
{/* Images */}
|
|
76
|
+
<div>
|
|
77
|
+
<motion.div
|
|
78
|
+
initial={{ opacity: 0, y: 20 }}
|
|
79
|
+
animate={{ opacity: 1, y: 0 }}
|
|
80
|
+
className="bg-white rounded-2xl overflow-hidden shadow-sm mb-4"
|
|
81
|
+
>
|
|
82
|
+
<div className="relative aspect-square">
|
|
83
|
+
<Image
|
|
84
|
+
src={product.images[selectedImage] || '/placeholder-product.jpg'}
|
|
85
|
+
alt={product.name}
|
|
86
|
+
fill
|
|
87
|
+
className="object-cover"
|
|
88
|
+
/>
|
|
89
|
+
{discount > 0 && (
|
|
90
|
+
<Badge variant="danger" className="absolute top-4 left-4">
|
|
91
|
+
-{discount}%
|
|
92
|
+
</Badge>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
</motion.div>
|
|
96
|
+
|
|
97
|
+
{/* Thumbnail Images */}
|
|
98
|
+
{product.images.length > 1 && (
|
|
99
|
+
<div className="grid grid-cols-4 gap-4">
|
|
100
|
+
{product.images.map((image, index) => (
|
|
101
|
+
<button
|
|
102
|
+
key={index}
|
|
103
|
+
onClick={() => setSelectedImage(index)}
|
|
104
|
+
className={`relative aspect-square rounded-lg overflow-hidden border-2 transition-all ${
|
|
105
|
+
selectedImage === index
|
|
106
|
+
? 'border-primary-600 ring-2 ring-primary-600/20'
|
|
107
|
+
: 'border-gray-200 hover:border-gray-300'
|
|
108
|
+
}`}
|
|
109
|
+
>
|
|
110
|
+
<Image src={image} alt="" fill className="object-cover" />
|
|
111
|
+
</button>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{/* Product Info */}
|
|
118
|
+
<div>
|
|
119
|
+
<motion.div
|
|
120
|
+
initial={{ opacity: 0, y: 20 }}
|
|
121
|
+
animate={{ opacity: 1, y: 0 }}
|
|
122
|
+
transition={{ delay: 0.1 }}
|
|
123
|
+
>
|
|
124
|
+
{/* Category */}
|
|
125
|
+
<p className="text-sm text-gray-500 uppercase tracking-wider mb-2">
|
|
126
|
+
{product.category}
|
|
127
|
+
</p>
|
|
128
|
+
|
|
129
|
+
{/* Title */}
|
|
130
|
+
<h1 className="text-4xl font-bold text-gray-900 mb-4">{product.name}</h1>
|
|
131
|
+
|
|
132
|
+
{/* Rating */}
|
|
133
|
+
<div className="flex items-center gap-2 mb-6">
|
|
134
|
+
<div className="flex items-center">
|
|
135
|
+
{[...Array(5)].map((_, i) => (
|
|
136
|
+
<Star
|
|
137
|
+
key={i}
|
|
138
|
+
className="w-5 h-5 fill-yellow-400 text-yellow-400"
|
|
139
|
+
/>
|
|
140
|
+
))}
|
|
141
|
+
</div>
|
|
142
|
+
<span className="text-gray-600">(4.8) • 127 reviews</span>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{/* Price */}
|
|
146
|
+
<div className="flex items-baseline gap-3 mb-6">
|
|
147
|
+
<span className="text-4xl font-bold text-gray-900">
|
|
148
|
+
{formatPrice(product.price)}
|
|
149
|
+
</span>
|
|
150
|
+
{product.compareAtPrice && (
|
|
151
|
+
<span className="text-xl text-gray-500 line-through">
|
|
152
|
+
{formatPrice(product.compareAtPrice)}
|
|
153
|
+
</span>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{/* Stock Status */}
|
|
158
|
+
<div className="mb-6">
|
|
159
|
+
{product.inStock ? (
|
|
160
|
+
<div className="flex items-center gap-2 text-green-600">
|
|
161
|
+
<Check className="w-5 h-5" />
|
|
162
|
+
<span className="font-medium">In Stock</span>
|
|
163
|
+
{product.stock && <span className="text-gray-500">({product.stock} available)</span>}
|
|
164
|
+
</div>
|
|
165
|
+
) : (
|
|
166
|
+
<div className="text-red-600 font-medium">Out of Stock</div>
|
|
167
|
+
)}
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{/* Description */}
|
|
171
|
+
<div className="mb-8">
|
|
172
|
+
<p className="text-gray-700 leading-relaxed">{product.description}</p>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{/* Quantity Selector */}
|
|
176
|
+
{product.inStock && (
|
|
177
|
+
<div className="mb-6">
|
|
178
|
+
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
179
|
+
Quantity
|
|
180
|
+
</label>
|
|
181
|
+
<div className="flex items-center border-2 border-gray-200 rounded-lg w-fit">
|
|
182
|
+
<button
|
|
183
|
+
onClick={() => setQuantity(Math.max(1, quantity - 1))}
|
|
184
|
+
className="p-3 hover:bg-gray-100 transition-colors"
|
|
185
|
+
>
|
|
186
|
+
<Minus className="w-5 h-5" />
|
|
187
|
+
</button>
|
|
188
|
+
<span className="px-6 font-medium min-w-[4rem] text-center">
|
|
189
|
+
{quantity}
|
|
190
|
+
</span>
|
|
191
|
+
<button
|
|
192
|
+
onClick={() => setQuantity(quantity + 1)}
|
|
193
|
+
className="p-3 hover:bg-gray-100 transition-colors"
|
|
194
|
+
>
|
|
195
|
+
<Plus className="w-5 h-5" />
|
|
196
|
+
</button>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
)}
|
|
200
|
+
|
|
201
|
+
{/* Actions */}
|
|
202
|
+
<div className="flex gap-4 mb-8">
|
|
203
|
+
<Button
|
|
204
|
+
size="lg"
|
|
205
|
+
onClick={handleAddToCart}
|
|
206
|
+
isLoading={isAddingToCart}
|
|
207
|
+
disabled={!product.inStock}
|
|
208
|
+
className="flex-1"
|
|
209
|
+
>
|
|
210
|
+
<ShoppingCart className="w-5 h-5" />
|
|
211
|
+
Add to Cart
|
|
212
|
+
</Button>
|
|
213
|
+
<Button
|
|
214
|
+
size="lg"
|
|
215
|
+
variant="outline"
|
|
216
|
+
onClick={() => setIsFavorited(!isFavorited)}
|
|
217
|
+
className="px-6"
|
|
218
|
+
>
|
|
219
|
+
<Heart
|
|
220
|
+
className={`w-5 h-5 ${isFavorited ? 'fill-red-500 text-red-500' : ''}`}
|
|
221
|
+
/>
|
|
222
|
+
</Button>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
{/* Features */}
|
|
226
|
+
<div className="space-y-4 border-t border-gray-200 pt-6">
|
|
227
|
+
<div className="flex items-center gap-3 text-gray-700">
|
|
228
|
+
<Truck className="w-5 h-5 text-primary-600" />
|
|
229
|
+
<span>Free shipping on orders over $50</span>
|
|
230
|
+
</div>
|
|
231
|
+
<div className="flex items-center gap-3 text-gray-700">
|
|
232
|
+
<Shield className="w-5 h-5 text-primary-600" />
|
|
233
|
+
<span>30-day money-back guarantee</span>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
</motion.div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Related Products */}
|
|
241
|
+
{relatedProducts.length > 0 && (
|
|
242
|
+
<div>
|
|
243
|
+
<h2 className="text-3xl font-bold text-gray-900 mb-8">You May Also Like</h2>
|
|
244
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
245
|
+
{relatedProducts.map((relatedProduct) => (
|
|
246
|
+
<ProductCard key={relatedProduct.id} product={relatedProduct} />
|
|
247
|
+
))}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|