feat: billing change

This commit is contained in:
Nevo David 2025-12-22 17:08:35 +07:00
parent 991d354c34
commit 0fe10976b0
18 changed files with 7662 additions and 7106 deletions

View File

@ -15,6 +15,7 @@ import { Button } from '@gitroom/react/form/button';
import dayjs from 'dayjs';
import Image from 'next/image';
import { useToaster } from '@gitroom/react/toaster/toaster';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
export const EmbeddedBilling: FC<{
stripe: Promise<Stripe>;
@ -119,22 +120,23 @@ const FormWrapper = () => {
const StripeInputs = () => {
const checkout = useCheckout();
const t = useT();
return (
<>
<div>
<h4 className="mb-[8px] text-[24px]">
{checkout.type === 'loading' ? '' : 'Billing Address'}
{checkout.type === 'loading' ? '' : t('billing_billing_address', 'Billing Address')}
</h4>
<BillingAddressElement />
</div>
<div>
<h4 className="mt-[20px] mb-[8px] text-[24px]">
{checkout.type === 'loading' ? '' : 'Payment'}
{checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')}
</h4>
<PaymentElement id="payment-element" options={{ layout: 'tabs' }} />
{checkout.type === 'loading' ? null : (
<div className="mt-[24px] flex gap-[10px]">
<div>Powered by Stripe</div>
<div>{t('billing_powered_by_stripe', 'Powered by Stripe')}</div>
<Image src="/stripe.svg" alt="Stripe" width={20} height={20} />
</div>
)}
@ -145,6 +147,7 @@ const StripeInputs = () => {
const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
const checkout = useCheckout();
const t = useT();
if (checkout.type === 'loading' || checkout.type === 'error') {
return null;
}
@ -154,15 +157,15 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
<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]">
{checkout.checkout.recurring?.trial?.trialEnd ? (
<div>
Your 7-day trial is{' '}
<span className="text-textColor font-[600]">100% free</span> ending{' '}
{t('billing_your_7_day_trial_is', 'Your 7-day trial is')}{' '}
<span className="text-textColor font-[600]">{t('billing_100_percent_free', '100% free')}</span> {t('billing_ending', 'ending')}{' '}
<span className="text-textColor font-[600]">
{dayjs(
checkout.checkout.recurring?.trial?.trialEnd * 1000
).format('MMMM D, YYYY')}{' '}
{' '}
</span>
<span className="text-textColor font-[600]">Cancel anytime.</span>
<span className="text-textColor font-[600]">{t('billing_cancel_anytime_short', 'Cancel anytime.')}</span>
</div>
) : null}
<div>
@ -172,8 +175,8 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => {
loading={loading}
>
{checkout.checkout.recurring?.trial?.trialEnd
? 'Pay $0 Today - Start your free trial!'
: 'Pay Now'}
? t('billing_pay_0_start_trial', 'Pay $0 Today - Start your free trial!')
: t('billing_pay_now', 'Pay Now')}
</Button>
</div>
</div>

View File

@ -21,6 +21,7 @@ import {
FAQComponent,
FAQSection,
} from '@gitroom/frontend/components/billing/faq.component';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
const ModeComponent = dynamic(
() => import('@gitroom/frontend/components/layout/mode.component'),
@ -45,6 +46,7 @@ export const FirstBillingComponent = () => {
const [tier, setTier] = useState('STANDARD');
const [period, setPeriod] = useState('MONTHLY');
const fetch = useFetch();
const t = useT();
useEffect(() => {
setStripe(loadStripe(stripeClient));
@ -102,9 +104,8 @@ export const FirstBillingComponent = () => {
<div className="flex px-[80px] flex-1">
<div className="flex-1 py-[40px] flex flex-col pe-[40px]">
<div className="text-[36px] font-[600] leading-[110%] whitespace-pre-line">
Join Over{' '}
<span className="text-[#FC69FF]">18,000+ Entrepreneurs</span> who
use{'\n'}Postiz To Grow Their Social Presence
{t('billing_join_over', 'Join Over')}{' '}
<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')}
</div>
<div className="flex mt-[34px] mb-[10px]">
@ -112,19 +113,19 @@ export const FirstBillingComponent = () => {
<div>
<CheckIconComponent />
</div>
<div>100% No-Risk Free Trial</div>
<div>{t('billing_no_risk_trial', '100% No-Risk Free Trial')}</div>
</div>
<div className="flex-1 flex gap-[8px] justify-center">
<div>
<CheckIconComponent />
</div>
<div>Pay NOTHING for the first 7-days</div>
<div>{t('billing_pay_nothing_7_days', 'Pay NOTHING for the first 7-days')}</div>
</div>
<div className="flex gap-[8px]">
<div>
<CheckIconComponent />
</div>
<div>Cancel anytime, hassle-free</div>
<div>{t('billing_cancel_anytime', 'Cancel anytime, hassle-free')}</div>
</div>
</div>
@ -140,7 +141,7 @@ export const FirstBillingComponent = () => {
<div className="flex flex-col ps-[40px] border-l border-newColColor py-[40px]">
<div className="top-[20px] sticky">
<div className="flex mb-[24px]">
<div className="flex-1 text-[24px] font-[700]">Choose a Plan</div>
<div className="flex-1 text-[24px] font-[700]">{t('billing_choose_plan', 'Choose a Plan')}</div>
<div className="h-[44px] px-[6px] flex items-center justify-center gap-[12px] border border-newColColor rounded-[12px] select-none">
<div
className={clsx(
@ -151,7 +152,7 @@ export const FirstBillingComponent = () => {
)}
onClick={() => setPeriod('MONTHLY')}
>
Monthly
{t('billing_monthly', 'Monthly')}
</div>
<div
className={clsx(
@ -162,9 +163,9 @@ export const FirstBillingComponent = () => {
)}
onClick={() => setPeriod('YEARLY')}
>
<div>Yearly</div>
<div>{t('billing_yearly', 'Yearly')}</div>
<div className="bg-[#AA0FA4] text-[white] px-[8px] rounded-[4px]">
20% Off
{t('billing_20_percent_off', '20% Off')}
</div>
</div>
</div>
@ -194,7 +195,7 @@ export const FirstBillingComponent = () => {
]
}
</span>{' '}
/ month
{t('billing_per_month', '/ month')}
</div>
</div>
),
@ -202,7 +203,7 @@ export const FirstBillingComponent = () => {
)}
</div>
<div className="flex flex-col mt-[54px] gap-[24px]">
<div className="text-[24px] font-[700]">Features</div>
<div className="text-[24px] font-[700]">{t('billing_features', 'Features')}</div>
<BillingFeatures tier={tier} />
</div>
</div>
@ -212,43 +213,72 @@ export const FirstBillingComponent = () => {
);
};
type FeatureItem = {
key: string;
defaultValue: string;
prefix?: string | number;
};
export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => {
const render = useMemo(() => {
const t = useT();
const features = useMemo(() => {
const currentPricing = pricing[tier];
const channelsOr = currentPricing.channel;
const list = [];
list.push(`${channelsOr} ${channelsOr === 1 ? 'channel' : 'channels'}`);
list.push(
`${
currentPricing.posts_per_month > 10000
? 'Unlimited'
: currentPricing.posts_per_month
} posts per month`
);
const list: FeatureItem[] = [];
list.push({
key: channelsOr === 1 ? 'billing_channel' : 'billing_channels',
defaultValue: channelsOr === 1 ? 'channel' : 'channels',
prefix: channelsOr,
});
list.push({
key: 'billing_posts_per_month',
defaultValue: 'posts per month',
prefix: currentPricing.posts_per_month > 10000 ? 'unlimited' : currentPricing.posts_per_month,
});
if (currentPricing.team_members) {
list.push(`Unlimited team members`);
list.push({ key: 'billing_unlimited_team_members', defaultValue: 'Unlimited team members' });
}
if (currentPricing?.ai) {
list.push(`AI auto-complete`);
list.push(`AI copilots`);
list.push(`AI Autocomplete`);
list.push({ key: 'billing_ai_auto_complete', defaultValue: 'AI auto-complete' });
list.push({ key: 'billing_ai_copilots', defaultValue: 'AI copilots' });
list.push({ key: 'billing_ai_autocomplete', defaultValue: 'AI Autocomplete' });
}
list.push(`Advanced Picture Editor`);
list.push({ key: 'billing_advanced_picture_editor', defaultValue: 'Advanced Picture Editor' });
if (currentPricing?.image_generator) {
list.push(
`${currentPricing?.image_generation_count} AI Images per month`
);
list.push({
key: 'billing_ai_images_per_month',
defaultValue: 'AI Images per month',
prefix: currentPricing?.image_generation_count,
});
}
if (currentPricing?.generate_videos) {
list.push(`${currentPricing?.generate_videos} AI Videos per month`);
list.push({
key: 'billing_ai_videos_per_month',
defaultValue: 'AI Videos per month',
prefix: currentPricing?.generate_videos,
});
}
return list;
}, [tier]);
const renderFeature = (feature: FeatureItem) => {
const translatedText = t(feature.key, feature.defaultValue);
if (feature.prefix === 'unlimited') {
return `${t('billing_unlimited', 'Unlimited')} ${translatedText}`;
}
if (feature.prefix !== undefined) {
return `${feature.prefix} ${translatedText}`;
}
return translatedText;
};
return (
<div className="grid grid-cols-2 gap-y-[8px] gap-x-[32px]">
{render.map((p) => (
<div key={p} className="flex items-center gap-[8px]">
{features.map((feature) => (
<div key={feature.key} className="flex items-center gap-[8px]">
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -263,7 +293,7 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => {
/>
</svg>
</div>
<div>{p}</div>
<div>{renderFeature(feature)}</div>
</div>
))}
</div>

View File

@ -15,7 +15,7 @@ checksums:
send_test: 6252eb4669859b7f7db4cdbc227580e1
select_role: 406451b1c9a26f1484164b8b71c1bd7e
video_made_with_ai: c37747aaf8107d339d6238a0463f7096
please_add_at_least: 90d3c0237b56e57c7a58d5decf6e9d3c
please_add_at_least: 776aa47593c7961b69172b49f49dc304
send_invitation_via_email: 9275e0b85147a931421b3bf6c3083cb4
global_settings: ba55734261d6bc26e792fda32de3e7ec
copy_id: 831147124db35832872f8470c577e440
@ -94,6 +94,7 @@ checksums:
you_can_also_drag_drop_pictures: 40cc62ceac0cde30c1218c48796eb202
you_don_t_have_any_assets_yet: ec8d0dc0a7ebf4c2437afb95f0e4cdb5
click_the_button_below_to_upload_one: e2fb4d45f48de2277a0d71b9dd2c08ee
click_the_button_below_to_upload_other: 8999406064d8cb19c648097db01f1b1a
add_selected_media: 0f1bf2187f3df8d3b90df1895c6e530b
insert_media: 15267304e6c6b2c19fc7d1941b2a6a11
design_media: fee3bbf1846d9e5f4a1af783b9e3fc90
@ -506,3 +507,36 @@ checksums:
label_who_can_reply_to_this_post: 4d8913296a1fc3f197cb0aead34af73d
delete_integration: ccc879ccfcf7f85bcfe09f2bc3fa0dd3
start_writing_your_post: 471efc4f2a7e2cf02a065a2de34e7213
billing_join_over: 6e1c237241ba00ddbb07fd603344c5c3
billing_entrepreneurs_count: 05164f2ca2e8e20de3f63977c07d39fe
billing_who_use: 63bdc59ef443e193eca87889e83ea07a
billing_postiz_grow_social: 3cf5ab166df9cb65e17b9a039ccbbbad
billing_no_risk_trial: 6e5c60d9ddf3affa8ba8870272f9f9ab
billing_pay_nothing_7_days: 2db15615cad39981069b65e6a0852b18
billing_cancel_anytime: f69ae39eec3fb9eb6114481d67c8481e
billing_choose_plan: 6eacca8177c43945435ac9c97a1e9734
billing_monthly: 818f1192e32bb855597f930d3e78806e
billing_yearly: 87f43e016c19cb25860f456549a2f431
billing_20_percent_off: 2d643feeaf30cafb2dd864b90f5c014b
billing_features: 341ff316a339b106a178f0b8d362951b
billing_channel: 7f661d461a99f05a2d57195fc3d262c3
billing_channels: f6cd2d03caa496e649e95df2d6610879
billing_unlimited: e1a92523172cd1bdde5550689840e42d
billing_posts_per_month: ca72453dbacbffceddb12b41e3d1f559
billing_unlimited_team_members: 254b7e4f144033e09d0127f50cd0422e
billing_ai_auto_complete: 91eab83ad474698d10e25fbde8b8ffce
billing_ai_copilots: 00054c5d5fe79ed4bb7e6decaa9f6608
billing_ai_autocomplete: 25c332479a2cfa33c6f8a1548b58199f
billing_advanced_picture_editor: 329a6c2e8b2685f81609859a4de07b0f
billing_ai_images_per_month: 5073aa90b32654abe6200d426a97f03e
billing_ai_videos_per_month: c786199d8dc9bded54fab8f92c350b19
billing_billing_address: 48980a775bfc7292b0c4f9b74b4d352e
billing_payment: 95142d3fd8b6a6f551aba771842e9c11
billing_powered_by_stripe: 4b1f500613fe28f3cc17cb003ed0caf6
billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c
billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66
billing_ending: f66133a14aa7d86ea2453df97de12cd5
billing_cancel_anytime_short: 5b1b4a998fd56b18e4153dd83c84022c
billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309
billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819
billing_per_month: 6293d01c3d13f6938d47285122bd1a48

View File

@ -502,5 +502,38 @@
"separate_post": "Separate post to multiple posts",
"label_who_can_reply_to_this_post": "Who can reply to this post?",
"delete_integration": "Delete Integration",
"start_writing_your_post": "Start writing your post for a preview"
"start_writing_your_post": "Start writing your post for a preview",
"billing_join_over": "Join Over",
"billing_entrepreneurs_count": "18,000+ Entrepreneurs",
"billing_who_use": "who use",
"billing_postiz_grow_social": "Postiz To Grow Their Social Presence",
"billing_no_risk_trial": "100% No-Risk Free Trial",
"billing_pay_nothing_7_days": "Pay NOTHING for the first 7-days",
"billing_cancel_anytime": "Cancel anytime, hassle-free",
"billing_choose_plan": "Choose a Plan",
"billing_monthly": "Monthly",
"billing_yearly": "Yearly",
"billing_20_percent_off": "20% Off",
"billing_features": "Features",
"billing_channel": "channel",
"billing_channels": "channels",
"billing_unlimited": "Unlimited",
"billing_posts_per_month": "posts per month",
"billing_unlimited_team_members": "Unlimited team members",
"billing_ai_auto_complete": "AI auto-complete",
"billing_ai_copilots": "AI copilots",
"billing_ai_autocomplete": "AI Autocomplete",
"billing_advanced_picture_editor": "Advanced Picture Editor",
"billing_ai_images_per_month": "AI Images per month",
"billing_ai_videos_per_month": "AI Videos per month",
"billing_billing_address": "Billing Address",
"billing_payment": "Payment",
"billing_powered_by_stripe": "Powered by Stripe",
"billing_your_7_day_trial_is": "Your 7-day trial is",
"billing_100_percent_free": "100% free",
"billing_ending": "ending",
"billing_cancel_anytime_short": "Cancel anytime.",
"billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!",
"billing_pay_now": "Pay Now",
"billing_per_month": "/ month"
}