Skip to content

Commit 0fe1097

Browse files
committed
feat: billing change
1 parent 991d354 commit 0fe1097

File tree

18 files changed

+7662
-7106
lines changed

18 files changed

+7662
-7106
lines changed

apps/frontend/src/components/billing/embedded.billing.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Button } from '@gitroom/react/form/button';
1515
import dayjs from 'dayjs';
1616
import Image from 'next/image';
1717
import { useToaster } from '@gitroom/react/toaster/toaster';
18+
import { useT } from '@gitroom/react/translation/get.transation.service.client';
1819

1920
export const EmbeddedBilling: FC<{
2021
stripe: Promise<Stripe>;
@@ -119,22 +120,23 @@ const FormWrapper = () => {
119120

120121
const StripeInputs = () => {
121122
const checkout = useCheckout();
123+
const t = useT();
122124
return (
123125
<>
124126
<div>
125127
<h4 className="mb-[8px] text-[24px]">
126-
{checkout.type === 'loading' ? '' : 'Billing Address'}
128+
{checkout.type === 'loading' ? '' : t('billing_billing_address', 'Billing Address')}
127129
</h4>
128130
<BillingAddressElement />
129131
</div>
130132
<div>
131133
<h4 className="mt-[20px] mb-[8px] text-[24px]">
132-
{checkout.type === 'loading' ? '' : 'Payment'}
134+
{checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')}
133135
</h4>
134136
<PaymentElement id="payment-element" options={{ layout: 'tabs' }} />
135137
{checkout.type === 'loading' ? null : (
136138
<div className="mt-[24px] flex gap-[10px]">
137-
<div>Powered by Stripe</div>
139+
<div>{t('billing_powered_by_stripe', 'Powered by Stripe')}</div>
138140
<Image src="/stripe.svg" alt="Stripe" width={20} height={20} />
139141
</div>
140142
)}
@@ -145,6 +147,7 @@ const StripeInputs = () => {
145147

146148
const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
147149
const checkout = useCheckout();
150+
const t = useT();
148151
if (checkout.type === 'loading' || checkout.type === 'error') {
149152
return null;
150153
}
@@ -154,15 +157,15 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
154157
<div className="w-full h-full border-t border-newColColor bg-newBgColorInner px-[80px] flex gap-[32px] justify-end items-center font-[400] text-[14px] text-[#A3A3A3]">
155158
{checkout.checkout.recurring?.trial?.trialEnd ? (
156159
<div>
157-
Your 7-day trial is{' '}
158-
<span className="text-textColor font-[600]">100% free</span> ending{' '}
160+
{t('billing_your_7_day_trial_is', 'Your 7-day trial is')}{' '}
161+
<span className="text-textColor font-[600]">{t('billing_100_percent_free', '100% free')}</span> {t('billing_ending', 'ending')}{' '}
159162
<span className="text-textColor font-[600]">
160163
{dayjs(
161164
checkout.checkout.recurring?.trial?.trialEnd * 1000
162165
).format('MMMM D, YYYY')}{' '}
163166
{' '}
164167
</span>
165-
<span className="text-textColor font-[600]">Cancel anytime.</span>
168+
<span className="text-textColor font-[600]">{t('billing_cancel_anytime_short', 'Cancel anytime.')}</span>
166169
</div>
167170
) : null}
168171
<div>
@@ -172,8 +175,8 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
172175
loading={loading}
173176
>
174177
{checkout.checkout.recurring?.trial?.trialEnd
175-
? 'Pay $0 Today - Start your free trial!'
176-
: 'Pay Now'}
178+
? t('billing_pay_0_start_trial', 'Pay $0 Today - Start your free trial!')
179+
: t('billing_pay_now', 'Pay Now')}
177180
</Button>
178181
</div>
179182
</div>

apps/frontend/src/components/billing/first.billing.component.tsx

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
FAQComponent,
2222
FAQSection,
2323
} from '@gitroom/frontend/components/billing/faq.component';
24+
import { useT } from '@gitroom/react/translation/get.transation.service.client';
2425

2526
const ModeComponent = dynamic(
2627
() => import('@gitroom/frontend/components/layout/mode.component'),
@@ -45,6 +46,7 @@ export const FirstBillingComponent = () => {
4546
const [tier, setTier] = useState('STANDARD');
4647
const [period, setPeriod] = useState('MONTHLY');
4748
const fetch = useFetch();
49+
const t = useT();
4850

4951
useEffect(() => {
5052
setStripe(loadStripe(stripeClient));
@@ -102,29 +104,28 @@ export const FirstBillingComponent = () => {
102104
<div className="flex px-[80px] flex-1">
103105
<div className="flex-1 py-[40px] flex flex-col pe-[40px]">
104106
<div className="text-[36px] font-[600] leading-[110%] whitespace-pre-line">
105-
Join Over{' '}
106-
<span className="text-[#FC69FF]">18,000+ Entrepreneurs</span> who
107-
use{'\n'}Postiz To Grow Their Social Presence
107+
{t('billing_join_over', 'Join Over')}{' '}
108+
<span className="text-[#FC69FF]">{t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')}</span> {t('billing_who_use', 'who use')}{'\n'}{t('billing_postiz_grow_social', 'Postiz To Grow Their Social Presence')}
108109
</div>
109110

110111
<div className="flex mt-[34px] mb-[10px]">
111112
<div className="flex gap-[8px]">
112113
<div>
113114
<CheckIconComponent />
114115
</div>
115-
<div>100% No-Risk Free Trial</div>
116+
<div>{t('billing_no_risk_trial', '100% No-Risk Free Trial')}</div>
116117
</div>
117118
<div className="flex-1 flex gap-[8px] justify-center">
118119
<div>
119120
<CheckIconComponent />
120121
</div>
121-
<div>Pay NOTHING for the first 7-days</div>
122+
<div>{t('billing_pay_nothing_7_days', 'Pay NOTHING for the first 7-days')}</div>
122123
</div>
123124
<div className="flex gap-[8px]">
124125
<div>
125126
<CheckIconComponent />
126127
</div>
127-
<div>Cancel anytime, hassle-free</div>
128+
<div>{t('billing_cancel_anytime', 'Cancel anytime, hassle-free')}</div>
128129
</div>
129130
</div>
130131

@@ -140,7 +141,7 @@ export const FirstBillingComponent = () => {
140141
<div className="flex flex-col ps-[40px] border-l border-newColColor py-[40px]">
141142
<div className="top-[20px] sticky">
142143
<div className="flex mb-[24px]">
143-
<div className="flex-1 text-[24px] font-[700]">Choose a Plan</div>
144+
<div className="flex-1 text-[24px] font-[700]">{t('billing_choose_plan', 'Choose a Plan')}</div>
144145
<div className="h-[44px] px-[6px] flex items-center justify-center gap-[12px] border border-newColColor rounded-[12px] select-none">
145146
<div
146147
className={clsx(
@@ -151,7 +152,7 @@ export const FirstBillingComponent = () => {
151152
)}
152153
onClick={() => setPeriod('MONTHLY')}
153154
>
154-
Monthly
155+
{t('billing_monthly', 'Monthly')}
155156
</div>
156157
<div
157158
className={clsx(
@@ -162,9 +163,9 @@ export const FirstBillingComponent = () => {
162163
)}
163164
onClick={() => setPeriod('YEARLY')}
164165
>
165-
<div>Yearly</div>
166+
<div>{t('billing_yearly', 'Yearly')}</div>
166167
<div className="bg-[#AA0FA4] text-[white] px-[8px] rounded-[4px]">
167-
20% Off
168+
{t('billing_20_percent_off', '20% Off')}
168169
</div>
169170
</div>
170171
</div>
@@ -194,15 +195,15 @@ export const FirstBillingComponent = () => {
194195
]
195196
}
196197
</span>{' '}
197-
/ month
198+
{t('billing_per_month', '/ month')}
198199
</div>
199200
</div>
200201
),
201202
[]
202203
)}
203204
</div>
204205
<div className="flex flex-col mt-[54px] gap-[24px]">
205-
<div className="text-[24px] font-[700]">Features</div>
206+
<div className="text-[24px] font-[700]">{t('billing_features', 'Features')}</div>
206207
<BillingFeatures tier={tier} />
207208
</div>
208209
</div>
@@ -212,43 +213,72 @@ export const FirstBillingComponent = () => {
212213
);
213214
};
214215

216+
type FeatureItem = {
217+
key: string;
218+
defaultValue: string;
219+
prefix?: string | number;
220+
};
221+
215222
export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => {
216-
const render = useMemo(() => {
223+
const t = useT();
224+
const features = useMemo(() => {
217225
const currentPricing = pricing[tier];
218226
const channelsOr = currentPricing.channel;
219-
const list = [];
220-
list.push(`${channelsOr} ${channelsOr === 1 ? 'channel' : 'channels'}`);
221-
list.push(
222-
`${
223-
currentPricing.posts_per_month > 10000
224-
? 'Unlimited'
225-
: currentPricing.posts_per_month
226-
} posts per month`
227-
);
227+
const list: FeatureItem[] = [];
228+
229+
list.push({
230+
key: channelsOr === 1 ? 'billing_channel' : 'billing_channels',
231+
defaultValue: channelsOr === 1 ? 'channel' : 'channels',
232+
prefix: channelsOr,
233+
});
234+
235+
list.push({
236+
key: 'billing_posts_per_month',
237+
defaultValue: 'posts per month',
238+
prefix: currentPricing.posts_per_month > 10000 ? 'unlimited' : currentPricing.posts_per_month,
239+
});
240+
228241
if (currentPricing.team_members) {
229-
list.push(`Unlimited team members`);
242+
list.push({ key: 'billing_unlimited_team_members', defaultValue: 'Unlimited team members' });
230243
}
231244
if (currentPricing?.ai) {
232-
list.push(`AI auto-complete`);
233-
list.push(`AI copilots`);
234-
list.push(`AI Autocomplete`);
245+
list.push({ key: 'billing_ai_auto_complete', defaultValue: 'AI auto-complete' });
246+
list.push({ key: 'billing_ai_copilots', defaultValue: 'AI copilots' });
247+
list.push({ key: 'billing_ai_autocomplete', defaultValue: 'AI Autocomplete' });
235248
}
236-
list.push(`Advanced Picture Editor`);
249+
list.push({ key: 'billing_advanced_picture_editor', defaultValue: 'Advanced Picture Editor' });
237250
if (currentPricing?.image_generator) {
238-
list.push(
239-
`${currentPricing?.image_generation_count} AI Images per month`
240-
);
251+
list.push({
252+
key: 'billing_ai_images_per_month',
253+
defaultValue: 'AI Images per month',
254+
prefix: currentPricing?.image_generation_count,
255+
});
241256
}
242257
if (currentPricing?.generate_videos) {
243-
list.push(`${currentPricing?.generate_videos} AI Videos per month`);
258+
list.push({
259+
key: 'billing_ai_videos_per_month',
260+
defaultValue: 'AI Videos per month',
261+
prefix: currentPricing?.generate_videos,
262+
});
244263
}
245264
return list;
246265
}, [tier]);
247266

267+
const renderFeature = (feature: FeatureItem) => {
268+
const translatedText = t(feature.key, feature.defaultValue);
269+
if (feature.prefix === 'unlimited') {
270+
return `${t('billing_unlimited', 'Unlimited')} ${translatedText}`;
271+
}
272+
if (feature.prefix !== undefined) {
273+
return `${feature.prefix} ${translatedText}`;
274+
}
275+
return translatedText;
276+
};
277+
248278
return (
249279
<div className="grid grid-cols-2 gap-y-[8px] gap-x-[32px]">
250-
{render.map((p) => (
251-
<div key={p} className="flex items-center gap-[8px]">
280+
{features.map((feature) => (
281+
<div key={feature.key} className="flex items-center gap-[8px]">
252282
<div>
253283
<svg
254284
xmlns="http://www.w3.org/2000/svg"
@@ -263,7 +293,7 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => {
263293
/>
264294
</svg>
265295
</div>
266-
<div>{p}</div>
296+
<div>{renderFeature(feature)}</div>
267297
</div>
268298
))}
269299
</div>

i18n.lock

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ checksums:
1515
send_test: 6252eb4669859b7f7db4cdbc227580e1
1616
select_role: 406451b1c9a26f1484164b8b71c1bd7e
1717
video_made_with_ai: c37747aaf8107d339d6238a0463f7096
18-
please_add_at_least: 90d3c0237b56e57c7a58d5decf6e9d3c
18+
please_add_at_least: 776aa47593c7961b69172b49f49dc304
1919
send_invitation_via_email: 9275e0b85147a931421b3bf6c3083cb4
2020
global_settings: ba55734261d6bc26e792fda32de3e7ec
2121
copy_id: 831147124db35832872f8470c577e440
@@ -94,6 +94,7 @@ checksums:
9494
you_can_also_drag_drop_pictures: 40cc62ceac0cde30c1218c48796eb202
9595
you_don_t_have_any_assets_yet: ec8d0dc0a7ebf4c2437afb95f0e4cdb5
9696
click_the_button_below_to_upload_one: e2fb4d45f48de2277a0d71b9dd2c08ee
97+
click_the_button_below_to_upload_other: 8999406064d8cb19c648097db01f1b1a
9798
add_selected_media: 0f1bf2187f3df8d3b90df1895c6e530b
9899
insert_media: 15267304e6c6b2c19fc7d1941b2a6a11
99100
design_media: fee3bbf1846d9e5f4a1af783b9e3fc90
@@ -506,3 +507,36 @@ checksums:
506507
label_who_can_reply_to_this_post: 4d8913296a1fc3f197cb0aead34af73d
507508
delete_integration: ccc879ccfcf7f85bcfe09f2bc3fa0dd3
508509
start_writing_your_post: 471efc4f2a7e2cf02a065a2de34e7213
510+
billing_join_over: 6e1c237241ba00ddbb07fd603344c5c3
511+
billing_entrepreneurs_count: 05164f2ca2e8e20de3f63977c07d39fe
512+
billing_who_use: 63bdc59ef443e193eca87889e83ea07a
513+
billing_postiz_grow_social: 3cf5ab166df9cb65e17b9a039ccbbbad
514+
billing_no_risk_trial: 6e5c60d9ddf3affa8ba8870272f9f9ab
515+
billing_pay_nothing_7_days: 2db15615cad39981069b65e6a0852b18
516+
billing_cancel_anytime: f69ae39eec3fb9eb6114481d67c8481e
517+
billing_choose_plan: 6eacca8177c43945435ac9c97a1e9734
518+
billing_monthly: 818f1192e32bb855597f930d3e78806e
519+
billing_yearly: 87f43e016c19cb25860f456549a2f431
520+
billing_20_percent_off: 2d643feeaf30cafb2dd864b90f5c014b
521+
billing_features: 341ff316a339b106a178f0b8d362951b
522+
billing_channel: 7f661d461a99f05a2d57195fc3d262c3
523+
billing_channels: f6cd2d03caa496e649e95df2d6610879
524+
billing_unlimited: e1a92523172cd1bdde5550689840e42d
525+
billing_posts_per_month: ca72453dbacbffceddb12b41e3d1f559
526+
billing_unlimited_team_members: 254b7e4f144033e09d0127f50cd0422e
527+
billing_ai_auto_complete: 91eab83ad474698d10e25fbde8b8ffce
528+
billing_ai_copilots: 00054c5d5fe79ed4bb7e6decaa9f6608
529+
billing_ai_autocomplete: 25c332479a2cfa33c6f8a1548b58199f
530+
billing_advanced_picture_editor: 329a6c2e8b2685f81609859a4de07b0f
531+
billing_ai_images_per_month: 5073aa90b32654abe6200d426a97f03e
532+
billing_ai_videos_per_month: c786199d8dc9bded54fab8f92c350b19
533+
billing_billing_address: 48980a775bfc7292b0c4f9b74b4d352e
534+
billing_payment: 95142d3fd8b6a6f551aba771842e9c11
535+
billing_powered_by_stripe: 4b1f500613fe28f3cc17cb003ed0caf6
536+
billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c
537+
billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66
538+
billing_ending: f66133a14aa7d86ea2453df97de12cd5
539+
billing_cancel_anytime_short: 5b1b4a998fd56b18e4153dd83c84022c
540+
billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309
541+
billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819
542+
billing_per_month: 6293d01c3d13f6938d47285122bd1a48

0 commit comments

Comments
 (0)