@basedone/core 0.1.10 → 0.2.1
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/dist/chunk-4UEJOM6W.mjs +1 -3
- package/dist/chunk-MVFO4WRF.mjs +2091 -0
- package/dist/chunk-VBC6EQ7Q.mjs +235 -0
- package/dist/client-CgmiTuEX.d.mts +179 -0
- package/dist/client-CgmiTuEX.d.ts +179 -0
- package/dist/ecommerce.d.mts +3986 -0
- package/dist/ecommerce.d.ts +3986 -0
- package/dist/ecommerce.js +2135 -0
- package/dist/ecommerce.mjs +2 -0
- package/dist/index.d.mts +51 -43
- package/dist/index.d.ts +51 -43
- package/dist/index.js +2795 -205
- package/dist/index.mjs +68 -90
- package/dist/{meta-FVJIMALT.mjs → meta-JB5ITE27.mjs} +4 -10
- package/dist/meta-UOGUG3OW.mjs +3 -7
- package/dist/{perpDexs-GGL32HT4.mjs → perpDexs-3LRJ5ZHM.mjs} +37 -8
- package/dist/{perpDexs-G7V2QIM6.mjs → perpDexs-4ISLD7NX.mjs} +177 -32
- package/dist/react.d.mts +39 -0
- package/dist/react.d.ts +39 -0
- package/dist/react.js +268 -0
- package/dist/react.mjs +31 -0
- package/dist/{spotMeta-OD7S6HGW.mjs → spotMeta-GHXX7C5M.mjs} +24 -9
- package/dist/{spotMeta-PCN4Z4R3.mjs → spotMeta-IBBUP2SG.mjs} +54 -6
- package/dist/staticMeta-GM7T3OYL.mjs +3 -6
- package/dist/staticMeta-QV2KMX57.mjs +3 -6
- package/ecommerce.ts +15 -0
- package/index.ts +6 -0
- package/lib/ecommerce/FLASH_SALES.md +340 -0
- package/lib/ecommerce/QUICK_REFERENCE.md +211 -0
- package/lib/ecommerce/README.md +391 -0
- package/lib/ecommerce/USAGE_EXAMPLES.md +704 -0
- package/lib/ecommerce/client/base.ts +272 -0
- package/lib/ecommerce/client/customer.ts +639 -0
- package/lib/ecommerce/client/merchant.ts +1341 -0
- package/lib/ecommerce/index.ts +51 -0
- package/lib/ecommerce/types/entities.ts +791 -0
- package/lib/ecommerce/types/enums.ts +270 -0
- package/lib/ecommerce/types/index.ts +18 -0
- package/lib/ecommerce/types/requests.ts +580 -0
- package/lib/ecommerce/types/responses.ts +857 -0
- package/lib/ecommerce/utils/errors.ts +113 -0
- package/lib/ecommerce/utils/helpers.ts +131 -0
- package/lib/hip3/market-info.ts +1 -1
- package/lib/instrument/client.ts +351 -0
- package/lib/meta/data/mainnet/perpDexs.json +34 -4
- package/lib/meta/data/mainnet/spotMeta.json +21 -3
- package/lib/meta/data/testnet/meta.json +1 -3
- package/lib/meta/data/testnet/perpDexs.json +174 -28
- package/lib/meta/data/testnet/spotMeta.json +51 -0
- package/lib/react/InstrumentProvider.tsx +69 -0
- package/lib/utils/flooredDateTime.ts +55 -0
- package/lib/utils/time.ts +51 -0
- package/package.json +37 -11
- package/react.ts +1 -0
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { InstrumentClient } from './chunk-VBC6EQ7Q.mjs';
|
|
2
|
+
import './chunk-4UEJOM6W.mjs';
|
|
3
|
+
import { createContext, useMemo, useContext } from 'react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var InstrumentContext = createContext(null);
|
|
7
|
+
function InstrumentsProvider({
|
|
8
|
+
spotMeta,
|
|
9
|
+
allPerpsMeta,
|
|
10
|
+
children
|
|
11
|
+
}) {
|
|
12
|
+
const client = useMemo(
|
|
13
|
+
() => new InstrumentClient(spotMeta, allPerpsMeta).init(),
|
|
14
|
+
[spotMeta, allPerpsMeta]
|
|
15
|
+
);
|
|
16
|
+
return /* @__PURE__ */ jsx(InstrumentContext.Provider, { value: client, children });
|
|
17
|
+
}
|
|
18
|
+
function useInstruments() {
|
|
19
|
+
const client = useContext(InstrumentContext);
|
|
20
|
+
if (!client) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
"useInstruments must be used within an InstrumentsProvider"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return client;
|
|
26
|
+
}
|
|
27
|
+
function useInstrumentsOptional() {
|
|
28
|
+
return useContext(InstrumentContext);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { InstrumentsProvider, useInstruments, useInstrumentsOptional };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './chunk-4UEJOM6W.mjs';
|
|
2
2
|
|
|
3
3
|
// lib/meta/data/mainnet/spotMeta.json
|
|
4
4
|
var universe = [
|
|
@@ -2179,6 +2179,15 @@ var universe = [
|
|
|
2179
2179
|
name: "@257",
|
|
2180
2180
|
index: 257,
|
|
2181
2181
|
isCanonical: false
|
|
2182
|
+
},
|
|
2183
|
+
{
|
|
2184
|
+
tokens: [
|
|
2185
|
+
398,
|
|
2186
|
+
0
|
|
2187
|
+
],
|
|
2188
|
+
name: "@258",
|
|
2189
|
+
index: 258,
|
|
2190
|
+
isCanonical: false
|
|
2182
2191
|
}
|
|
2183
2192
|
];
|
|
2184
2193
|
var tokens = [
|
|
@@ -2189,7 +2198,10 @@ var tokens = [
|
|
|
2189
2198
|
index: 0,
|
|
2190
2199
|
tokenId: "0x6d1e7cde53ba9467b783cb7c530ce054",
|
|
2191
2200
|
isCanonical: true,
|
|
2192
|
-
evmContract:
|
|
2201
|
+
evmContract: {
|
|
2202
|
+
address: "0x6b9e773128f453f5c2c60935ee2de2cbc5390a24",
|
|
2203
|
+
evm_extra_wei_decimals: -2
|
|
2204
|
+
},
|
|
2193
2205
|
fullName: null,
|
|
2194
2206
|
deployerTradingFeeShare: "0.0"
|
|
2195
2207
|
},
|
|
@@ -6683,7 +6695,10 @@ var tokens = [
|
|
|
6683
6695
|
index: 381,
|
|
6684
6696
|
tokenId: "0xbd6af8c18f3e56d2da95a3c81d18e814",
|
|
6685
6697
|
isCanonical: false,
|
|
6686
|
-
evmContract:
|
|
6698
|
+
evmContract: {
|
|
6699
|
+
address: "0x99603f5cb890faca5cc5495ccc74391cdffa28bc",
|
|
6700
|
+
evm_extra_wei_decimals: 10
|
|
6701
|
+
},
|
|
6687
6702
|
fullName: "Funnel BNB",
|
|
6688
6703
|
deployerTradingFeeShare: "1.0"
|
|
6689
6704
|
},
|
|
@@ -6882,7 +6897,10 @@ var tokens = [
|
|
|
6882
6897
|
index: 398,
|
|
6883
6898
|
tokenId: "0xec43194f64d555bdaef5afb5b6c6c686",
|
|
6884
6899
|
isCanonical: false,
|
|
6885
|
-
evmContract:
|
|
6900
|
+
evmContract: {
|
|
6901
|
+
address: "0xa51dc81944a15623874981181a99d6c56b20ed56",
|
|
6902
|
+
evm_extra_wei_decimals: 11
|
|
6903
|
+
},
|
|
6886
6904
|
fullName: "Stable",
|
|
6887
6905
|
deployerTradingFeeShare: "1.0"
|
|
6888
6906
|
},
|
|
@@ -6946,8 +6964,5 @@ var spotMeta_default = {
|
|
|
6946
6964
|
universe,
|
|
6947
6965
|
tokens
|
|
6948
6966
|
};
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
tokens,
|
|
6952
|
-
universe
|
|
6953
|
-
};
|
|
6967
|
+
|
|
6968
|
+
export { spotMeta_default as default, tokens, universe };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './chunk-4UEJOM6W.mjs';
|
|
2
2
|
|
|
3
3
|
// lib/meta/data/testnet/spotMeta.json
|
|
4
4
|
var universe = [
|
|
@@ -10648,6 +10648,24 @@ var universe = [
|
|
|
10648
10648
|
name: "@1378",
|
|
10649
10649
|
index: 1378,
|
|
10650
10650
|
isCanonical: false
|
|
10651
|
+
},
|
|
10652
|
+
{
|
|
10653
|
+
tokens: [
|
|
10654
|
+
1498,
|
|
10655
|
+
0
|
|
10656
|
+
],
|
|
10657
|
+
name: "@1379",
|
|
10658
|
+
index: 1379,
|
|
10659
|
+
isCanonical: false
|
|
10660
|
+
},
|
|
10661
|
+
{
|
|
10662
|
+
tokens: [
|
|
10663
|
+
1498,
|
|
10664
|
+
1452
|
|
10665
|
+
],
|
|
10666
|
+
name: "@1380",
|
|
10667
|
+
index: 1380,
|
|
10668
|
+
isCanonical: false
|
|
10651
10669
|
}
|
|
10652
10670
|
];
|
|
10653
10671
|
var tokens = [
|
|
@@ -27531,14 +27549,44 @@ aa aaa\\aaaaaa"aaaa'aaaaa{}`,
|
|
|
27531
27549
|
evmContract: null,
|
|
27532
27550
|
fullName: "$WASH launchpad 2025-12-02T20:17:18.214Z",
|
|
27533
27551
|
deployerTradingFeeShare: "1.0"
|
|
27552
|
+
},
|
|
27553
|
+
{
|
|
27554
|
+
name: "RNSZPK",
|
|
27555
|
+
szDecimals: 0,
|
|
27556
|
+
weiDecimals: 10,
|
|
27557
|
+
index: 1496,
|
|
27558
|
+
tokenId: "0x699de51e4fc0e6d7a6e450d2c3d92a1a",
|
|
27559
|
+
isCanonical: false,
|
|
27560
|
+
evmContract: null,
|
|
27561
|
+
fullName: "$WASH launchpad 2025-12-04T03:17:21.132Z",
|
|
27562
|
+
deployerTradingFeeShare: "1.0"
|
|
27563
|
+
},
|
|
27564
|
+
{
|
|
27565
|
+
name: "VENAHX",
|
|
27566
|
+
szDecimals: 1,
|
|
27567
|
+
weiDecimals: 9,
|
|
27568
|
+
index: 1497,
|
|
27569
|
+
tokenId: "0x43513901b952fcb6d8d5697ce17fa58f",
|
|
27570
|
+
isCanonical: false,
|
|
27571
|
+
evmContract: null,
|
|
27572
|
+
fullName: "$WASH launchpad 2025-12-05T10:17:24.096Z",
|
|
27573
|
+
deployerTradingFeeShare: "1.0"
|
|
27574
|
+
},
|
|
27575
|
+
{
|
|
27576
|
+
name: "LMB",
|
|
27577
|
+
szDecimals: 2,
|
|
27578
|
+
weiDecimals: 8,
|
|
27579
|
+
index: 1498,
|
|
27580
|
+
tokenId: "0x9f416264220c915ec41b6c4c0b11fab1",
|
|
27581
|
+
isCanonical: false,
|
|
27582
|
+
evmContract: null,
|
|
27583
|
+
fullName: "LMB",
|
|
27584
|
+
deployerTradingFeeShare: "1.0"
|
|
27534
27585
|
}
|
|
27535
27586
|
];
|
|
27536
27587
|
var spotMeta_default = {
|
|
27537
27588
|
universe,
|
|
27538
27589
|
tokens
|
|
27539
27590
|
};
|
|
27540
|
-
|
|
27541
|
-
|
|
27542
|
-
tokens,
|
|
27543
|
-
universe
|
|
27544
|
-
};
|
|
27591
|
+
|
|
27592
|
+
export { spotMeta_default as default, tokens, universe };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './chunk-4UEJOM6W.mjs';
|
|
2
2
|
|
|
3
3
|
// lib/meta/data/testnet/staticMeta.json
|
|
4
4
|
var coins = {
|
|
@@ -16,8 +16,5 @@ var staticMeta_default = {
|
|
|
16
16
|
coins,
|
|
17
17
|
dexs
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
staticMeta_default as default,
|
|
22
|
-
dexs
|
|
23
|
-
};
|
|
19
|
+
|
|
20
|
+
export { coins, staticMeta_default as default, dexs };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import './chunk-4UEJOM6W.mjs';
|
|
2
2
|
|
|
3
3
|
// lib/meta/data/mainnet/staticMeta.json
|
|
4
4
|
var coins = {
|
|
@@ -18,8 +18,5 @@ var staticMeta_default = {
|
|
|
18
18
|
coins,
|
|
19
19
|
dexs
|
|
20
20
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
staticMeta_default as default,
|
|
24
|
-
dexs
|
|
25
|
-
};
|
|
21
|
+
|
|
22
|
+
export { coins, staticMeta_default as default, dexs };
|
package/ecommerce.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ecommerce SDK Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This file provides a separate entry point for the ecommerce SDK,
|
|
5
|
+
* allowing users to import only ecommerce functionality without
|
|
6
|
+
* pulling in the entire @basedone/core package.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export * from "./lib/ecommerce";
|
|
15
|
+
|
package/index.ts
CHANGED
|
@@ -4,5 +4,11 @@ export * from "./lib/pup";
|
|
|
4
4
|
export * from "./lib/constants/tokens";
|
|
5
5
|
export * from "./lib/meta/metadata";
|
|
6
6
|
export * from "./lib/utils/formatter";
|
|
7
|
+
export * from "./lib/utils/flooredDateTime";
|
|
8
|
+
export * from "./lib/utils/time";
|
|
7
9
|
export * from "./lib/hip3/utils";
|
|
8
10
|
export * from "./lib/hip3/market-info";
|
|
11
|
+
export * from "./lib/instrument/client";
|
|
12
|
+
|
|
13
|
+
// Ecommerce SDK
|
|
14
|
+
export * from "./lib/ecommerce";
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Flash Sales SDK Documentation
|
|
2
|
+
|
|
3
|
+
This guide shows you how to use the `@basedone/core/ecommerce` SDK to fetch and display active flash sales.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Flash sales are time-limited deals with discounted products. The SDK provides a simple method to fetch all currently active flash sales with their products, pricing, and countdown information.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Basic Usage
|
|
16
|
+
|
|
17
|
+
### Initialize the Client
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
const client = new CustomerEcommerceClient({
|
|
21
|
+
baseURL: "https://your-api-domain.com",
|
|
22
|
+
authToken: "your-auth-token", // Optional for public endpoints
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Fetch Active Flash Sales
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Get all active flash sales
|
|
30
|
+
const result = await client.getActiveFlashSales();
|
|
31
|
+
|
|
32
|
+
console.log(`Found ${result.flashSales.length} active flash sales`);
|
|
33
|
+
|
|
34
|
+
// Access the first flash sale
|
|
35
|
+
const featuredSale = result.flashSales[0];
|
|
36
|
+
console.log(`Sale: ${featuredSale.name}`);
|
|
37
|
+
console.log(`Ends at: ${featuredSale.endsAt}`);
|
|
38
|
+
|
|
39
|
+
// Access countdown timer info
|
|
40
|
+
if (result.timeRemaining) {
|
|
41
|
+
console.log(`Time remaining: ${result.timeRemaining.remainingSeconds} seconds`);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### With Parameters
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
// Limit the number of flash sales returned
|
|
49
|
+
const result = await client.getActiveFlashSales({
|
|
50
|
+
limit: 5, // Maximum 50
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Filter by merchant (optional)
|
|
54
|
+
const result = await client.getActiveFlashSales({
|
|
55
|
+
limit: 10,
|
|
56
|
+
merchantId: "merchant_123",
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Response Structure
|
|
61
|
+
|
|
62
|
+
### ActiveFlashSalesResponse
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
{
|
|
66
|
+
flashSales: FlashSale[]; // Array of active flash sales
|
|
67
|
+
timeRemaining: { // Countdown info for featured sale
|
|
68
|
+
endsAt: string; // ISO timestamp
|
|
69
|
+
remainingSeconds: number; // Seconds until end
|
|
70
|
+
} | null;
|
|
71
|
+
serverTime: string; // Server timestamp for sync
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### FlashSale
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
{
|
|
79
|
+
id: string; // Flash sale ID
|
|
80
|
+
name: string; // Sale name (e.g., "Summer Flash Sale")
|
|
81
|
+
description?: string; // Optional description
|
|
82
|
+
startsAt: string; // ISO timestamp
|
|
83
|
+
endsAt: string; // ISO timestamp
|
|
84
|
+
badgeText?: string; // Badge text (e.g., "Mall")
|
|
85
|
+
badgeColor?: string; // Badge color hex (e.g., "#facc15")
|
|
86
|
+
priority: number; // Display priority (higher = first)
|
|
87
|
+
merchant?: { // Merchant info (null for admin sales)
|
|
88
|
+
id: string;
|
|
89
|
+
name: string;
|
|
90
|
+
} | null;
|
|
91
|
+
items: FlashSaleItem[]; // Products in this sale
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### FlashSaleItem
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
{
|
|
99
|
+
id: string; // Item ID
|
|
100
|
+
productId: string; // Product ID
|
|
101
|
+
salePrice: string; // Sale price in USDC (e.g., "17.90")
|
|
102
|
+
originalPrice: string; // Original price in USDC (e.g., "30.00")
|
|
103
|
+
discountPercent: number; // Discount percentage (e.g., 42)
|
|
104
|
+
maxQuantity: number | null; // Max available (null = unlimited)
|
|
105
|
+
soldQuantity: number; // Already sold count
|
|
106
|
+
remainingQuantity: number | null; // Remaining (null if unlimited)
|
|
107
|
+
limitPerCustomer: number | null; // Max per customer
|
|
108
|
+
product: { // Full product details
|
|
109
|
+
id: string;
|
|
110
|
+
title: string;
|
|
111
|
+
description?: string;
|
|
112
|
+
images: string[];
|
|
113
|
+
priceUSDC: string;
|
|
114
|
+
inventory: number | null;
|
|
115
|
+
soldCount: number;
|
|
116
|
+
averageRating: number | null;
|
|
117
|
+
reviewCount: number;
|
|
118
|
+
merchant?: {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Example: Display Flash Sales
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
130
|
+
|
|
131
|
+
const client = new CustomerEcommerceClient({
|
|
132
|
+
baseURL: "https://api.example.com",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
async function displayFlashSales() {
|
|
136
|
+
try {
|
|
137
|
+
const result = await client.getActiveFlashSales({ limit: 10 });
|
|
138
|
+
|
|
139
|
+
if (result.flashSales.length === 0) {
|
|
140
|
+
console.log("No active flash sales");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Display each flash sale
|
|
145
|
+
result.flashSales.forEach((sale) => {
|
|
146
|
+
console.log(`\n⚡ ${sale.name}`);
|
|
147
|
+
console.log(` Badge: ${sale.badgeText || "Flash Deal"}`);
|
|
148
|
+
console.log(` Ends: ${new Date(sale.endsAt).toLocaleString()}`);
|
|
149
|
+
console.log(` Products: ${sale.items.length}`);
|
|
150
|
+
|
|
151
|
+
// Display products
|
|
152
|
+
sale.items.forEach((item) => {
|
|
153
|
+
console.log(` - ${item.product.title}`);
|
|
154
|
+
console.log(` Original: $${item.originalPrice} → Sale: $${item.salePrice}`);
|
|
155
|
+
console.log(` Discount: ${item.discountPercent}% off`);
|
|
156
|
+
|
|
157
|
+
if (item.remainingQuantity !== null) {
|
|
158
|
+
console.log(` Only ${item.remainingQuantity} left!`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Display countdown
|
|
164
|
+
if (result.timeRemaining) {
|
|
165
|
+
const hours = Math.floor(result.timeRemaining.remainingSeconds / 3600);
|
|
166
|
+
const minutes = Math.floor((result.timeRemaining.remainingSeconds % 3600) / 60);
|
|
167
|
+
const seconds = result.timeRemaining.remainingSeconds % 60;
|
|
168
|
+
console.log(`\n⏰ Time remaining: ${hours}h ${minutes}m ${seconds}s`);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error("Error fetching flash sales:", error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
displayFlashSales();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Example: React Component
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { useState, useEffect } from "react";
|
|
182
|
+
import { CustomerEcommerceClient, ActiveFlashSalesResponse } from "@basedone/core/ecommerce";
|
|
183
|
+
|
|
184
|
+
function FlashSalesList() {
|
|
185
|
+
const [flashSales, setFlashSales] = useState<ActiveFlashSalesResponse | null>(null);
|
|
186
|
+
const [loading, setLoading] = useState(true);
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const client = new CustomerEcommerceClient({
|
|
190
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || "",
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
client
|
|
194
|
+
.getActiveFlashSales({ limit: 10 })
|
|
195
|
+
.then((data) => {
|
|
196
|
+
setFlashSales(data);
|
|
197
|
+
setLoading(false);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
console.error("Failed to load flash sales:", error);
|
|
201
|
+
setLoading(false);
|
|
202
|
+
});
|
|
203
|
+
}, []);
|
|
204
|
+
|
|
205
|
+
if (loading) return <div>Loading flash sales...</div>;
|
|
206
|
+
if (!flashSales || flashSales.flashSales.length === 0) {
|
|
207
|
+
return <div>No active flash sales</div>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<div>
|
|
212
|
+
{flashSales.flashSales.map((sale) => (
|
|
213
|
+
<div key={sale.id} className="flash-sale-card">
|
|
214
|
+
<h2>{sale.name}</h2>
|
|
215
|
+
{sale.badgeText && (
|
|
216
|
+
<span style={{ backgroundColor: sale.badgeColor || "#f97316" }}>
|
|
217
|
+
{sale.badgeText}
|
|
218
|
+
</span>
|
|
219
|
+
)}
|
|
220
|
+
<p>Ends: {new Date(sale.endsAt).toLocaleString()}</p>
|
|
221
|
+
|
|
222
|
+
<div className="products">
|
|
223
|
+
{sale.items.map((item) => (
|
|
224
|
+
<div key={item.id} className="product-card">
|
|
225
|
+
<img src={item.product.images[0]} alt={item.product.title} />
|
|
226
|
+
<h3>{item.product.title}</h3>
|
|
227
|
+
<div className="pricing">
|
|
228
|
+
<span className="original-price">${item.originalPrice}</span>
|
|
229
|
+
<span className="sale-price">${item.salePrice}</span>
|
|
230
|
+
<span className="discount">-{item.discountPercent}%</span>
|
|
231
|
+
</div>
|
|
232
|
+
{item.remainingQuantity !== null && item.remainingQuantity <= 10 && (
|
|
233
|
+
<div className="scarcity">Only {item.remainingQuantity} left!</div>
|
|
234
|
+
)}
|
|
235
|
+
</div>
|
|
236
|
+
))}
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
))}
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Example: Countdown Timer
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { useState, useEffect } from "react";
|
|
249
|
+
import { CustomerEcommerceClient } from "@basedone/core/ecommerce";
|
|
250
|
+
|
|
251
|
+
function CountdownTimer() {
|
|
252
|
+
const [timeLeft, setTimeLeft] = useState({ hours: 0, minutes: 0, seconds: 0 });
|
|
253
|
+
const [serverTime, setServerTime] = useState<Date | null>(null);
|
|
254
|
+
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
const client = new CustomerEcommerceClient({
|
|
257
|
+
baseURL: process.env.NEXT_PUBLIC_API_URL || "",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Fetch flash sales and sync with server time
|
|
261
|
+
client.getActiveFlashSales().then((result) => {
|
|
262
|
+
if (result.timeRemaining) {
|
|
263
|
+
const serverTimeDate = new Date(result.serverTime);
|
|
264
|
+
setServerTime(serverTimeDate);
|
|
265
|
+
|
|
266
|
+
// Calculate offset for accurate countdown
|
|
267
|
+
const offset = serverTimeDate.getTime() - Date.now();
|
|
268
|
+
const endTime = new Date(result.timeRemaining.endsAt).getTime();
|
|
269
|
+
|
|
270
|
+
const updateTimer = () => {
|
|
271
|
+
const now = Date.now() + offset;
|
|
272
|
+
const remaining = Math.max(0, endTime - now);
|
|
273
|
+
|
|
274
|
+
setTimeLeft({
|
|
275
|
+
hours: Math.floor(remaining / (1000 * 60 * 60)),
|
|
276
|
+
minutes: Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)),
|
|
277
|
+
seconds: Math.floor((remaining % (1000 * 60)) / 1000),
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
updateTimer();
|
|
282
|
+
const interval = setInterval(updateTimer, 1000);
|
|
283
|
+
return () => clearInterval(interval);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}, []);
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<div className="countdown">
|
|
290
|
+
{timeLeft.hours.toString().padStart(2, "0")}:
|
|
291
|
+
{timeLeft.minutes.toString().padStart(2, "0")}:
|
|
292
|
+
{timeLeft.seconds.toString().padStart(2, "0")}
|
|
293
|
+
</div>
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Error Handling
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
try {
|
|
302
|
+
const result = await client.getActiveFlashSales();
|
|
303
|
+
// Use result...
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if (error instanceof EcommerceApiError) {
|
|
306
|
+
console.error("API Error:", error.message);
|
|
307
|
+
console.error("Status:", error.status);
|
|
308
|
+
} else {
|
|
309
|
+
console.error("Unexpected error:", error);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Notes
|
|
315
|
+
|
|
316
|
+
- Flash sales are automatically filtered to only show active sales (between `startsAt` and `endsAt`)
|
|
317
|
+
- The `timeRemaining` field provides countdown info for the featured/first flash sale
|
|
318
|
+
- Use `serverTime` to sync client-side countdown timers accurately
|
|
319
|
+
- Products in flash sales are automatically filtered to only include active products
|
|
320
|
+
- Discount percentages are pre-calculated for convenience
|
|
321
|
+
- The endpoint caches responses for 30 seconds due to time-sensitive nature
|
|
322
|
+
|
|
323
|
+
## API Endpoint
|
|
324
|
+
|
|
325
|
+
The SDK method calls:
|
|
326
|
+
```
|
|
327
|
+
GET /api/marketplace/flash-sales/active?limit={limit}&merchantId={merchantId}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Related Types
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import type {
|
|
334
|
+
ActiveFlashSalesResponse,
|
|
335
|
+
FlashSale,
|
|
336
|
+
FlashSaleItem,
|
|
337
|
+
ListActiveFlashSalesParams,
|
|
338
|
+
} from "@basedone/core/ecommerce";
|
|
339
|
+
```
|
|
340
|
+
|