@windrun-huaiin/third-ui 30.1.0 → 31.0.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/README.md +109 -143
- package/dist/ai/ai-prompt-textarea.js +5 -5
- package/dist/ai/ai-prompt-textarea.mjs +5 -5
- package/dist/clerk/clerk-auth-appearance.d.ts +13 -0
- package/dist/clerk/clerk-auth-appearance.js +19 -0
- package/dist/clerk/clerk-auth-appearance.mjs +15 -0
- package/dist/clerk/clerk-auth-modal-appearance.d.ts +12 -0
- package/dist/clerk/clerk-auth-modal-appearance.js +17 -0
- package/dist/clerk/clerk-auth-modal-appearance.mjs +14 -0
- package/dist/clerk/clerk-page-context-generator.js +3 -3
- package/dist/clerk/clerk-page-context-generator.mjs +3 -3
- package/dist/clerk/clerk-page-generator.js +4 -4
- package/dist/clerk/clerk-page-generator.mjs +4 -4
- package/dist/clerk/clerk-user-client.js +2 -1
- package/dist/clerk/clerk-user-client.mjs +2 -1
- package/dist/clerk/fingerprint/fingerprint-client.d.ts +10 -10
- package/dist/clerk/fingerprint/fingerprint-client.js +20 -20
- package/dist/clerk/fingerprint/fingerprint-client.mjs +20 -20
- package/dist/clerk/fingerprint/fingerprint-provider.d.ts +3 -3
- package/dist/clerk/fingerprint/fingerprint-provider.js +8 -8
- package/dist/clerk/fingerprint/fingerprint-provider.mjs +8 -8
- package/dist/clerk/fingerprint/fingerprint-server.d.ts +12 -12
- package/dist/clerk/fingerprint/fingerprint-server.js +17 -17
- package/dist/clerk/fingerprint/fingerprint-server.mjs +17 -17
- package/dist/clerk/fingerprint/fingerprint-shared.d.ts +3 -3
- package/dist/clerk/fingerprint/fingerprint-shared.js +10 -10
- package/dist/clerk/fingerprint/fingerprint-shared.mjs +10 -10
- package/dist/clerk/fingerprint/types.d.ts +0 -1
- package/dist/clerk/fingerprint/use-fingerprint.js +7 -7
- package/dist/clerk/fingerprint/use-fingerprint.mjs +7 -7
- package/dist/clerk/signin-with-fingerprint-client.d.ts +2 -2
- package/dist/clerk/signin-with-fingerprint-client.js +7 -6
- package/dist/clerk/signin-with-fingerprint-client.mjs +7 -6
- package/dist/clerk/signup-button-with-fingerprint-client.js +6 -4
- package/dist/clerk/signup-button-with-fingerprint-client.mjs +6 -4
- package/dist/clerk/signup-with-fingerprint-client.d.ts +2 -2
- package/dist/clerk/signup-with-fingerprint-client.js +7 -6
- package/dist/clerk/signup-with-fingerprint-client.mjs +7 -6
- package/dist/fuma/heavy/mermaid.js +1 -1
- package/dist/fuma/heavy/mermaid.mjs +1 -1
- package/dist/fuma/mdx/fuma-github-info.d.ts +1 -2
- package/dist/fuma/mdx/fuma-github-info.js +3 -6
- package/dist/fuma/mdx/fuma-github-info.mjs +3 -6
- package/dist/fuma/site-x.js +0 -1
- package/dist/fuma/site-x.mjs +0 -1
- package/dist/main/calendar/calendar-date-range-input.js +1 -1
- package/dist/main/calendar/calendar-date-range-input.mjs +1 -1
- package/dist/main/credit/credit-overview-nav-client.d.ts +12 -0
- package/dist/main/credit/credit-overview-nav-client.js +65 -0
- package/dist/main/credit/credit-overview-nav-client.mjs +63 -0
- package/dist/main/credit/index.d.ts +2 -0
- package/dist/main/credit/index.js +2 -0
- package/dist/main/credit/index.mjs +1 -0
- package/dist/main/credit/types.d.ts +8 -8
- package/dist/main/money-price/index.d.ts +1 -1
- package/dist/main/money-price/money-price-button.js +10 -10
- package/dist/main/money-price/money-price-button.mjs +10 -10
- package/dist/main/money-price/money-price-config-util.d.ts +30 -30
- package/dist/main/money-price/money-price-config-util.js +48 -48
- package/dist/main/money-price/money-price-config-util.mjs +48 -48
- package/dist/main/money-price/money-price-interactive.js +30 -18
- package/dist/main/money-price/money-price-interactive.mjs +30 -18
- package/dist/main/money-price/money-price-types.d.ts +7 -1
- package/dist/main/money-price/money-price-types.js +2 -2
- package/dist/main/money-price/money-price-types.mjs +2 -2
- package/dist/main/money-price/server.d.ts +1 -1
- package/dist/main/pill-select/x-pill-select.js +2 -2
- package/dist/main/pill-select/x-pill-select.mjs +2 -2
- package/dist/main/server.d.ts +1 -1
- package/package.json +13 -7
- package/src/ai/ai-prompt-textarea.tsx +6 -6
- package/src/clerk/clerk-auth-appearance.ts +16 -0
- package/src/clerk/clerk-page-context-generator.tsx +3 -5
- package/src/clerk/clerk-page-generator.tsx +9 -8
- package/src/clerk/clerk-user-client.tsx +14 -5
- package/src/clerk/fingerprint/fingerprint-client.ts +20 -20
- package/src/clerk/fingerprint/fingerprint-provider.tsx +11 -11
- package/src/clerk/fingerprint/fingerprint-server.ts +17 -17
- package/src/clerk/fingerprint/fingerprint-shared.ts +10 -10
- package/src/clerk/fingerprint/types.ts +0 -1
- package/src/clerk/fingerprint/use-fingerprint.ts +7 -7
- package/src/clerk/signin-with-fingerprint-client.tsx +7 -7
- package/src/clerk/signup-button-with-fingerprint-client.tsx +7 -5
- package/src/clerk/signup-with-fingerprint-client.tsx +7 -7
- package/src/fuma/base/custom-home-layout.tsx +4 -4
- package/src/fuma/heavy/mermaid.tsx +1 -1
- package/src/fuma/mdx/fuma-github-info.tsx +3 -8
- package/src/fuma/site-x.tsx +0 -1
- package/src/main/calendar/calendar-date-range-input.tsx +1 -1
- package/src/main/credit/credit-overview-nav-client.tsx +95 -0
- package/src/main/credit/index.ts +5 -0
- package/src/main/credit/types.ts +8 -8
- package/src/main/gallery/gallery-mobile-swiper.tsx +0 -1
- package/src/main/gallery/gallery-server.tsx +2 -2
- package/src/main/money-price/index.ts +2 -0
- package/src/main/money-price/money-price-button.tsx +10 -10
- package/src/main/money-price/money-price-config-util.ts +49 -49
- package/src/main/money-price/money-price-interactive.tsx +40 -20
- package/src/main/money-price/money-price-types.ts +21 -14
- package/src/main/money-price/server.ts +2 -0
- package/src/main/pill-select/x-pill-select.tsx +2 -2
- package/src/main/server.ts +3 -1
- package/src/styles/third-ui.css +8 -0
|
@@ -2,30 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Money Price Configuration
|
|
5
|
-
*
|
|
5
|
+
* Pricing component configuration.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Get the currently active payment provider configuration.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
10
|
+
* Security design:
|
|
11
|
+
* - The utility layer extracts the active provider configuration from the config.
|
|
12
|
+
* - Only the extracted result is returned; the full config structure is not exposed.
|
|
13
|
+
* - Application-level wrappers hide the config object from callers.
|
|
14
14
|
*
|
|
15
|
-
* @param config - MoneyPriceConfig
|
|
16
|
-
* @returns
|
|
15
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
16
|
+
* @returns The currently active payment provider configuration.
|
|
17
17
|
*/
|
|
18
18
|
function getActiveProviderConfigUtil(config) {
|
|
19
19
|
const provider = config.activeProvider;
|
|
20
20
|
return config.paymentProviders[provider];
|
|
21
21
|
}
|
|
22
|
-
//
|
|
22
|
+
// Helper: get pricing information for a specific product.
|
|
23
23
|
function getProductPricing(productKey, billingType, provider, config) {
|
|
24
24
|
const providerConfig = config.paymentProviders[provider];
|
|
25
|
-
//
|
|
25
|
+
// For one-time billing, try to resolve pricing from credit packs.
|
|
26
26
|
if (billingType === 'onetime') {
|
|
27
27
|
const creditPacks = providerConfig.creditPackProducts;
|
|
28
|
-
//
|
|
28
|
+
// Use the same product key directly: F1 -> F1, P2 -> P2, U3 -> U3.
|
|
29
29
|
if (creditPacks && creditPacks[productKey]) {
|
|
30
30
|
const pack = creditPacks[productKey];
|
|
31
31
|
return {
|
|
@@ -36,33 +36,33 @@ function getProductPricing(productKey, billingType, provider, config) {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
//
|
|
39
|
+
// Otherwise resolve pricing from subscription products.
|
|
40
40
|
const products = providerConfig.subscriptionProducts || providerConfig.products;
|
|
41
41
|
if (products && products[productKey] && products[productKey].plans[billingType]) {
|
|
42
42
|
return products[productKey].plans[billingType];
|
|
43
43
|
}
|
|
44
44
|
throw new Error(`Product pricing not found for ${productKey} ${billingType}`);
|
|
45
45
|
}
|
|
46
|
-
// ============
|
|
46
|
+
// ============ Safe utility functions: accept only simple mapping inputs and do not expose config internals ============
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
48
|
+
* Get the credit amount for a price ID.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
* -
|
|
52
|
-
* -
|
|
53
|
-
* -
|
|
50
|
+
* Security design:
|
|
51
|
+
* - The utility layer parses the config and extracts only the required data.
|
|
52
|
+
* - Only the query result is returned; the full config structure is not exposed.
|
|
53
|
+
* - Application-level wrappers hide the config object from callers.
|
|
54
54
|
*
|
|
55
|
-
* @param priceId -
|
|
56
|
-
* @param config - MoneyPriceConfig
|
|
57
|
-
* @returns
|
|
55
|
+
* @param priceId - Price ID to query.
|
|
56
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
57
|
+
* @returns The matching credit amount, or null.
|
|
58
58
|
*/
|
|
59
59
|
function getCreditsFromPriceIdUtil(priceId, config) {
|
|
60
60
|
if (!priceId) {
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
|
-
//
|
|
63
|
+
// Iterate through all payment providers.
|
|
64
64
|
for (const provider of Object.values(config.paymentProviders)) {
|
|
65
|
-
//
|
|
65
|
+
// Iterate through subscription products.
|
|
66
66
|
const subscriptionProducts = (provider.subscriptionProducts || provider.products);
|
|
67
67
|
if (subscriptionProducts) {
|
|
68
68
|
for (const product of Object.values(subscriptionProducts)) {
|
|
@@ -76,7 +76,7 @@ function getCreditsFromPriceIdUtil(priceId, config) {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
//
|
|
79
|
+
// Iterate through credit pack products.
|
|
80
80
|
const creditPacks = provider.creditPackProducts;
|
|
81
81
|
if (creditPacks) {
|
|
82
82
|
for (const pack of Object.values(creditPacks)) {
|
|
@@ -90,46 +90,46 @@ function getCreditsFromPriceIdUtil(priceId, config) {
|
|
|
90
90
|
return null;
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* Get price configuration by query parameters.
|
|
94
94
|
*
|
|
95
|
-
*
|
|
96
|
-
* 1.
|
|
97
|
-
* 2.
|
|
98
|
-
* 3.
|
|
95
|
+
* Supported query modes:
|
|
96
|
+
* 1. Query directly by priceId.
|
|
97
|
+
* 2. Query by plan and billingType.
|
|
98
|
+
* 3. Query by plan.
|
|
99
99
|
*
|
|
100
|
-
*
|
|
101
|
-
* -
|
|
102
|
-
* -
|
|
103
|
-
* -
|
|
100
|
+
* Security design:
|
|
101
|
+
* - The utility layer parses the config and extracts only matching data.
|
|
102
|
+
* - Only the query result is returned; the full config structure is not exposed.
|
|
103
|
+
* - Application-level wrappers hide the config object from callers.
|
|
104
104
|
*
|
|
105
|
-
* @param priceId -
|
|
106
|
-
* @param plan -
|
|
107
|
-
* @param billingType -
|
|
108
|
-
* @param config - MoneyPriceConfig
|
|
109
|
-
* @returns
|
|
105
|
+
* @param priceId - Optional price ID to query.
|
|
106
|
+
* @param plan - Optional plan name, such as 'P2' or 'U3'.
|
|
107
|
+
* @param billingType - Optional billing type, such as 'monthly' or 'yearly'.
|
|
108
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
109
|
+
* @returns The matching price config with derived metadata: priceName, description, and interval.
|
|
110
110
|
*/
|
|
111
111
|
function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
112
|
-
//
|
|
112
|
+
// Iterate through all payment providers.
|
|
113
113
|
for (const provider of Object.values(config.paymentProviders)) {
|
|
114
|
-
//
|
|
114
|
+
// Iterate through subscription products.
|
|
115
115
|
const subscriptionProducts = (provider.subscriptionProducts || provider.products);
|
|
116
116
|
if (subscriptionProducts) {
|
|
117
117
|
for (const [productKey, product] of Object.entries(subscriptionProducts)) {
|
|
118
118
|
if (product.plans) {
|
|
119
119
|
for (const [billingKey, planConfig] of Object.entries(product.plans)) {
|
|
120
120
|
const plan_config = planConfig;
|
|
121
|
-
//
|
|
122
|
-
// 1.
|
|
121
|
+
// Matching order by priority.
|
|
122
|
+
// 1. Exact priceId match with highest priority.
|
|
123
123
|
if (priceId && plan_config.priceId === priceId) {
|
|
124
124
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
125
125
|
}
|
|
126
|
-
// 2.
|
|
126
|
+
// 2. Match by both plan and billingType.
|
|
127
127
|
if (!priceId && plan && billingType) {
|
|
128
128
|
if (productKey === plan && billingKey === billingType) {
|
|
129
129
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
|
-
// 3.
|
|
132
|
+
// 3. Match by plan when billingType is empty.
|
|
133
133
|
if (!priceId && !billingType && plan && productKey === plan) {
|
|
134
134
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
135
135
|
}
|
|
@@ -137,12 +137,12 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
//
|
|
140
|
+
// Iterate through credit pack products.
|
|
141
141
|
const creditPacks = provider.creditPackProducts;
|
|
142
142
|
if (creditPacks) {
|
|
143
143
|
for (const [packKey, pack] of Object.entries(creditPacks)) {
|
|
144
144
|
const pack_typed = pack;
|
|
145
|
-
//
|
|
145
|
+
// Credit pack match.
|
|
146
146
|
if (priceId && pack_typed.priceId === priceId) {
|
|
147
147
|
return {
|
|
148
148
|
priceId: pack_typed.priceId,
|
|
@@ -154,7 +154,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
154
154
|
interval: 'onetime',
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
|
-
//
|
|
157
|
+
// Match by plan and one-time billing.
|
|
158
158
|
if (!priceId && plan && billingType === 'onetime') {
|
|
159
159
|
if (packKey === plan) {
|
|
160
160
|
return {
|
|
@@ -168,7 +168,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
168
168
|
};
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
//
|
|
171
|
+
// Match by plan; also resolves the first credit pack when billingType is empty.
|
|
172
172
|
if (!priceId && !billingType && plan && packKey === plan) {
|
|
173
173
|
return {
|
|
174
174
|
priceId: pack_typed.priceId,
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Money Price Configuration
|
|
3
|
-
*
|
|
3
|
+
* Pricing component configuration.
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Get the currently active payment provider configuration.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
8
|
+
* Security design:
|
|
9
|
+
* - The utility layer extracts the active provider configuration from the config.
|
|
10
|
+
* - Only the extracted result is returned; the full config structure is not exposed.
|
|
11
|
+
* - Application-level wrappers hide the config object from callers.
|
|
12
12
|
*
|
|
13
|
-
* @param config - MoneyPriceConfig
|
|
14
|
-
* @returns
|
|
13
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
14
|
+
* @returns The currently active payment provider configuration.
|
|
15
15
|
*/
|
|
16
16
|
function getActiveProviderConfigUtil(config) {
|
|
17
17
|
const provider = config.activeProvider;
|
|
18
18
|
return config.paymentProviders[provider];
|
|
19
19
|
}
|
|
20
|
-
//
|
|
20
|
+
// Helper: get pricing information for a specific product.
|
|
21
21
|
function getProductPricing(productKey, billingType, provider, config) {
|
|
22
22
|
const providerConfig = config.paymentProviders[provider];
|
|
23
|
-
//
|
|
23
|
+
// For one-time billing, try to resolve pricing from credit packs.
|
|
24
24
|
if (billingType === 'onetime') {
|
|
25
25
|
const creditPacks = providerConfig.creditPackProducts;
|
|
26
|
-
//
|
|
26
|
+
// Use the same product key directly: F1 -> F1, P2 -> P2, U3 -> U3.
|
|
27
27
|
if (creditPacks && creditPacks[productKey]) {
|
|
28
28
|
const pack = creditPacks[productKey];
|
|
29
29
|
return {
|
|
@@ -34,33 +34,33 @@ function getProductPricing(productKey, billingType, provider, config) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
//
|
|
37
|
+
// Otherwise resolve pricing from subscription products.
|
|
38
38
|
const products = providerConfig.subscriptionProducts || providerConfig.products;
|
|
39
39
|
if (products && products[productKey] && products[productKey].plans[billingType]) {
|
|
40
40
|
return products[productKey].plans[billingType];
|
|
41
41
|
}
|
|
42
42
|
throw new Error(`Product pricing not found for ${productKey} ${billingType}`);
|
|
43
43
|
}
|
|
44
|
-
// ============
|
|
44
|
+
// ============ Safe utility functions: accept only simple mapping inputs and do not expose config internals ============
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
46
|
+
* Get the credit amount for a price ID.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
* -
|
|
50
|
-
* -
|
|
51
|
-
* -
|
|
48
|
+
* Security design:
|
|
49
|
+
* - The utility layer parses the config and extracts only the required data.
|
|
50
|
+
* - Only the query result is returned; the full config structure is not exposed.
|
|
51
|
+
* - Application-level wrappers hide the config object from callers.
|
|
52
52
|
*
|
|
53
|
-
* @param priceId -
|
|
54
|
-
* @param config - MoneyPriceConfig
|
|
55
|
-
* @returns
|
|
53
|
+
* @param priceId - Price ID to query.
|
|
54
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
55
|
+
* @returns The matching credit amount, or null.
|
|
56
56
|
*/
|
|
57
57
|
function getCreditsFromPriceIdUtil(priceId, config) {
|
|
58
58
|
if (!priceId) {
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
|
-
//
|
|
61
|
+
// Iterate through all payment providers.
|
|
62
62
|
for (const provider of Object.values(config.paymentProviders)) {
|
|
63
|
-
//
|
|
63
|
+
// Iterate through subscription products.
|
|
64
64
|
const subscriptionProducts = (provider.subscriptionProducts || provider.products);
|
|
65
65
|
if (subscriptionProducts) {
|
|
66
66
|
for (const product of Object.values(subscriptionProducts)) {
|
|
@@ -74,7 +74,7 @@ function getCreditsFromPriceIdUtil(priceId, config) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
//
|
|
77
|
+
// Iterate through credit pack products.
|
|
78
78
|
const creditPacks = provider.creditPackProducts;
|
|
79
79
|
if (creditPacks) {
|
|
80
80
|
for (const pack of Object.values(creditPacks)) {
|
|
@@ -88,46 +88,46 @@ function getCreditsFromPriceIdUtil(priceId, config) {
|
|
|
88
88
|
return null;
|
|
89
89
|
}
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
91
|
+
* Get price configuration by query parameters.
|
|
92
92
|
*
|
|
93
|
-
*
|
|
94
|
-
* 1.
|
|
95
|
-
* 2.
|
|
96
|
-
* 3.
|
|
93
|
+
* Supported query modes:
|
|
94
|
+
* 1. Query directly by priceId.
|
|
95
|
+
* 2. Query by plan and billingType.
|
|
96
|
+
* 3. Query by plan.
|
|
97
97
|
*
|
|
98
|
-
*
|
|
99
|
-
* -
|
|
100
|
-
* -
|
|
101
|
-
* -
|
|
98
|
+
* Security design:
|
|
99
|
+
* - The utility layer parses the config and extracts only matching data.
|
|
100
|
+
* - Only the query result is returned; the full config structure is not exposed.
|
|
101
|
+
* - Application-level wrappers hide the config object from callers.
|
|
102
102
|
*
|
|
103
|
-
* @param priceId -
|
|
104
|
-
* @param plan -
|
|
105
|
-
* @param billingType -
|
|
106
|
-
* @param config - MoneyPriceConfig
|
|
107
|
-
* @returns
|
|
103
|
+
* @param priceId - Optional price ID to query.
|
|
104
|
+
* @param plan - Optional plan name, such as 'P2' or 'U3'.
|
|
105
|
+
* @param billingType - Optional billing type, such as 'monthly' or 'yearly'.
|
|
106
|
+
* @param config - MoneyPriceConfig object provided by the application layer.
|
|
107
|
+
* @returns The matching price config with derived metadata: priceName, description, and interval.
|
|
108
108
|
*/
|
|
109
109
|
function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
110
|
-
//
|
|
110
|
+
// Iterate through all payment providers.
|
|
111
111
|
for (const provider of Object.values(config.paymentProviders)) {
|
|
112
|
-
//
|
|
112
|
+
// Iterate through subscription products.
|
|
113
113
|
const subscriptionProducts = (provider.subscriptionProducts || provider.products);
|
|
114
114
|
if (subscriptionProducts) {
|
|
115
115
|
for (const [productKey, product] of Object.entries(subscriptionProducts)) {
|
|
116
116
|
if (product.plans) {
|
|
117
117
|
for (const [billingKey, planConfig] of Object.entries(product.plans)) {
|
|
118
118
|
const plan_config = planConfig;
|
|
119
|
-
//
|
|
120
|
-
// 1.
|
|
119
|
+
// Matching order by priority.
|
|
120
|
+
// 1. Exact priceId match with highest priority.
|
|
121
121
|
if (priceId && plan_config.priceId === priceId) {
|
|
122
122
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
123
123
|
}
|
|
124
|
-
// 2.
|
|
124
|
+
// 2. Match by both plan and billingType.
|
|
125
125
|
if (!priceId && plan && billingType) {
|
|
126
126
|
if (productKey === plan && billingKey === billingType) {
|
|
127
127
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
// 3.
|
|
130
|
+
// 3. Match by plan when billingType is empty.
|
|
131
131
|
if (!priceId && !billingType && plan && productKey === plan) {
|
|
132
132
|
return Object.assign(Object.assign({}, plan_config), { priceName: `${productKey} ${billingKey}`, description: `${productKey} plan - ${billingKey} billing`, interval: billingKey === 'yearly' ? 'year' : 'month' });
|
|
133
133
|
}
|
|
@@ -135,12 +135,12 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
//
|
|
138
|
+
// Iterate through credit pack products.
|
|
139
139
|
const creditPacks = provider.creditPackProducts;
|
|
140
140
|
if (creditPacks) {
|
|
141
141
|
for (const [packKey, pack] of Object.entries(creditPacks)) {
|
|
142
142
|
const pack_typed = pack;
|
|
143
|
-
//
|
|
143
|
+
// Credit pack match.
|
|
144
144
|
if (priceId && pack_typed.priceId === priceId) {
|
|
145
145
|
return {
|
|
146
146
|
priceId: pack_typed.priceId,
|
|
@@ -152,7 +152,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
152
152
|
interval: 'onetime',
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
|
-
//
|
|
155
|
+
// Match by plan and one-time billing.
|
|
156
156
|
if (!priceId && plan && billingType === 'onetime') {
|
|
157
157
|
if (packKey === plan) {
|
|
158
158
|
return {
|
|
@@ -166,7 +166,7 @@ function getPriceConfigUtil(priceId, plan, billingType, config) {
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
//
|
|
169
|
+
// Match by plan; also resolves the first credit pack when billingType is empty.
|
|
170
170
|
if (!priceId && !billingType && plan && packKey === plan) {
|
|
171
171
|
return {
|
|
172
172
|
priceId: pack_typed.priceId,
|
|
@@ -12,6 +12,9 @@ var moneyPriceConfigUtil = require('./money-price-config-util.js');
|
|
|
12
12
|
var moneyPriceTypes = require('./money-price-types.js');
|
|
13
13
|
var customerPortal = require('./customer-portal.js');
|
|
14
14
|
var lib = require('@windrun-huaiin/base-ui/lib');
|
|
15
|
+
var animeBeamFrame = require('../anime/anime-beam-frame.js');
|
|
16
|
+
require('animejs');
|
|
17
|
+
require('../anime/anime-404-page.js');
|
|
15
18
|
|
|
16
19
|
const PLAN_KEYS = ['F1', 'P2', 'U3'];
|
|
17
20
|
function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPortalApiEndpoint, enableClerkModal = false, enabledBillingTypes, enableSubscriptionUpgrade = true, initialBillingType, disableAutoDetectBilling = false, initUserContext, isInitLoading = false, }) {
|
|
@@ -21,11 +24,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
21
24
|
const providerConfig = React.useMemo(() => moneyPriceConfigUtil.getActiveProviderConfigUtil(config), [config]);
|
|
22
25
|
const billingOptions = React.useMemo(() => {
|
|
23
26
|
const options = data.billingSwitch.options;
|
|
24
|
-
//
|
|
27
|
+
// If enabledBillingTypes is configured, show only those billing types.
|
|
25
28
|
if (enabledBillingTypes === null || enabledBillingTypes === void 0 ? void 0 : enabledBillingTypes.length) {
|
|
26
29
|
return options.filter(option => enabledBillingTypes.includes(option.key));
|
|
27
30
|
}
|
|
28
|
-
//
|
|
31
|
+
// Otherwise show all configured options.
|
|
29
32
|
return options;
|
|
30
33
|
}, [data.billingSwitch.options, enabledBillingTypes]);
|
|
31
34
|
const billingOptionMap = React.useMemo(() => {
|
|
@@ -37,11 +40,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
37
40
|
const defaultBilling = React.useMemo(() => {
|
|
38
41
|
var _a;
|
|
39
42
|
const defaultKey = data.billingSwitch.defaultKey;
|
|
40
|
-
//
|
|
43
|
+
// Use the default value when it is available.
|
|
41
44
|
if (billingOptions.some(opt => opt.key === defaultKey)) {
|
|
42
45
|
return defaultKey;
|
|
43
46
|
}
|
|
44
|
-
//
|
|
47
|
+
// Otherwise use the first available option.
|
|
45
48
|
return ((_a = billingOptions[0]) === null || _a === void 0 ? void 0 : _a.key) || 'monthly';
|
|
46
49
|
}, [data.billingSwitch.defaultKey, billingOptions]);
|
|
47
50
|
const resolvedInitialBilling = React.useMemo(() => {
|
|
@@ -53,18 +56,18 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
53
56
|
}, [initialBillingType, billingOptions, defaultBilling]);
|
|
54
57
|
const priceIdsByCycle = React.useMemo(() => {
|
|
55
58
|
const priceIds = {};
|
|
56
|
-
//
|
|
59
|
+
// Build a price ID list for each available billing type.
|
|
57
60
|
billingOptions.forEach(option => {
|
|
58
61
|
priceIds[option.key] = [];
|
|
59
62
|
if (option.key === 'onetime') {
|
|
60
|
-
//
|
|
63
|
+
// Handle credit pack products.
|
|
61
64
|
const creditPacks = providerConfig.creditPackProducts || {};
|
|
62
65
|
Object.values(creditPacks).forEach((pack) => {
|
|
63
66
|
priceIds[option.key].push(pack.priceId);
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
69
|
else {
|
|
67
|
-
//
|
|
70
|
+
// Handle subscription products.
|
|
68
71
|
const products = providerConfig.subscriptionProducts || providerConfig.products || {};
|
|
69
72
|
PLAN_KEYS.forEach(planKey => {
|
|
70
73
|
const product = products[planKey];
|
|
@@ -263,7 +266,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
263
266
|
userContext,
|
|
264
267
|
enableSubscriptionUpgrade
|
|
265
268
|
]);
|
|
266
|
-
//
|
|
269
|
+
// Select visible plans dynamically based on the current billing type.
|
|
267
270
|
const currentPlans = React.useMemo(() => {
|
|
268
271
|
if (billingType === 'onetime') {
|
|
269
272
|
return data.creditsPlans || [];
|
|
@@ -288,11 +291,11 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
288
291
|
const discountBadgeText = React.useMemo(() => {
|
|
289
292
|
if (!(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText))
|
|
290
293
|
return null;
|
|
291
|
-
//
|
|
294
|
+
// In one-time mode, show discountText directly without relying on discountPercent.
|
|
292
295
|
if (billingType === 'onetime') {
|
|
293
296
|
return selectedBillingOption.discountText;
|
|
294
297
|
}
|
|
295
|
-
//
|
|
298
|
+
// In subscription mode, find discountPercent and interpolate it.
|
|
296
299
|
let discountPercent = null;
|
|
297
300
|
const products = providerConfig.subscriptionProducts || providerConfig.products || {};
|
|
298
301
|
PLAN_KEYS.forEach(planKey => {
|
|
@@ -306,7 +309,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
306
309
|
return null;
|
|
307
310
|
return selectedBillingOption.discountText.replace('{percent}', String(discountPercent));
|
|
308
311
|
}, [selectedBillingOption, providerConfig, billingType]);
|
|
309
|
-
//
|
|
312
|
+
// Configure the mobile floating style for BillingTypeButton.
|
|
310
313
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "flex justify-center mb-6 max-md:sticky max-md:top-30 max-md:z-30 max-md:py-2 max-md:bg-transparent", children: jsxRuntime.jsx("div", { className: "inline-flex bg-white dark:bg-gray-900 border border-gray-300 dark:border-gray-700 rounded-full px-2 py-2 sm:px-3 sm:py-3 max-md:w-full max-md:max-w-[340px] max-md:mx-auto shadow-sm", "data-billing-switch": true, children: billingOptions.map(option => {
|
|
311
314
|
const isActive = option.key === billingType;
|
|
312
315
|
const buttonClasses = isActive
|
|
@@ -315,6 +318,7 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
315
318
|
const showBadge = option.key === billingType && !!discountBadgeText;
|
|
316
319
|
return (jsxRuntime.jsxs("div", { className: "relative flex items-center justify-center mx-1", children: [showBadge && (jsxRuntime.jsx("span", { className: "absolute z-10 left-1/2 -translate-x-1/2 -top-3 sm:-top-4 translate-y-[-50%] px-3 py-0.5 text-[0.625rem] sm:text-xs rounded-md bg-yellow-100 text-yellow-800 font-semibold shadow-sm whitespace-nowrap", children: discountBadgeText })), jsxRuntime.jsx("button", { className: utils.cn('text-sm md:text-base font-medium transition relative text-center z-10 px-2 sm:px-4 py-2 min-w-[100px] sm:min-w-[120px]', buttonClasses), type: "button", "data-billing-button": option.key, onClick: () => setUserSelectedBillingType(option.key), children: option.name })] }, option.key));
|
|
317
320
|
}) }) }), jsxRuntime.jsx("div", { className: "w-full", children: jsxRuntime.jsx("div", { className: "flex flex-wrap justify-center gap-5 md:gap-6 xl:gap-8 w-full max-w-6xl mx-auto", children: currentPlans.map((plan) => {
|
|
321
|
+
var _a, _b;
|
|
318
322
|
const planKey = plan.key;
|
|
319
323
|
if (!PLAN_KEYS.includes(planKey)) {
|
|
320
324
|
console.warn(`Unknown plan key "${plan.key}" detected in pricing plans`);
|
|
@@ -323,13 +327,21 @@ function MoneyPriceInteractive({ data, config, checkoutApiEndpoint, customerPort
|
|
|
323
327
|
const pricing = getPricingForPlan(planKey);
|
|
324
328
|
const showBillingSubtitle = plan.showBillingSubTitle !== false;
|
|
325
329
|
const hasDiscount = !!pricing.discountPercent && !!pricing.originalAmount;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
const hasStrictDiffAnime = Object.prototype.hasOwnProperty.call((_a = plan.strictDiffAnime) !== null && _a !== void 0 ? _a : {}, billingType);
|
|
331
|
+
const animeTone = hasStrictDiffAnime
|
|
332
|
+
? (_b = plan.strictDiffAnime) === null || _b === void 0 ? void 0 : _b[billingType]
|
|
333
|
+
: plan.animeTone;
|
|
334
|
+
const hasAnimeTone = !!animeTone;
|
|
335
|
+
return (jsxRuntime.jsx(animeBeamFrame.AnimeBeamFrame, { active: false, interactive: hasAnimeTone, tone: animeTone !== null && animeTone !== void 0 ? animeTone : 'theme', radius: 16, className: utils.cn('h-full w-[85vw] max-w-[360px]', 'md:w-[clamp(280px,32vw,360px)] md:max-w-[360px] md:shrink-0'), children: jsxRuntime.jsxs("div", { "data-price-plan": planKey, className: utils.cn('flex flex-col bg-white dark:bg-gray-800/60 rounded-2xl border border-gray-300 dark:border-[#7c3aed40] transition p-5 md:p-8 h-full shadow-sm dark:shadow-none', !hasAnimeTone && [
|
|
336
|
+
'hover:border-current',
|
|
337
|
+
'focus-within:border-current',
|
|
338
|
+
lib.themeIconColor,
|
|
339
|
+
]), style: { minHeight: maxFeaturesCount * (isTouchDevice ? 86 : 100) }, children: [jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [jsxRuntime.jsx("span", { className: "text-lg md:text-xl font-bold text-gray-900 dark:text-gray-100", children: plan.title }), plan.titleTags && plan.titleTags.map((tag, i) => (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs rounded bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 font-semibold align-middle", children: tag }, i)))] }), jsxRuntime.jsxs("div", { className: "flex flex-col items-start w-full", "data-price-container": planKey, children: [jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [jsxRuntime.jsx("span", { className: "text-3xl md:text-4xl font-extrabold text-gray-900 dark:text-gray-100", "data-price-value": planKey, children: pricing.amount === 0 ? 'Free' : `${data.currency}${pricing.amount}` }), pricing.amount > 0 && (jsxRuntime.jsx("span", { className: "text-base md:text-lg text-gray-700 dark:text-gray-300 font-medium mb-1", "data-price-unit": planKey, children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.unit) || '/month' }))] }), jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-2 min-h-[28px] mt-1", children: [hasDiscount && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("span", { className: "text-sm md:text-base text-gray-400 line-through", "data-price-original": planKey, children: [data.currency, pricing.originalAmount] }), (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.discountText) && (jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", "data-price-discount": planKey, children: selectedBillingOption.discountText.replace('{percent}', String(pricing.discountPercent)) }))] })), jsxRuntime.jsx("div", { className: utils.cn('flex items-center gap-2 text-[11px] md:text-xs', !showBillingSubtitle && 'opacity-0 select-none'), "data-price-subtitle": planKey, children: showBillingSubtitle && billingType === 'onetime' ? (
|
|
340
|
+
// Special one-time mode rendering: plain text plus styled product subtitle.
|
|
341
|
+
jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: selectedBillingOption.subTitle })), plan.subtitle && (jsxRuntime.jsxs("span", { className: "px-2 py-0.5 text-[11px] md:text-xs rounded bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 font-semibold align-middle", children: ["+", plan.subtitle] }))] })) : (
|
|
342
|
+
// Keep the original rendering for other modes.
|
|
343
|
+
showBillingSubtitle && (jsxRuntime.jsx("span", { className: "text-[11px] md:text-xs text-gray-700 dark:text-gray-300 font-medium", children: (selectedBillingOption === null || selectedBillingOption === void 0 ? void 0 : selectedBillingOption.subTitle) || '' }))) })] })] }), jsxRuntime.jsx("ul", { className: "flex-1 mb-6 mt-4 text-xs md:text-sm leading-5", children: getFeatureRows(plan).map((feature, i) => (jsxRuntime.jsxs("li", { className: "flex items-start gap-2 mb-2 min-h-[24px] md:min-h-[28px]", "data-feature-item": `${planKey}-${i}`, children: [feature ? (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-200 mr-1", children: feature.icon ? jsxRuntime.jsx("span", { children: feature.icon }) : jsxRuntime.jsx("span", { className: "font-bold", children: "\u2713" }) })) : (jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-5 h-5 rounded-full mr-1", children: "\u00A0" })), feature && feature.tag && (jsxRuntime.jsx("span", { className: "px-1 py-0.5 text-[6px] rounded bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 font-semibold align-middle", children: feature.tag })), feature ? (jsxRuntime.jsxs("div", { className: "flex-1 text-gray-800 dark:text-gray-200", children: [jsxRuntime.jsx("span", { children: feature.description }), feature.tooltip && (jsxRuntime.jsx("span", { className: "block text-[11px] text-gray-500 dark:text-gray-400 mt-1", children: feature.tooltip }))] })) : (jsxRuntime.jsx("span", { children: "\u00A0" }))] }, i))) }), jsxRuntime.jsx("div", { className: "flex-1" }), jsxRuntime.jsx(moneyPriceButton.MoneyPriceButton, { planKey: planKey, userContext: userContext, billingType: billingType, onAuth: handleAuth, onAction: handleAction, texts: data.buttonTexts, isProcessing: (processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.plan) === planKey &&
|
|
344
|
+
(processingTarget === null || processingTarget === void 0 ? void 0 : processingTarget.billing) === billingType, isAnyProcessing: !!processingTarget, isInitLoading: isInitLoading, enableSubscriptionUpgrade: enableSubscriptionUpgrade })] }) }, `${billingType}-${plan.key}-${animeTone !== null && animeTone !== void 0 ? animeTone : 'none'}`));
|
|
333
345
|
}) }) })] }));
|
|
334
346
|
}
|
|
335
347
|
|