hey-pharmacist-ecommerce 1.0.4 → 1.0.6
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 +107 -1
- package/dist/index.d.mts +3636 -316
- package/dist/index.d.ts +3636 -316
- package/dist/index.js +6802 -3865
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6756 -3817
- package/dist/index.mjs.map +1 -1
- package/package.json +17 -14
- package/src/components/AddressFormModal.tsx +171 -0
- package/src/components/CartItem.tsx +17 -12
- package/src/components/FilterChips.tsx +195 -0
- package/src/components/Header.tsx +121 -71
- package/src/components/OrderCard.tsx +18 -25
- package/src/components/ProductCard.tsx +209 -72
- package/src/components/ui/Button.tsx +13 -5
- package/src/components/ui/Card.tsx +46 -0
- package/src/hooks/useAddresses.ts +83 -0
- package/src/hooks/useOrders.ts +37 -19
- package/src/hooks/useProducts.ts +55 -63
- package/src/hooks/useWishlistProducts.ts +75 -0
- package/src/index.ts +3 -19
- package/src/lib/Apis/api.ts +1 -0
- package/src/lib/Apis/apis/cart-api.ts +3 -3
- package/src/lib/Apis/apis/inventory-api.ts +0 -108
- package/src/lib/Apis/apis/stores-api.ts +70 -0
- package/src/lib/Apis/apis/wishlist-api.ts +447 -0
- package/src/lib/Apis/models/cart-item-populated.ts +0 -1
- package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
- package/src/lib/Apis/models/create-variant-dto.ts +26 -33
- package/src/lib/Apis/models/extended-product-dto.ts +20 -24
- package/src/lib/Apis/models/index.ts +2 -1
- package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
- package/src/lib/Apis/models/order.ts +3 -8
- package/src/lib/Apis/models/populated-order.ts +3 -8
- package/src/lib/Apis/models/product-variant.ts +29 -0
- package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
- package/src/lib/Apis/models/wishlist.ts +51 -0
- package/src/lib/Apis/wrapper.ts +18 -7
- package/src/lib/api-adapter/index.ts +0 -12
- package/src/lib/types/index.ts +16 -61
- package/src/lib/utils/colors.ts +7 -4
- package/src/lib/utils/format.ts +1 -1
- package/src/lib/validations/address.ts +14 -0
- package/src/providers/AuthProvider.tsx +61 -31
- package/src/providers/CartProvider.tsx +18 -28
- package/src/providers/EcommerceProvider.tsx +7 -0
- package/src/providers/FavoritesProvider.tsx +86 -0
- package/src/providers/ThemeProvider.tsx +16 -1
- package/src/providers/WishlistProvider.tsx +174 -0
- package/src/screens/AddressesScreen.tsx +484 -0
- package/src/screens/CartScreen.tsx +120 -84
- package/src/screens/CategoriesScreen.tsx +120 -0
- package/src/screens/CheckoutScreen.tsx +919 -241
- package/src/screens/CurrentOrdersScreen.tsx +125 -61
- package/src/screens/HomeScreen.tsx +209 -0
- package/src/screens/LoginScreen.tsx +133 -88
- package/src/screens/NewAddressScreen.tsx +187 -0
- package/src/screens/OrdersScreen.tsx +162 -50
- package/src/screens/ProductDetailScreen.tsx +641 -190
- package/src/screens/ProfileScreen.tsx +192 -116
- package/src/screens/RegisterScreen.tsx +193 -144
- package/src/screens/SearchResultsScreen.tsx +165 -0
- package/src/screens/ShopScreen.tsx +1110 -146
- package/src/screens/WishlistScreen.tsx +428 -0
- package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
- package/src/lib/api/auth.ts +0 -81
- package/src/lib/api/cart.ts +0 -42
- package/src/lib/api/orders.ts +0 -53
- package/src/lib/api/products.ts +0 -51
- package/src/lib/api-adapter/auth-adapter.ts +0 -196
- package/src/lib/api-adapter/cart-adapter.ts +0 -193
- package/src/lib/api-adapter/mappers.ts +0 -147
- package/src/lib/api-adapter/orders-adapter.ts +0 -195
- package/src/lib/api-adapter/products-adapter.ts +0 -194
|
@@ -1,86 +1,198 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
|
-
import { motion } from 'framer-motion';
|
|
5
|
-
import {
|
|
3
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { AnimatePresence, motion } from 'framer-motion';
|
|
5
|
+
import { CalendarDays, ChevronLeft, ChevronRight, CreditCard, Filter, Package, Sparkles } from 'lucide-react';
|
|
6
6
|
import { OrderCard } from '@/components/OrderCard';
|
|
7
7
|
import { OrderCardSkeleton } from '@/components/ui/Skeleton';
|
|
8
8
|
import { EmptyState } from '@/components/EmptyState';
|
|
9
9
|
import { Button } from '@/components/ui/Button';
|
|
10
10
|
import { useOrders } from '@/hooks/useOrders';
|
|
11
|
+
import { FilterChips } from '@/components/FilterChips';
|
|
11
12
|
import { useRouter } from 'next/navigation';
|
|
13
|
+
import { ManualOrderDTOOrderStatusEnum, PaymentPaymentStatusEnum } from '@/lib/Apis';
|
|
14
|
+
|
|
15
|
+
const STATUS_FILTERS = ['All', ...Object.values(ManualOrderDTOOrderStatusEnum)];
|
|
16
|
+
const PAYMENT_FILTERS = ['All', ...Object.values(PaymentPaymentStatusEnum)];
|
|
17
|
+
type StatusFilterType = (typeof STATUS_FILTERS)[number];
|
|
18
|
+
type PaymentFilterType = (typeof PAYMENT_FILTERS)[number];
|
|
12
19
|
|
|
13
20
|
export function OrdersScreen() {
|
|
14
21
|
const router = useRouter();
|
|
15
22
|
const [page, setPage] = useState(1);
|
|
16
|
-
const
|
|
23
|
+
const [selectedFilter, setSelectedFilter] = useState<StatusFilterType>('All');
|
|
24
|
+
const [selectedPaymentFilter, setSelectedPaymentFilter] = useState<PaymentFilterType>('All');
|
|
25
|
+
const { orders, isLoading, pagination } = useOrders(
|
|
26
|
+
page,
|
|
27
|
+
10,
|
|
28
|
+
selectedFilter,
|
|
29
|
+
selectedPaymentFilter
|
|
30
|
+
);
|
|
31
|
+
const [isOverflowOpen, setIsOverflowOpen] = useState(false);
|
|
32
|
+
const [filterSearchTerm, setFilterSearchTerm] = useState('');
|
|
33
|
+
const overflowMenuRef = useRef<HTMLDivElement | null>(null);
|
|
34
|
+
|
|
35
|
+
const filteredOrders = useMemo(() => {
|
|
36
|
+
return orders.filter((order) => {
|
|
37
|
+
const matchesStatus =
|
|
38
|
+
selectedFilter === 'All' ||
|
|
39
|
+
order?.orderStatus?.toLowerCase() === selectedFilter.toLowerCase();
|
|
40
|
+
const matchesPayment =
|
|
41
|
+
selectedPaymentFilter === 'All' ||
|
|
42
|
+
order?.payment?.paymentStatus?.toLowerCase() === selectedPaymentFilter.toLowerCase();
|
|
43
|
+
|
|
44
|
+
return matchesStatus && matchesPayment;
|
|
45
|
+
});
|
|
46
|
+
}, [orders, selectedFilter, selectedPaymentFilter]);
|
|
47
|
+
|
|
48
|
+
const hasOrders = filteredOrders.length > 0;
|
|
49
|
+
const MAX_VISIBLE_FILTERS = 4;
|
|
50
|
+
|
|
51
|
+
// Removed local overflow/search logic in favor of reusable FilterChips
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!isOverflowOpen) {
|
|
55
|
+
setFilterSearchTerm('');
|
|
56
|
+
}
|
|
57
|
+
}, [isOverflowOpen]);
|
|
58
|
+
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
function handleClickOutside(event: MouseEvent) {
|
|
61
|
+
if (overflowMenuRef.current && !overflowMenuRef.current.contains(event.target as Node)) {
|
|
62
|
+
setIsOverflowOpen(false);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isOverflowOpen) {
|
|
67
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return () => {
|
|
71
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
72
|
+
};
|
|
73
|
+
}, [isOverflowOpen]);
|
|
17
74
|
|
|
18
75
|
return (
|
|
19
|
-
<div className="min-h-screen
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
76
|
+
<div className="min-h-screen bg-slate-50">
|
|
77
|
+
<section className="relative overflow-hidden bg-gradient-to-br from-[rgb(var(--header-from))] via-[rgb(var(--header-via))] to-[rgb(var(--header-to))] text-white mb-8">
|
|
78
|
+
<div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.35),_transparent_60%)]" />
|
|
79
|
+
<div className="relative container mx-auto px-4 py-16">
|
|
80
|
+
<motion.div
|
|
81
|
+
initial={{ opacity: 0, y: 24 }}
|
|
82
|
+
animate={{ opacity: 1, y: 0 }}
|
|
83
|
+
className="space-y-6"
|
|
84
|
+
>
|
|
85
|
+
<span className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-semibold uppercase tracking-[0.35em] text-white/70 backdrop-blur">
|
|
86
|
+
<CalendarDays className="h-4 w-4" />
|
|
87
|
+
Order history
|
|
88
|
+
</span>
|
|
89
|
+
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
90
|
+
<div className="space-y-4">
|
|
91
|
+
<h1 className="text-4xl font-bold md:text-5xl">All of your pharmacy orders</h1>
|
|
92
|
+
<p className="max-w-2xl text-white/80 md:text-lg">
|
|
93
|
+
Access receipts, shipping statuses, and reorder suggestions in one organized
|
|
94
|
+
timeline curated by our pharmacists.
|
|
95
|
+
</p>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="rounded-3xl bg-white/15 p-6 backdrop-blur">
|
|
98
|
+
<p className="text-sm font-semibold uppercase tracking-[0.35em] text-white/70">
|
|
99
|
+
Quick tip
|
|
100
|
+
</p>
|
|
101
|
+
<p className="mt-3 text-sm text-white/80">
|
|
102
|
+
Use filters to review previous prescriptions, reorder favorites, or download
|
|
103
|
+
invoices for insurance claims.
|
|
104
|
+
</p>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</motion.div>
|
|
108
|
+
</div>
|
|
109
|
+
</section>
|
|
110
|
+
|
|
111
|
+
<div className="relative -mt-16 pb-16 container mx-auto px-4">
|
|
112
|
+
<div className="container mx-auto px-4">
|
|
113
|
+
<motion.div
|
|
114
|
+
initial={{ opacity: 0, y: 24 }}
|
|
115
|
+
animate={{ opacity: 1, y: 0 }}
|
|
116
|
+
className="rounded-3xl border border-slate-100 bg-white p-6 shadow-lg shadow-primary-50"
|
|
117
|
+
>
|
|
118
|
+
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
|
119
|
+
<div className="flex items-center gap-3 text-sm text-slate-500">
|
|
120
|
+
<Sparkles className="h-4 w-4 text-primary-500" />
|
|
121
|
+
<span>Explore your complete order archive with pharmacist notes.</span>
|
|
122
|
+
</div>
|
|
123
|
+
<div className="flex flex-col gap-4 md:items-end">
|
|
124
|
+
<FilterChips
|
|
125
|
+
label="Status filters"
|
|
126
|
+
icon={Filter}
|
|
127
|
+
filters={STATUS_FILTERS}
|
|
128
|
+
selected={selectedFilter}
|
|
129
|
+
onSelect={(value) => {
|
|
130
|
+
setSelectedFilter(value as any);
|
|
131
|
+
setPage(1);
|
|
132
|
+
}}
|
|
133
|
+
maxVisible={MAX_VISIBLE_FILTERS}
|
|
134
|
+
variant="primary"
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
<FilterChips
|
|
138
|
+
label="Payment status"
|
|
139
|
+
icon={CreditCard}
|
|
140
|
+
filters={PAYMENT_FILTERS}
|
|
141
|
+
selected={selectedPaymentFilter}
|
|
142
|
+
onSelect={(value) => {
|
|
143
|
+
setSelectedPaymentFilter(value as any);
|
|
144
|
+
setPage(1);
|
|
145
|
+
}}
|
|
146
|
+
maxVisible={MAX_VISIBLE_FILTERS}
|
|
147
|
+
variant="primary"
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
46
150
|
</div>
|
|
47
151
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
152
|
+
<div className="mt-6 space-y-4">
|
|
153
|
+
{isLoading ? (
|
|
154
|
+
Array.from({ length: 3 }).map((_, index) => <OrderCardSkeleton key={index} />)
|
|
155
|
+
) : hasOrders ? (
|
|
156
|
+
filteredOrders.map((order) => <OrderCard key={order.id} order={order} />)
|
|
157
|
+
) : (
|
|
158
|
+
<EmptyState
|
|
159
|
+
icon={Package}
|
|
160
|
+
title="No orders found for these filters"
|
|
161
|
+
description="Adjust the status or payment filters, or browse the shop for new essentials."
|
|
162
|
+
actionLabel="Shop products"
|
|
163
|
+
onAction={() => router.push('/shop')}
|
|
164
|
+
/>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
{!isLoading && pagination.totalPages > 1 && hasOrders && (
|
|
169
|
+
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
|
|
51
170
|
<Button
|
|
52
171
|
variant="outline"
|
|
53
|
-
onClick={() => setPage(
|
|
172
|
+
onClick={() => setPage((current) => Math.max(1, current - 1))}
|
|
54
173
|
disabled={page === 1}
|
|
55
174
|
>
|
|
56
|
-
<ChevronLeft className="
|
|
175
|
+
<ChevronLeft className="h-5 w-5" />
|
|
57
176
|
Previous
|
|
58
177
|
</Button>
|
|
59
|
-
<span className="text-
|
|
178
|
+
<span className="text-sm font-semibold text-slate-600">
|
|
60
179
|
Page {page} of {pagination.totalPages}
|
|
61
180
|
</span>
|
|
62
181
|
<Button
|
|
63
182
|
variant="outline"
|
|
64
|
-
onClick={() =>
|
|
183
|
+
onClick={() =>
|
|
184
|
+
setPage((current) => Math.min(pagination.totalPages, current + 1))
|
|
185
|
+
}
|
|
65
186
|
disabled={page === pagination.totalPages}
|
|
66
187
|
>
|
|
67
188
|
Next
|
|
68
|
-
<ChevronRight className="
|
|
189
|
+
<ChevronRight className="h-5 w-5" />
|
|
69
190
|
</Button>
|
|
70
191
|
</div>
|
|
71
192
|
)}
|
|
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
|
-
)}
|
|
193
|
+
</motion.div>
|
|
194
|
+
</div>
|
|
82
195
|
</div>
|
|
83
196
|
</div>
|
|
84
197
|
);
|
|
85
198
|
}
|
|
86
|
-
|