@diffsome/react 1.1.1 → 1.1.3
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 +218 -24
- package/dist/index.d.mts +195 -8
- package/dist/index.d.ts +195 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,25 +8,91 @@ Headless React hooks and providers for Diffsome SDK. Build any e-commerce or CMS
|
|
|
8
8
|
npm install @diffsome/react @diffsome/sdk
|
|
9
9
|
# or
|
|
10
10
|
yarn add @diffsome/react @diffsome/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @diffsome/react @diffsome/sdk
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
##
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
### Option 1: Environment Variables (Recommended)
|
|
18
|
+
|
|
19
|
+
**Next.js** (`.env.local`)
|
|
20
|
+
```bash
|
|
21
|
+
NEXT_PUBLIC_DIFFSOME_TENANT_ID=my-store
|
|
22
|
+
NEXT_PUBLIC_DIFFSOME_API_KEY=pky_xxx # optional
|
|
23
|
+
NEXT_PUBLIC_DIFFSOME_BASE_URL=https://api.diffsome.com # optional
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Create React App** (`.env`)
|
|
27
|
+
```bash
|
|
28
|
+
REACT_APP_DIFFSOME_TENANT_ID=my-store
|
|
29
|
+
REACT_APP_DIFFSOME_API_KEY=pky_xxx
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then simply wrap your app:
|
|
14
33
|
|
|
15
34
|
```tsx
|
|
16
|
-
import { DiffsomeProvider
|
|
35
|
+
import { DiffsomeProvider } from '@diffsome/react';
|
|
17
36
|
|
|
18
37
|
function App() {
|
|
19
38
|
return (
|
|
20
|
-
<DiffsomeProvider
|
|
21
|
-
|
|
22
|
-
tenantId: 'my-store',
|
|
23
|
-
apiKey: 'pky_your_api_key',
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
<Shop />
|
|
39
|
+
<DiffsomeProvider>
|
|
40
|
+
<YourApp />
|
|
27
41
|
</DiffsomeProvider>
|
|
28
42
|
);
|
|
29
43
|
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Option 2: Direct Config
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<DiffsomeProvider
|
|
50
|
+
config={{
|
|
51
|
+
tenantId: 'my-store', // required
|
|
52
|
+
apiKey: 'pky_xxx', // optional
|
|
53
|
+
baseUrl: 'https://api.diffsome.com', // optional
|
|
54
|
+
persistToken: true, // default: true
|
|
55
|
+
storageType: 'localStorage', // 'localStorage' | 'sessionStorage'
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
<YourApp />
|
|
59
|
+
</DiffsomeProvider>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Next.js App Router Setup
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
// app/providers.tsx
|
|
66
|
+
'use client';
|
|
67
|
+
|
|
68
|
+
import { DiffsomeProvider } from '@diffsome/react';
|
|
69
|
+
|
|
70
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
71
|
+
return (
|
|
72
|
+
<DiffsomeProvider>
|
|
73
|
+
{children}
|
|
74
|
+
</DiffsomeProvider>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// app/layout.tsx
|
|
79
|
+
import { Providers } from './providers';
|
|
80
|
+
|
|
81
|
+
export default function RootLayout({ children }) {
|
|
82
|
+
return (
|
|
83
|
+
<html>
|
|
84
|
+
<body>
|
|
85
|
+
<Providers>{children}</Providers>
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Quick Start
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { DiffsomeProvider, useAuth, useCart, useProducts } from '@diffsome/react';
|
|
30
96
|
|
|
31
97
|
function Shop() {
|
|
32
98
|
const { user, login, logout } = useAuth();
|
|
@@ -62,29 +128,34 @@ function Shop() {
|
|
|
62
128
|
## Available Hooks
|
|
63
129
|
|
|
64
130
|
### Authentication
|
|
65
|
-
- `useAuth()` - Login, register, logout, profile
|
|
131
|
+
- `useAuth()` - Login, register, logout, profile, password reset
|
|
66
132
|
- `useSocialAuth()` - Social login (Google, Kakao, Naver, etc.)
|
|
67
133
|
|
|
68
134
|
### E-commerce
|
|
69
135
|
- `useCart()` - Cart management (add, update, remove items)
|
|
70
|
-
- `useWishlist()` - Wishlist management
|
|
71
|
-
- `useProducts()` - Product listing with pagination
|
|
136
|
+
- `useWishlist()` - Wishlist management with toggle
|
|
137
|
+
- `useProducts(options?)` - Product listing with pagination & search
|
|
72
138
|
- `useProduct(slug)` - Single product details
|
|
73
139
|
- `useCategories()` - Product categories
|
|
74
|
-
- `useFeaturedProducts()` - Featured products
|
|
140
|
+
- `useFeaturedProducts(limit?)` - Featured products
|
|
141
|
+
- `useProductsByType(type)` - Products by type (digital, subscription, bundle)
|
|
142
|
+
- `useDigitalProducts()` - Digital products shorthand
|
|
143
|
+
- `useSubscriptionProducts()` - Subscription products shorthand
|
|
144
|
+
- `useBundleProducts()` - Bundle products shorthand
|
|
145
|
+
- `useBundleItems(slug)` - Bundle items with pricing
|
|
75
146
|
- `useOrders()` - Order history
|
|
76
147
|
- `useOrder(id)` - Single order details
|
|
77
148
|
- `useCreateOrder()` - Create new order
|
|
78
149
|
|
|
79
150
|
### Payment
|
|
80
|
-
- `usePaymentStatus()` - Check available payment methods
|
|
151
|
+
- `usePaymentStatus()` - Check available payment methods
|
|
81
152
|
- `useTossPayment()` - Toss Payments (Korea)
|
|
82
153
|
- `useStripePayment()` - Stripe Payments (Global)
|
|
83
154
|
- `useCoupons()` - User's available coupons
|
|
84
155
|
- `useValidateCoupon()` - Validate coupon code
|
|
85
156
|
|
|
86
157
|
### Subscriptions
|
|
87
|
-
- `useSubscriptions()` - User's subscriptions
|
|
158
|
+
- `useSubscriptions()` - User's subscriptions list
|
|
88
159
|
- `useSubscription(id)` - Single subscription with cancel/pause/resume
|
|
89
160
|
- `useCreateSubscription()` - Create subscription via Stripe
|
|
90
161
|
|
|
@@ -93,15 +164,18 @@ function Shop() {
|
|
|
93
164
|
- `useOrderDownloads(orderNumber)` - Downloads for specific order
|
|
94
165
|
|
|
95
166
|
### Reviews
|
|
96
|
-
- `useProductReviews(slug)` - Product reviews
|
|
167
|
+
- `useProductReviews(slug)` - Product reviews with stats
|
|
97
168
|
- `useMyReviews()` - User's reviews
|
|
98
169
|
- `useCanReview(slug)` - Check review eligibility
|
|
99
170
|
- `useCreateReview()` - Create product review
|
|
100
171
|
|
|
101
172
|
### Content
|
|
102
|
-
- `useBlog()` - Blog posts with pagination
|
|
173
|
+
- `useBlog(options?)` - Blog posts with pagination
|
|
103
174
|
- `useBlogPost(slug)` - Single blog post
|
|
104
175
|
- `useBlogCategories()` - Blog categories
|
|
176
|
+
- `useBlogTags()` - Blog tags
|
|
177
|
+
- `useFeaturedBlog(limit?)` - Featured blog posts
|
|
178
|
+
- `useBlogSearch()` - Blog search
|
|
105
179
|
- `useBoards()` - Board listing
|
|
106
180
|
- `useBoard(slug)` - Single board
|
|
107
181
|
- `useBoardPosts(slug)` - Board posts
|
|
@@ -112,8 +186,8 @@ function Shop() {
|
|
|
112
186
|
|
|
113
187
|
### Reservations
|
|
114
188
|
- `useReservationServices()` - Available services
|
|
115
|
-
- `useReservationStaffs()` - Staff members
|
|
116
|
-
- `useAvailableSlots(serviceId, date)` -
|
|
189
|
+
- `useReservationStaffs(serviceId?)` - Staff members
|
|
190
|
+
- `useAvailableSlots(serviceId, staffId?, date)` - Available time slots
|
|
117
191
|
- `useMyReservations()` - User's reservations
|
|
118
192
|
- `useCreateReservation()` - Create reservation
|
|
119
193
|
- `useReservationSettings()` - Reservation settings
|
|
@@ -134,7 +208,7 @@ function Shop() {
|
|
|
134
208
|
|
|
135
209
|
```tsx
|
|
136
210
|
function LoginForm() {
|
|
137
|
-
const { user, login, logout, loading, error } = useAuth();
|
|
211
|
+
const { user, login, logout, forgotPassword, loading, error } = useAuth();
|
|
138
212
|
const [email, setEmail] = useState('');
|
|
139
213
|
const [password, setPassword] = useState('');
|
|
140
214
|
|
|
@@ -143,6 +217,11 @@ function LoginForm() {
|
|
|
143
217
|
await login({ email, password });
|
|
144
218
|
};
|
|
145
219
|
|
|
220
|
+
const handleForgotPassword = async () => {
|
|
221
|
+
await forgotPassword(email);
|
|
222
|
+
alert('Password reset email sent!');
|
|
223
|
+
};
|
|
224
|
+
|
|
146
225
|
if (user) {
|
|
147
226
|
return (
|
|
148
227
|
<div>
|
|
@@ -157,17 +236,53 @@ function LoginForm() {
|
|
|
157
236
|
<input value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
158
237
|
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
|
|
159
238
|
<button disabled={loading}>Login</button>
|
|
239
|
+
<button type="button" onClick={handleForgotPassword}>Forgot Password</button>
|
|
160
240
|
{error && <p>{error.message}</p>}
|
|
161
241
|
</form>
|
|
162
242
|
);
|
|
163
243
|
}
|
|
164
244
|
```
|
|
165
245
|
|
|
246
|
+
### useProducts with Filters
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
function ProductGrid() {
|
|
250
|
+
const { products, loading, hasMore, loadMore, search } = useProducts({
|
|
251
|
+
per_page: 12,
|
|
252
|
+
category: 'electronics',
|
|
253
|
+
});
|
|
254
|
+
const [query, setQuery] = useState('');
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<div>
|
|
258
|
+
<input
|
|
259
|
+
placeholder="Search products..."
|
|
260
|
+
value={query}
|
|
261
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
262
|
+
onKeyDown={(e) => e.key === 'Enter' && search(query)}
|
|
263
|
+
/>
|
|
264
|
+
|
|
265
|
+
<div className="grid grid-cols-4 gap-4">
|
|
266
|
+
{products.map(product => (
|
|
267
|
+
<ProductCard key={product.id} product={product} />
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{hasMore && (
|
|
272
|
+
<button onClick={loadMore} disabled={loading}>
|
|
273
|
+
Load More
|
|
274
|
+
</button>
|
|
275
|
+
)}
|
|
276
|
+
</div>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
166
281
|
### useCart
|
|
167
282
|
|
|
168
283
|
```tsx
|
|
169
284
|
function Cart() {
|
|
170
|
-
const { items, total, updateItem, removeItem, clearCart } = useCart();
|
|
285
|
+
const { items, total, updateItem, removeItem, clearCart, applyCoupon } = useCart();
|
|
171
286
|
|
|
172
287
|
return (
|
|
173
288
|
<div>
|
|
@@ -193,12 +308,17 @@ function Cart() {
|
|
|
193
308
|
|
|
194
309
|
```tsx
|
|
195
310
|
function WishlistButton({ productId }) {
|
|
196
|
-
const { isInWishlist, toggleWishlist } = useWishlist();
|
|
311
|
+
const { isInWishlist, toggleWishlist, getProductWishlistCount } = useWishlist();
|
|
312
|
+
const [count, setCount] = useState(0);
|
|
197
313
|
const inWishlist = isInWishlist(productId);
|
|
198
314
|
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
getProductWishlistCount(productId).then(setCount);
|
|
317
|
+
}, [productId]);
|
|
318
|
+
|
|
199
319
|
return (
|
|
200
320
|
<button onClick={() => toggleWishlist(productId)}>
|
|
201
|
-
{inWishlist ? '❤️' : '🤍'}
|
|
321
|
+
{inWishlist ? '❤️' : '🤍'} {count}
|
|
202
322
|
</button>
|
|
203
323
|
);
|
|
204
324
|
}
|
|
@@ -227,6 +347,65 @@ function CheckoutButton({ orderNumber }) {
|
|
|
227
347
|
}
|
|
228
348
|
```
|
|
229
349
|
|
|
350
|
+
### useSubscription
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
function SubscriptionManager({ subscriptionId }) {
|
|
354
|
+
const { subscription, cancel, pause, resume, loading } = useSubscription(subscriptionId);
|
|
355
|
+
|
|
356
|
+
if (!subscription) return null;
|
|
357
|
+
|
|
358
|
+
return (
|
|
359
|
+
<div>
|
|
360
|
+
<p>Plan: {subscription.plan.name}</p>
|
|
361
|
+
<p>Status: {subscription.status}</p>
|
|
362
|
+
<p>Next billing: {subscription.current_period_end}</p>
|
|
363
|
+
|
|
364
|
+
{subscription.status === 'active' && (
|
|
365
|
+
<>
|
|
366
|
+
<button onClick={pause}>Pause</button>
|
|
367
|
+
<button onClick={cancel}>Cancel</button>
|
|
368
|
+
</>
|
|
369
|
+
)}
|
|
370
|
+
{subscription.status === 'paused' && (
|
|
371
|
+
<button onClick={resume}>Resume</button>
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### useBlogSearch
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
function BlogSearch() {
|
|
382
|
+
const { results, search, loading } = useBlogSearch();
|
|
383
|
+
const [query, setQuery] = useState('');
|
|
384
|
+
|
|
385
|
+
const handleSearch = () => {
|
|
386
|
+
search(query);
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return (
|
|
390
|
+
<div>
|
|
391
|
+
<input
|
|
392
|
+
value={query}
|
|
393
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
394
|
+
placeholder="Search blog..."
|
|
395
|
+
/>
|
|
396
|
+
<button onClick={handleSearch} disabled={loading}>Search</button>
|
|
397
|
+
|
|
398
|
+
{results.map(post => (
|
|
399
|
+
<article key={post.id}>
|
|
400
|
+
<h3>{post.title}</h3>
|
|
401
|
+
<p>{post.excerpt}</p>
|
|
402
|
+
</article>
|
|
403
|
+
))}
|
|
404
|
+
</div>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
230
409
|
### useComments
|
|
231
410
|
|
|
232
411
|
```tsx
|
|
@@ -345,9 +524,24 @@ function CustomComponent() {
|
|
|
345
524
|
All types are re-exported from `@diffsome/sdk`:
|
|
346
525
|
|
|
347
526
|
```tsx
|
|
348
|
-
import type {
|
|
527
|
+
import type {
|
|
528
|
+
Product,
|
|
529
|
+
Cart,
|
|
530
|
+
Order,
|
|
531
|
+
Member,
|
|
532
|
+
CustomEntity,
|
|
533
|
+
EntityRecord,
|
|
534
|
+
BlogPost,
|
|
535
|
+
Reservation,
|
|
536
|
+
// ... and more
|
|
537
|
+
} from '@diffsome/react';
|
|
349
538
|
```
|
|
350
539
|
|
|
540
|
+
## Requirements
|
|
541
|
+
|
|
542
|
+
- React 18.0+
|
|
543
|
+
- @diffsome/sdk 3.2.0+
|
|
544
|
+
|
|
351
545
|
## License
|
|
352
546
|
|
|
353
547
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -17,24 +17,75 @@ declare function DiffsomeProvider({ children, config }: DiffsomeProviderProps):
|
|
|
17
17
|
declare function useDiffsome(): DiffsomeContextValue;
|
|
18
18
|
declare function useClient(): Diffsome;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Return type for the useAuth hook
|
|
22
|
+
*/
|
|
20
23
|
interface UseAuthReturn {
|
|
24
|
+
/** Currently logged in user, or null if not authenticated */
|
|
21
25
|
user: Member | null;
|
|
26
|
+
/** Whether the user is currently authenticated */
|
|
22
27
|
isAuthenticated: boolean;
|
|
28
|
+
/** Whether an auth operation is in progress */
|
|
23
29
|
loading: boolean;
|
|
30
|
+
/** Error from the last auth operation, if any */
|
|
24
31
|
error: Error | null;
|
|
32
|
+
/**
|
|
33
|
+
* Log in with email and password
|
|
34
|
+
* @param credentials - { email: string, password: string }
|
|
35
|
+
* @returns AuthResponse with user and token
|
|
36
|
+
*/
|
|
25
37
|
login: (credentials: LoginCredentials) => Promise<AuthResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Register a new account
|
|
40
|
+
* @param data - { name: string, email: string, password: string, password_confirmation: string }
|
|
41
|
+
* @returns AuthResponse with user and token
|
|
42
|
+
*/
|
|
26
43
|
register: (data: RegisterData) => Promise<AuthResponse>;
|
|
44
|
+
/** Log out and clear session */
|
|
27
45
|
logout: () => Promise<void>;
|
|
46
|
+
/** Fetch current user profile */
|
|
28
47
|
getProfile: () => Promise<Member>;
|
|
48
|
+
/**
|
|
49
|
+
* Update user profile
|
|
50
|
+
* @param data - { name?: string, email?: string, phone?: string, avatar?: File }
|
|
51
|
+
*/
|
|
29
52
|
updateProfile: (data: UpdateProfileData) => Promise<Member>;
|
|
53
|
+
/**
|
|
54
|
+
* Send password reset email
|
|
55
|
+
* @param email - User's email address
|
|
56
|
+
*/
|
|
30
57
|
forgotPassword: (email: string) => Promise<{
|
|
31
58
|
message: string;
|
|
32
59
|
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Reset password with token
|
|
62
|
+
* @param data - { token: string, email: string, password: string, password_confirmation: string }
|
|
63
|
+
*/
|
|
33
64
|
resetPassword: (data: ResetPasswordData) => Promise<{
|
|
34
65
|
message: string;
|
|
35
66
|
}>;
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
/** Refresh user profile from server */
|
|
68
|
+
refresh: () => Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Hook for user authentication - login, register, logout, profile management
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* const { user, login, logout, loading } = useAuth();
|
|
76
|
+
*
|
|
77
|
+
* // Login
|
|
78
|
+
* await login({ email: 'user@example.com', password: 'secret' });
|
|
79
|
+
*
|
|
80
|
+
* // Check auth state
|
|
81
|
+
* if (user) {
|
|
82
|
+
* console.log('Logged in as:', user.name);
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* // Logout
|
|
86
|
+
* await logout();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
38
89
|
declare function useAuth(): UseAuthReturn;
|
|
39
90
|
|
|
40
91
|
interface UseSocialAuthReturn {
|
|
@@ -47,24 +98,90 @@ interface UseSocialAuthReturn {
|
|
|
47
98
|
}
|
|
48
99
|
declare function useSocialAuth(): UseSocialAuthReturn;
|
|
49
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Return type for the useCart hook
|
|
103
|
+
*/
|
|
50
104
|
interface UseCartReturn {
|
|
105
|
+
/** Full cart object with metadata */
|
|
51
106
|
cart: Cart | null;
|
|
107
|
+
/** Array of cart items */
|
|
52
108
|
items: CartItem[];
|
|
109
|
+
/** Number of unique items in cart */
|
|
53
110
|
itemCount: number;
|
|
111
|
+
/** Total quantity of all items */
|
|
54
112
|
totalQuantity: number;
|
|
113
|
+
/** Cart subtotal before shipping */
|
|
55
114
|
subtotal: number;
|
|
115
|
+
/** Shipping fee */
|
|
56
116
|
shippingFee: number;
|
|
117
|
+
/** Cart total including shipping */
|
|
57
118
|
total: number;
|
|
58
|
-
loading
|
|
59
|
-
|
|
119
|
+
/** Whether cart is loading */
|
|
120
|
+
loading: boolean;
|
|
121
|
+
/** Error from last cart operation */
|
|
122
|
+
error: Error | null;
|
|
123
|
+
/**
|
|
124
|
+
* Add product to cart
|
|
125
|
+
* @param productId - Product ID to add
|
|
126
|
+
* @param quantity - Quantity to add (default: 1)
|
|
127
|
+
* @param variantId - Optional variant ID for variable products
|
|
128
|
+
* @param options - Optional custom options
|
|
129
|
+
* @returns Updated cart
|
|
130
|
+
*/
|
|
60
131
|
addToCart: (productId: number, quantity?: number, variantId?: number, options?: Record<string, string>) => Promise<Cart>;
|
|
132
|
+
/**
|
|
133
|
+
* Update item quantity
|
|
134
|
+
* @param itemId - Cart item ID
|
|
135
|
+
* @param quantity - New quantity
|
|
136
|
+
*/
|
|
61
137
|
updateItem: (itemId: number, quantity: number) => Promise<Cart>;
|
|
138
|
+
/**
|
|
139
|
+
* Remove item from cart
|
|
140
|
+
* @param itemId - Cart item ID to remove
|
|
141
|
+
*/
|
|
62
142
|
removeItem: (itemId: number) => Promise<Cart>;
|
|
143
|
+
/** Clear all items from cart */
|
|
63
144
|
clearCart: () => Promise<void>;
|
|
145
|
+
/** Refresh cart from server */
|
|
64
146
|
refresh: () => Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Check if product is in cart
|
|
149
|
+
* @param productId - Product ID
|
|
150
|
+
* @param variantId - Optional variant ID
|
|
151
|
+
*/
|
|
65
152
|
isInCart: (productId: number, variantId?: number) => boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Get quantity of product in cart
|
|
155
|
+
* @param productId - Product ID
|
|
156
|
+
* @param variantId - Optional variant ID
|
|
157
|
+
* @returns Quantity in cart (0 if not in cart)
|
|
158
|
+
*/
|
|
66
159
|
getItemQuantity: (productId: number, variantId?: number) => number;
|
|
67
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Hook for shopping cart management
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const { items, total, addToCart, removeItem, clearCart } = useCart();
|
|
167
|
+
*
|
|
168
|
+
* // Add to cart
|
|
169
|
+
* await addToCart(123, 2); // Add product ID 123, quantity 2
|
|
170
|
+
*
|
|
171
|
+
* // With variant
|
|
172
|
+
* await addToCart(123, 1, 456); // Product 123, variant 456
|
|
173
|
+
*
|
|
174
|
+
* // Display cart
|
|
175
|
+
* items.map(item => (
|
|
176
|
+
* <div key={item.id}>
|
|
177
|
+
* {item.product_name} x {item.quantity} = ${item.subtotal}
|
|
178
|
+
* </div>
|
|
179
|
+
* ));
|
|
180
|
+
*
|
|
181
|
+
* // Total
|
|
182
|
+
* console.log('Total:', total);
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
68
185
|
declare function useCart(): UseCartReturn;
|
|
69
186
|
|
|
70
187
|
interface UseWishlistReturn {
|
|
@@ -83,26 +200,96 @@ interface UseWishlistReturn {
|
|
|
83
200
|
}
|
|
84
201
|
declare function useWishlist(): UseWishlistReturn;
|
|
85
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Return type for the useProducts hook
|
|
205
|
+
*/
|
|
86
206
|
interface UseProductsReturn {
|
|
207
|
+
/** Array of products */
|
|
87
208
|
products: Product[];
|
|
209
|
+
/** Pagination metadata (current_page, last_page, total, per_page) */
|
|
88
210
|
meta: PaginationMeta | null;
|
|
211
|
+
/** Whether products are loading */
|
|
89
212
|
loading: boolean;
|
|
213
|
+
/** Error from last operation */
|
|
90
214
|
error: Error | null;
|
|
215
|
+
/** Whether more pages are available */
|
|
91
216
|
hasMore: boolean;
|
|
217
|
+
/** Load next page of products (appends to list) */
|
|
92
218
|
loadMore: () => Promise<void>;
|
|
219
|
+
/** Refresh products from server */
|
|
93
220
|
refresh: () => Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Search products by query
|
|
223
|
+
* @param query - Search term
|
|
224
|
+
*/
|
|
94
225
|
search: (query: string) => Promise<void>;
|
|
95
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Options for useProducts hook
|
|
229
|
+
*/
|
|
96
230
|
interface UseProductsOptions extends ProductListParams {
|
|
231
|
+
/** Whether to fetch products on mount (default: true) */
|
|
97
232
|
autoFetch?: boolean;
|
|
98
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Hook for fetching products with pagination, filtering, and search
|
|
236
|
+
*
|
|
237
|
+
* @param options - Filtering and pagination options
|
|
238
|
+
* @example
|
|
239
|
+
* ```tsx
|
|
240
|
+
* // Basic usage
|
|
241
|
+
* const { products, loading } = useProducts();
|
|
242
|
+
*
|
|
243
|
+
* // With filters
|
|
244
|
+
* const { products } = useProducts({
|
|
245
|
+
* category: 'electronics',
|
|
246
|
+
* per_page: 20,
|
|
247
|
+
* sort: 'price_low',
|
|
248
|
+
* min_price: 10,
|
|
249
|
+
* max_price: 100,
|
|
250
|
+
* });
|
|
251
|
+
*
|
|
252
|
+
* // Infinite scroll
|
|
253
|
+
* const { products, hasMore, loadMore } = useProducts({ per_page: 12 });
|
|
254
|
+
* // Call loadMore() when user scrolls to bottom
|
|
255
|
+
*
|
|
256
|
+
* // Search
|
|
257
|
+
* const { products, search } = useProducts();
|
|
258
|
+
* await search('laptop'); // Searches products
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
99
261
|
declare function useProducts(options?: UseProductsOptions): UseProductsReturn;
|
|
262
|
+
/**
|
|
263
|
+
* Return type for the useProduct hook
|
|
264
|
+
*/
|
|
100
265
|
interface UseProductReturn {
|
|
266
|
+
/** Product data, or null if not loaded */
|
|
101
267
|
product: Product | null;
|
|
102
|
-
loading
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
268
|
+
/** Whether product is loading */
|
|
269
|
+
loading: boolean;
|
|
270
|
+
/** Error from last operation */
|
|
271
|
+
error: Error | null;
|
|
272
|
+
/** Refresh product from server */
|
|
273
|
+
refresh: () => Promise<void>;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Hook for fetching a single product by ID or slug
|
|
277
|
+
*
|
|
278
|
+
* @param idOrSlug - Product ID (number) or slug (string)
|
|
279
|
+
* @example
|
|
280
|
+
* ```tsx
|
|
281
|
+
* // By slug (recommended)
|
|
282
|
+
* const { product, loading } = useProduct('wireless-headphones');
|
|
283
|
+
*
|
|
284
|
+
* // By ID
|
|
285
|
+
* const { product } = useProduct(123);
|
|
286
|
+
*
|
|
287
|
+
* // Display product
|
|
288
|
+
* if (product) {
|
|
289
|
+
* console.log(product.name, product.sale_price, product.images);
|
|
290
|
+
* }
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
106
293
|
declare function useProduct(idOrSlug: number | string): UseProductReturn;
|
|
107
294
|
interface UseCategoriesReturn {
|
|
108
295
|
categories: ProductCategory[];
|
package/dist/index.d.ts
CHANGED
|
@@ -17,24 +17,75 @@ declare function DiffsomeProvider({ children, config }: DiffsomeProviderProps):
|
|
|
17
17
|
declare function useDiffsome(): DiffsomeContextValue;
|
|
18
18
|
declare function useClient(): Diffsome;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Return type for the useAuth hook
|
|
22
|
+
*/
|
|
20
23
|
interface UseAuthReturn {
|
|
24
|
+
/** Currently logged in user, or null if not authenticated */
|
|
21
25
|
user: Member | null;
|
|
26
|
+
/** Whether the user is currently authenticated */
|
|
22
27
|
isAuthenticated: boolean;
|
|
28
|
+
/** Whether an auth operation is in progress */
|
|
23
29
|
loading: boolean;
|
|
30
|
+
/** Error from the last auth operation, if any */
|
|
24
31
|
error: Error | null;
|
|
32
|
+
/**
|
|
33
|
+
* Log in with email and password
|
|
34
|
+
* @param credentials - { email: string, password: string }
|
|
35
|
+
* @returns AuthResponse with user and token
|
|
36
|
+
*/
|
|
25
37
|
login: (credentials: LoginCredentials) => Promise<AuthResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Register a new account
|
|
40
|
+
* @param data - { name: string, email: string, password: string, password_confirmation: string }
|
|
41
|
+
* @returns AuthResponse with user and token
|
|
42
|
+
*/
|
|
26
43
|
register: (data: RegisterData) => Promise<AuthResponse>;
|
|
44
|
+
/** Log out and clear session */
|
|
27
45
|
logout: () => Promise<void>;
|
|
46
|
+
/** Fetch current user profile */
|
|
28
47
|
getProfile: () => Promise<Member>;
|
|
48
|
+
/**
|
|
49
|
+
* Update user profile
|
|
50
|
+
* @param data - { name?: string, email?: string, phone?: string, avatar?: File }
|
|
51
|
+
*/
|
|
29
52
|
updateProfile: (data: UpdateProfileData) => Promise<Member>;
|
|
53
|
+
/**
|
|
54
|
+
* Send password reset email
|
|
55
|
+
* @param email - User's email address
|
|
56
|
+
*/
|
|
30
57
|
forgotPassword: (email: string) => Promise<{
|
|
31
58
|
message: string;
|
|
32
59
|
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Reset password with token
|
|
62
|
+
* @param data - { token: string, email: string, password: string, password_confirmation: string }
|
|
63
|
+
*/
|
|
33
64
|
resetPassword: (data: ResetPasswordData) => Promise<{
|
|
34
65
|
message: string;
|
|
35
66
|
}>;
|
|
36
|
-
|
|
37
|
-
|
|
67
|
+
/** Refresh user profile from server */
|
|
68
|
+
refresh: () => Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Hook for user authentication - login, register, logout, profile management
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```tsx
|
|
75
|
+
* const { user, login, logout, loading } = useAuth();
|
|
76
|
+
*
|
|
77
|
+
* // Login
|
|
78
|
+
* await login({ email: 'user@example.com', password: 'secret' });
|
|
79
|
+
*
|
|
80
|
+
* // Check auth state
|
|
81
|
+
* if (user) {
|
|
82
|
+
* console.log('Logged in as:', user.name);
|
|
83
|
+
* }
|
|
84
|
+
*
|
|
85
|
+
* // Logout
|
|
86
|
+
* await logout();
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
38
89
|
declare function useAuth(): UseAuthReturn;
|
|
39
90
|
|
|
40
91
|
interface UseSocialAuthReturn {
|
|
@@ -47,24 +98,90 @@ interface UseSocialAuthReturn {
|
|
|
47
98
|
}
|
|
48
99
|
declare function useSocialAuth(): UseSocialAuthReturn;
|
|
49
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Return type for the useCart hook
|
|
103
|
+
*/
|
|
50
104
|
interface UseCartReturn {
|
|
105
|
+
/** Full cart object with metadata */
|
|
51
106
|
cart: Cart | null;
|
|
107
|
+
/** Array of cart items */
|
|
52
108
|
items: CartItem[];
|
|
109
|
+
/** Number of unique items in cart */
|
|
53
110
|
itemCount: number;
|
|
111
|
+
/** Total quantity of all items */
|
|
54
112
|
totalQuantity: number;
|
|
113
|
+
/** Cart subtotal before shipping */
|
|
55
114
|
subtotal: number;
|
|
115
|
+
/** Shipping fee */
|
|
56
116
|
shippingFee: number;
|
|
117
|
+
/** Cart total including shipping */
|
|
57
118
|
total: number;
|
|
58
|
-
loading
|
|
59
|
-
|
|
119
|
+
/** Whether cart is loading */
|
|
120
|
+
loading: boolean;
|
|
121
|
+
/** Error from last cart operation */
|
|
122
|
+
error: Error | null;
|
|
123
|
+
/**
|
|
124
|
+
* Add product to cart
|
|
125
|
+
* @param productId - Product ID to add
|
|
126
|
+
* @param quantity - Quantity to add (default: 1)
|
|
127
|
+
* @param variantId - Optional variant ID for variable products
|
|
128
|
+
* @param options - Optional custom options
|
|
129
|
+
* @returns Updated cart
|
|
130
|
+
*/
|
|
60
131
|
addToCart: (productId: number, quantity?: number, variantId?: number, options?: Record<string, string>) => Promise<Cart>;
|
|
132
|
+
/**
|
|
133
|
+
* Update item quantity
|
|
134
|
+
* @param itemId - Cart item ID
|
|
135
|
+
* @param quantity - New quantity
|
|
136
|
+
*/
|
|
61
137
|
updateItem: (itemId: number, quantity: number) => Promise<Cart>;
|
|
138
|
+
/**
|
|
139
|
+
* Remove item from cart
|
|
140
|
+
* @param itemId - Cart item ID to remove
|
|
141
|
+
*/
|
|
62
142
|
removeItem: (itemId: number) => Promise<Cart>;
|
|
143
|
+
/** Clear all items from cart */
|
|
63
144
|
clearCart: () => Promise<void>;
|
|
145
|
+
/** Refresh cart from server */
|
|
64
146
|
refresh: () => Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Check if product is in cart
|
|
149
|
+
* @param productId - Product ID
|
|
150
|
+
* @param variantId - Optional variant ID
|
|
151
|
+
*/
|
|
65
152
|
isInCart: (productId: number, variantId?: number) => boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Get quantity of product in cart
|
|
155
|
+
* @param productId - Product ID
|
|
156
|
+
* @param variantId - Optional variant ID
|
|
157
|
+
* @returns Quantity in cart (0 if not in cart)
|
|
158
|
+
*/
|
|
66
159
|
getItemQuantity: (productId: number, variantId?: number) => number;
|
|
67
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Hook for shopping cart management
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```tsx
|
|
166
|
+
* const { items, total, addToCart, removeItem, clearCart } = useCart();
|
|
167
|
+
*
|
|
168
|
+
* // Add to cart
|
|
169
|
+
* await addToCart(123, 2); // Add product ID 123, quantity 2
|
|
170
|
+
*
|
|
171
|
+
* // With variant
|
|
172
|
+
* await addToCart(123, 1, 456); // Product 123, variant 456
|
|
173
|
+
*
|
|
174
|
+
* // Display cart
|
|
175
|
+
* items.map(item => (
|
|
176
|
+
* <div key={item.id}>
|
|
177
|
+
* {item.product_name} x {item.quantity} = ${item.subtotal}
|
|
178
|
+
* </div>
|
|
179
|
+
* ));
|
|
180
|
+
*
|
|
181
|
+
* // Total
|
|
182
|
+
* console.log('Total:', total);
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
68
185
|
declare function useCart(): UseCartReturn;
|
|
69
186
|
|
|
70
187
|
interface UseWishlistReturn {
|
|
@@ -83,26 +200,96 @@ interface UseWishlistReturn {
|
|
|
83
200
|
}
|
|
84
201
|
declare function useWishlist(): UseWishlistReturn;
|
|
85
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Return type for the useProducts hook
|
|
205
|
+
*/
|
|
86
206
|
interface UseProductsReturn {
|
|
207
|
+
/** Array of products */
|
|
87
208
|
products: Product[];
|
|
209
|
+
/** Pagination metadata (current_page, last_page, total, per_page) */
|
|
88
210
|
meta: PaginationMeta | null;
|
|
211
|
+
/** Whether products are loading */
|
|
89
212
|
loading: boolean;
|
|
213
|
+
/** Error from last operation */
|
|
90
214
|
error: Error | null;
|
|
215
|
+
/** Whether more pages are available */
|
|
91
216
|
hasMore: boolean;
|
|
217
|
+
/** Load next page of products (appends to list) */
|
|
92
218
|
loadMore: () => Promise<void>;
|
|
219
|
+
/** Refresh products from server */
|
|
93
220
|
refresh: () => Promise<void>;
|
|
221
|
+
/**
|
|
222
|
+
* Search products by query
|
|
223
|
+
* @param query - Search term
|
|
224
|
+
*/
|
|
94
225
|
search: (query: string) => Promise<void>;
|
|
95
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Options for useProducts hook
|
|
229
|
+
*/
|
|
96
230
|
interface UseProductsOptions extends ProductListParams {
|
|
231
|
+
/** Whether to fetch products on mount (default: true) */
|
|
97
232
|
autoFetch?: boolean;
|
|
98
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Hook for fetching products with pagination, filtering, and search
|
|
236
|
+
*
|
|
237
|
+
* @param options - Filtering and pagination options
|
|
238
|
+
* @example
|
|
239
|
+
* ```tsx
|
|
240
|
+
* // Basic usage
|
|
241
|
+
* const { products, loading } = useProducts();
|
|
242
|
+
*
|
|
243
|
+
* // With filters
|
|
244
|
+
* const { products } = useProducts({
|
|
245
|
+
* category: 'electronics',
|
|
246
|
+
* per_page: 20,
|
|
247
|
+
* sort: 'price_low',
|
|
248
|
+
* min_price: 10,
|
|
249
|
+
* max_price: 100,
|
|
250
|
+
* });
|
|
251
|
+
*
|
|
252
|
+
* // Infinite scroll
|
|
253
|
+
* const { products, hasMore, loadMore } = useProducts({ per_page: 12 });
|
|
254
|
+
* // Call loadMore() when user scrolls to bottom
|
|
255
|
+
*
|
|
256
|
+
* // Search
|
|
257
|
+
* const { products, search } = useProducts();
|
|
258
|
+
* await search('laptop'); // Searches products
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
99
261
|
declare function useProducts(options?: UseProductsOptions): UseProductsReturn;
|
|
262
|
+
/**
|
|
263
|
+
* Return type for the useProduct hook
|
|
264
|
+
*/
|
|
100
265
|
interface UseProductReturn {
|
|
266
|
+
/** Product data, or null if not loaded */
|
|
101
267
|
product: Product | null;
|
|
102
|
-
loading
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
268
|
+
/** Whether product is loading */
|
|
269
|
+
loading: boolean;
|
|
270
|
+
/** Error from last operation */
|
|
271
|
+
error: Error | null;
|
|
272
|
+
/** Refresh product from server */
|
|
273
|
+
refresh: () => Promise<void>;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Hook for fetching a single product by ID or slug
|
|
277
|
+
*
|
|
278
|
+
* @param idOrSlug - Product ID (number) or slug (string)
|
|
279
|
+
* @example
|
|
280
|
+
* ```tsx
|
|
281
|
+
* // By slug (recommended)
|
|
282
|
+
* const { product, loading } = useProduct('wireless-headphones');
|
|
283
|
+
*
|
|
284
|
+
* // By ID
|
|
285
|
+
* const { product } = useProduct(123);
|
|
286
|
+
*
|
|
287
|
+
* // Display product
|
|
288
|
+
* if (product) {
|
|
289
|
+
* console.log(product.name, product.sale_price, product.images);
|
|
290
|
+
* }
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
106
293
|
declare function useProduct(idOrSlug: number | string): UseProductReturn;
|
|
107
294
|
interface UseCategoriesReturn {
|
|
108
295
|
categories: ProductCategory[];
|