Skip to content

Commit e99a32b

Browse files
mrcfpslefarcenPerishCodeHanYuanxiclaude
authored
hotfix: credit pricing page (#1722)
feat: enhance EndMessage component for marketplace engagement - Introduced a new EndMessage component to encourage users to visit the marketplace instead of Discord. - Utilized React.memo for performance optimization and wrapped the button click handler with useCallback. - Updated translations for the new EndMessage content in both English and Chinese. - Ensured adherence to Tailwind CSS for consistent styling across the component. Co-authored-by: Marc Chan <[email protected]> Co-authored-by: lefarcen <[email protected]> Co-authored-by: PerishFire <[email protected]> Co-authored-by: HanYuanxi <[email protected]> Co-authored-by: Claude <[email protected]> Co-authored-by: a1chzt <[email protected]> Co-authored-by: Achieve <[email protected]> Co-authored-by: Sophia <[email protected]> Co-authored-by: William Liu <[email protected]>
2 parents 7b2757a + 71e08fd commit e99a32b

File tree

4 files changed

+107
-72
lines changed

4 files changed

+107
-72
lines changed

packages/ai-workspace-common/src/components/canvas-template/template-list.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { TemplateCardSkeleton } from './template-card-skeleton';
1010
import cn from 'classnames';
1111

1212
// Marketplace link
13-
const MARKETPLACE_LINK = `${window.location.origin}`;
13+
const MARKETPLACE_LINK = `${window.location.origin}/marketplace`;
1414

1515
// Custom EndMessage component for template list
1616
const EndMessage = memo(() => {

packages/ai-workspace-common/src/components/settings/subscribe-modal/priceContent.tsx

Lines changed: 89 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { logEvent } from '@refly/telemetry-web';
66
// styles
77
import './index.scss';
88
import { Spin } from '@refly-packages/ai-workspace-common/components/common/spin';
9-
import { Checked, Wait } from 'refly-icons';
9+
import { Checked, Subscription, Wait } from 'refly-icons';
1010
import { IconLightning01 } from '@refly-packages/ai-workspace-common/components/common/icon';
1111
import getClient from '@refly-packages/ai-workspace-common/requests/proxiedRequest';
1212
import {
@@ -66,8 +66,8 @@ const PriceOption = memo(({ type, isSelected, price, yearlyTotal, onSelect }: Pr
6666
relative flex-1 p-4 rounded-xl cursor-pointer transition-all duration-200
6767
${
6868
isSelected
69-
? 'border-2 border-[#0E9F77] bg-white'
70-
: 'border-[1.5px] border-gray-200 bg-[#FAFAFA] hover:border-[#0E9F77]'
69+
? 'border-2 !border-solid !border-black bg-white'
70+
: 'border-2 !border-solid !border-gray-200 bg-[#FAFAFA] hover:border-[#0E9F77]'
7171
}
7272
`}
7373
onClick={handleClick}
@@ -77,7 +77,10 @@ const PriceOption = memo(({ type, isSelected, price, yearlyTotal, onSelect }: Pr
7777
{type === 'monthly' ? t('subscription.monthly') : t('subscription.yearly')}
7878
</span>
7979
{type === 'yearly' && (
80-
<Tag className="!m-0 !px-2 !py-0.5 !text-xs !font-medium !rounded" color="orange">
80+
<Tag
81+
className="!m-0 !px-2 !py-0.5 !text-xs !font-medium !rounded-full !border-0"
82+
color="orange"
83+
>
8184
{t('subscription.save20')}
8285
</Tag>
8386
)}
@@ -105,62 +108,79 @@ interface FeatureItemProps {
105108
feature: Feature;
106109
isEnterprise?: boolean;
107110
isLast?: boolean;
111+
planType?: string;
112+
featureIndex?: number;
108113
}
109114

110-
const FeatureItem = memo(({ feature, isEnterprise, isLast }: FeatureItemProps) => {
111-
const parts = feature.name.split('\n');
112-
const name = parts[0];
113-
const description = parts.length > 1 ? parts.slice(1).join('\n') : null;
115+
const FeatureItem = memo(
116+
({ feature, isEnterprise, isLast, planType, featureIndex }: FeatureItemProps) => {
117+
const parts = feature.name.split('\n');
118+
const name = parts[0];
119+
const description = parts.length > 1 ? parts.slice(1).join('\n') : null;
120+
121+
// For plus plan, make the 2nd and 3rd description green
122+
const isGreenDescription =
123+
planType === 'plus' &&
124+
featureIndex !== undefined &&
125+
(featureIndex === 1 || featureIndex === 2);
126+
127+
// Handle pointFreeTools type with special display logic
128+
if (feature.type === 'pointFreeTools' && feature.items && feature.items.length > 0) {
129+
return (
130+
<div className="flex flex-col gap-2">
131+
{/* Header with check icon */}
132+
<div className="flex items-start gap-3">
133+
<div className="flex-shrink-0 mt-0.5">
134+
<Checked size={16} color="#0E9F77" />
135+
</div>
136+
<span className="text-sm leading-5 text-gray-900 font-semibold">{name}</span>
137+
</div>
138+
{/* Sub-items list */}
139+
<div className="ml-7 p-3 rounded-lg bg-transparent flex flex-col gap-2">
140+
{feature.items.map((item, index) => (
141+
<div key={index} className="flex items-center justify-between">
142+
<div className="flex items-center gap-2">
143+
<span className="w-1.5 h-1.5 rounded-full bg-[#0E9F77] flex-shrink-0" />
144+
<span className="text-sm text-gray-700">{item}</span>
145+
</div>
146+
{feature.duration && (
147+
<span className="text-xs font-medium text-gray-500 bg-white px-2 py-0.5 rounded">
148+
{feature.duration}
149+
</span>
150+
)}
151+
</div>
152+
))}
153+
</div>
154+
</div>
155+
);
156+
}
114157

115-
// Handle pointFreeTools type with special display logic
116-
if (feature.type === 'pointFreeTools' && feature.items && feature.items.length > 0) {
117158
return (
118-
<div className="flex flex-col gap-2">
119-
{/* Header with check icon */}
120-
<div className="flex items-start gap-3">
121-
<div className="flex-shrink-0 mt-0.5">
159+
<div className="flex items-start gap-3">
160+
<div className="flex-shrink-0 mt-0.5">
161+
{isEnterprise && isLast ? (
162+
<Wait size={16} color="rgba(28, 31, 35, 0.6)" />
163+
) : (
122164
<Checked size={16} color="#0E9F77" />
123-
</div>
124-
<span className="text-sm leading-5 text-gray-900 font-semibold">{name}</span>
165+
)}
125166
</div>
126-
{/* Sub-items list */}
127-
<div className="ml-7 p-3 rounded-lg bg-transparent flex flex-col gap-2">
128-
{feature.items.map((item, index) => (
129-
<div key={index} className="flex items-center justify-between">
130-
<div className="flex items-center gap-2">
131-
<span className="w-1.5 h-1.5 rounded-full bg-[#0E9F77] flex-shrink-0" />
132-
<span className="text-sm text-gray-700">{item}</span>
133-
</div>
134-
{feature.duration && (
135-
<span className="text-xs font-medium text-gray-500 bg-white px-2 py-0.5 rounded">
136-
{feature.duration}
137-
</span>
138-
)}
139-
</div>
140-
))}
167+
<div className="flex flex-col gap-0.5">
168+
<span className="text-sm leading-5 text-gray-900 font-semibold">
169+
{name}
170+
{description && (
171+
<span
172+
className={`font-normal ${isGreenDescription ? 'text-green-600' : 'text-gray-500'}`}
173+
>
174+
{' '}
175+
{description}
176+
</span>
177+
)}
178+
</span>
141179
</div>
142180
</div>
143181
);
144-
}
145-
146-
return (
147-
<div className="flex items-start gap-3">
148-
<div className="flex-shrink-0 mt-0.5">
149-
{isEnterprise && isLast ? (
150-
<Wait size={16} color="rgba(28, 31, 35, 0.6)" />
151-
) : (
152-
<Checked size={16} color="#0E9F77" />
153-
)}
154-
</div>
155-
<div className="flex flex-col gap-0.5">
156-
<span className="text-sm leading-5 text-gray-900 font-semibold">
157-
{name}
158-
{description && <span className="font-normal text-gray-500"> {description}</span>}
159-
</span>
160-
</div>
161-
</div>
162-
);
163-
});
182+
},
183+
);
164184

165185
FeatureItem.displayName = 'FeatureItem';
166186

@@ -195,6 +215,7 @@ const PlanItem = memo((props: PlanItemProps) => {
195215
PlanPriorityMap[PlanPriorityMap[currentPlan as keyof typeof PlanPriorityMap] + 1] ||
196216
'enterprise';
197217
const isUpgrade = upgradePlan === planType;
218+
const [isHovered, setIsHovered] = useState(false);
198219
const isDowngrade =
199220
PlanPriorityMap[currentPlan as keyof typeof PlanPriorityMap] >
200221
PlanPriorityMap[planType as keyof typeof PlanPriorityMap];
@@ -253,7 +274,7 @@ const PlanItem = memo((props: PlanItemProps) => {
253274
}
254275
return (
255276
<span className="flex items-center justify-center gap-2">
256-
<IconLightning01 size={16} />
277+
<IconLightning01 size={20} color="#0E9F77" />
257278
{t('subscription.plans.upgrade', {
258279
planType: planType.charAt(0).toUpperCase() + planType.slice(1),
259280
})}
@@ -276,6 +297,12 @@ const PlanItem = memo((props: PlanItemProps) => {
276297
</div>
277298
<p className="text-sm text-gray-500 mb-4">{description}</p>
278299

300+
{/* Price */}
301+
<div className="flex items-baseline gap-2 mb-4">
302+
<span className="text-2xl font-bold text-gray-900">$0</span>
303+
<span className="text-sm text-gray-500">/month</span>
304+
</div>
305+
279306
{/* Button */}
280307
<button
281308
type="button"
@@ -299,7 +326,7 @@ const PlanItem = memo((props: PlanItemProps) => {
299326
{t('subscription.plans.memberBenefits')}
300327
</span>
301328
{features.map((feature, index) => (
302-
<FeatureItem key={index} feature={feature} />
329+
<FeatureItem key={index} feature={feature} planType={planType} featureIndex={index} />
303330
))}
304331
</div>
305332
</div>
@@ -314,11 +341,16 @@ const PlanItem = memo((props: PlanItemProps) => {
314341
background:
315342
isCurrentPlan || isUpgrade
316343
? 'linear-gradient(180deg, rgba(14, 159, 119, 0.08) 0%, rgba(255, 255, 255, 0) 30%), #ffffff'
317-
: 'linear-gradient(180deg, #1FC99615, #45BEFF10), #ffffff',
344+
: isHovered
345+
? 'linear-gradient(180deg, #A4FFF6, #CFFFD3), #ffffff'
346+
: '#ffffff',
318347
}}
348+
onMouseEnter={() => setIsHovered(true)}
349+
onMouseLeave={() => setIsHovered(false)}
319350
>
320351
{/* Header */}
321352
<div className="flex items-center gap-2 mb-1">
353+
<Subscription size={24} className="text-gray-900" />
322354
<span className="text-xl font-semibold text-gray-900">{title}</span>
323355
{isCurrentPlan && (
324356
<Tag className="!m-0 !px-2 !py-0.5 !text-xs !font-medium !rounded !bg-gray-100 !text-gray-600 !border-gray-200">
@@ -386,6 +418,8 @@ const PlanItem = memo((props: PlanItemProps) => {
386418
feature={feature}
387419
isEnterprise={planType === 'enterprise'}
388420
isLast={index === features.length - 1}
421+
planType={planType}
422+
featureIndex={index}
389423
/>
390424
))}
391425
</div>
@@ -422,7 +456,7 @@ export const PriceContent = memo((props: { source: PriceSource }) => {
422456
}, [currentPlan]);
423457

424458
const plansData = useMemo(() => {
425-
const planTypes = ['free', 'starter', 'maker', 'enterprise'];
459+
const planTypes = ['free', 'plus'];
426460
const data: Record<string, { title: string; description: string; features: Feature[] }> = {};
427461
for (const planType of planTypes) {
428462
const rawFeatures =

packages/i18n/src/en-US/ui.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ const translations = {
888888
endMessage: {
889889
title: "Didn't find the workflow automation template you need?",
890890
subtitle: "Join our Discord and tell us what you're looking for",
891-
goToMarketplace: 'Go to Marketplace',
891+
goToMarketplace: 'Explore More Templates',
892892
},
893893
},
894894
agentInputPlaceholder: 'Enter task description, Agent will generate reusable workflows',
@@ -2704,7 +2704,7 @@ const translations = {
27042704
getStarted: 'Get Started',
27052705
subscribeNow: 'Subscribe Now',
27062706
manage: 'Manage Subscription',
2707-
currentPlan: 'Current Plan',
2707+
currentPlan: 'Your Current Plan',
27082708
t1Requests: 'T1 Requests',
27092709
t1RequestsDescription:
27102710
'T1 models include Claude 3.7 Sonnet (Thinking), DeepSeek R1, o3 Mini, GPT-4o and others. Each successful skill call to T1 models counts as one request.',
@@ -3591,7 +3591,7 @@ const translations = {
35913591
},
35923592
},
35933593
subscription: {
3594-
modalTitle: 'Upgrade to Get More Credits',
3594+
modalTitle: 'Upgrade Your Plan to Get More Credits',
35953595
cancelAnytime: "Cancel anytime. By subscribing, you agree to Refly's",
35963596
privacy: 'Privacy',
35973597
terms: 'Terms',
@@ -3621,14 +3621,15 @@ const translations = {
36213621
priceMonthly: '{{price}}/month',
36223622
priceYearly: '{{price}}/month',
36233623
priceYearlyTotal: '{{price}}/year Save 20%',
3624-
upgrade: 'Upgrade to {{planType}}',
3624+
upgrade: 'Get {{planType}}',
36253625
cannotChangeTo: 'Cannot change to {{planType}}',
3626-
currentPlan: 'Current Plan',
3626+
currentPlan: 'Your Current Plan',
36273627
cannotSwitchTo:
36283628
"Legacy plans can't be switched to Plus directly.Please contact [email protected]",
3629+
memberBenefits: 'Member Benefits',
36293630
free: {
3630-
title: 'Free Plan',
3631-
description: 'A welcoming gateway to explore the power of AI—completely free',
3631+
title: 'Free',
3632+
description: 'Ideal for beginners exploring workflow automation',
36323633
price: 'Free forever',
36333634
buttonText: 'Continue for free',
36343635
buttonTextDowngrade: 'Downgrade to Free',
@@ -3644,9 +3645,9 @@ const translations = {
36443645
features: [
36453646
'Daily new credits\n300 credits',
36463647
'Monthly credits\n2,000 credits',
3647-
'New subscribers receive an extra\n2,000 bonus credits',
3648+
//'New subscribers receive an extra\n2,000 bonus credits',
36483649
'Access to a vast library of tools',
3649-
{
3650+
/*{
36503651
name: 'Credit-free tools',
36513652
type: 'pointFreeTools',
36523653
items: [
@@ -3660,7 +3661,7 @@ const translations = {
36603661
'X',
36613662
],
36623663
duration: '365 DAYS',
3663-
},
3664+
},*/
36643665
'Service support\nHigh priority support',
36653666
],
36663667
},
@@ -3825,7 +3826,7 @@ const translations = {
38253826
storageFullModal: {
38263827
free: {
38273828
title: 'Free Plan',
3828-
titleCn: 'Current Plan',
3829+
titleCn: 'Your Current Plan',
38293830
description: 'A welcoming gateway to explore the power of AI—completely free',
38303831
price: 'Free forever',
38313832
buttonText: 'Continue for free',
@@ -3880,7 +3881,7 @@ const translations = {
38803881
'More enterprise features coming soon',
38813882
],
38823883
},
3883-
currentPlan: 'Current Plan',
3884+
currentPlan: 'Your Current Plan',
38843885
},
38853886
},
38863887
onboarding: {

packages/i18n/src/zh-Hans/ui.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ const translations = {
138138
features: [
139139
'每日可获取新积分\n100点',
140140
'每月积分\n2000点',
141-
'首次订阅额外赠送\n2000点',
141+
//'首次订阅额外赠送\n2000点',
142142
'访问丰富的工具库',
143-
{
143+
/*{
144144
name: '免积分使用工具',
145145
type: 'pointFreeTools',
146146
items: [
@@ -154,7 +154,7 @@ const translations = {
154154
'X',
155155
],
156156
duration: '365 DAYS',
157-
},
157+
},*/
158158
'服务支持\n高优邮件支持',
159159
],
160160
},
@@ -968,7 +968,7 @@ const translations = {
968968
endMessage: {
969969
title: '没有找到你需要的工作流自动化模板?',
970970
subtitle: '加入我们的 Discord 社区,告诉我们你想要什么',
971-
goToMarketplace: '前往 Marketplace',
971+
goToMarketplace: '探索更多模板',
972972
},
973973
},
974974
agentInputPlaceholder: '输入任务描述,Agent 将生成可复用的工作流',

0 commit comments

Comments
 (0)