Skip to content

Commit 6a2a6a7

Browse files
committed
Refactor PricingPage layout and styles for better readability and user interaction
1 parent b19fdb2 commit 6a2a6a7

1 file changed

Lines changed: 125 additions & 136 deletions

File tree

src/frontend/src/pages/PricingPage/index.tsx

Lines changed: 125 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import axios from "axios";
33
import type { SVGProps } from "react";
44
import { useEffect, useState } from "react";
55
import { useNavigate } from "react-router-dom";
6-
import { getURL } from "../../controllers/API/helpers/constants";
76
import planConfigData from "../../../public/plan_config.json";
7+
import { getURL } from "../../controllers/API/helpers/constants";
88

99
const MIN_SEATS = 1;
1010
const PADDLE_PRICE_CACHE_KEY = "pricing_page_paddle_prices";
@@ -19,7 +19,10 @@ interface SharedPlanConfig {
1919
features: string[];
2020
paddle: {
2121
enabled: boolean;
22-
plan_key: "starter_pack_monthly" | "pro_pack_monthly" | "enterprise_pack_monthly";
22+
plan_key:
23+
| "starter_pack_monthly"
24+
| "pro_pack_monthly"
25+
| "enterprise_pack_monthly";
2326
monthly_price_usd_cents: number;
2427
trial_days: number | null;
2528
};
@@ -65,11 +68,16 @@ interface PlanConfig {
6568
hasTrial: boolean;
6669
trialDays?: number;
6770
features: string[];
68-
paddlePlanKey: "starter_pack_monthly" | "pro_pack_monthly" | "enterprise_pack_monthly";
71+
paddlePlanKey:
72+
| "starter_pack_monthly"
73+
| "pro_pack_monthly"
74+
| "enterprise_pack_monthly";
6975
defaultSeats: number;
7076
}
7177

72-
const STATIC_PLANS: PlanConfig[] = (planConfigData.plans as SharedPlanConfig[]).map((plan) => ({
78+
const STATIC_PLANS: PlanConfig[] = (
79+
planConfigData.plans as SharedPlanConfig[]
80+
).map((plan) => ({
7381
key: plan.key,
7482
name: plan.name,
7583
pricePerSeat: plan.paddle.monthly_price_usd_cents / 100,
@@ -100,7 +108,8 @@ export default function PricingPage() {
100108

101109
const [selectedPlan, setSelectedPlan] = useState<PlanKey>("starter");
102110
const [seatsByPlan, setSeatsByPlan] = useState<Record<PlanKey, number>>({
103-
starter: STATIC_PLANS.find((plan) => plan.key === "starter")?.defaultSeats ?? 1,
111+
starter:
112+
STATIC_PLANS.find((plan) => plan.key === "starter")?.defaultSeats ?? 1,
104113
pro: STATIC_PLANS.find((plan) => plan.key === "pro")?.defaultSeats ?? 1,
105114
enterprise:
106115
STATIC_PLANS.find((plan) => plan.key === "enterprise")?.defaultSeats ?? 5,
@@ -200,147 +209,127 @@ export default function PricingPage() {
200209
};
201210

202211
return (
203-
<div className="w-full overflow-hidden">
204-
<div style={{
205-
transform: "scale(0.67)",
206-
transformOrigin: "top center",
207-
width: "100%",
208-
display: "flex",
209-
justifyContent: "center",
210-
}}
211-
>
212-
<div style={{ width: "149.25%" }}>
213-
<div>
214-
<div className="min-h-screen px-4 py-12 text-slate-900 overflow-hidden">
215-
<div className="mx-auto max-w-6xl">
216-
<div className="rounded-[28px] border border-slate-200 bg-white p-8 shadow-[0_24px_80px_rgba(15,23,42,0.08)] sm:p-10">
217-
<div className="text-center">
218-
<span className="inline-flex rounded-full border border-sky-200 bg-sky-50 px-3 py-1 text-xs font-medium uppercase tracking-[0.2em] text-sky-700">
219-
Pricing
220-
</span>
221-
<h1 className="mt-4 text-3xl font-semibold tracking-tight text-slate-900 sm:text-4xl">
222-
Simple, scalable pricing
223-
</h1>
224-
<p className="mt-3 text-base text-slate-600 sm:text-lg">
225-
Choose the plan that matches your team size and move from
226-
onboarding to workspace with a consistent experience.
227-
</p>
228-
</div>
212+
<div className="w-full overflow-x-hidden">
213+
<div className="min-h-screen px-4 py-12 text-slate-900">
214+
<div className="mx-auto w-full max-w-5xl px-4 sm:px-6 lg:px-8">
215+
<div className="rounded-2xl border border-slate-200 bg-white p-6 sm:p-8 shadow-lg">
216+
<div className="text-center">
217+
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-semibold">
218+
Simple, scalable pricing
219+
</h1>
220+
</div>
229221

230-
<div className="mt-10 grid gap-6 md:grid-cols-3">
231-
{STATIC_PLANS.map((plan) => {
232-
const seats = seatsByPlan[plan.key];
233-
const total = seats * plan.pricePerSeat;
234-
235-
return (
236-
<div
237-
key={plan.key}
238-
className={`flex flex-col rounded-2xl border p-6 transition-all ${
239-
selectedPlan === plan.key
240-
? "border-sky-400 bg-sky-50/80 shadow-[0_18px_45px_rgba(14,165,233,0.12)]"
241-
: "border-slate-200 bg-white shadow-sm"
242-
}`}
222+
<div className="mt-8 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
223+
{STATIC_PLANS.map((plan) => {
224+
const seats = seatsByPlan[plan.key];
225+
const total = seats * plan.pricePerSeat;
226+
227+
return (
228+
<div
229+
key={plan.key}
230+
className={`flex flex-col rounded-2xl border p-5 sm:p-6 transition-all ${
231+
selectedPlan === plan.key
232+
? "border-sky-400 bg-sky-50/80 shadow-[0_18px_45px_rgba(14,165,233,0.12)]"
233+
: "border-slate-200 bg-white shadow-sm"
234+
}`}
235+
>
236+
<div className="text-sm font-medium uppercase tracking-[0.18em] text-slate-500">
237+
{plan.name}
238+
</div>
239+
<div className="mt-2 text-3xl font-semibold text-slate-900">
240+
${plan.pricePerSeat}/seat/month
241+
</div>
242+
243+
{plan.hasTrial && (
244+
<div className="mt-1 text-sm font-medium text-sky-700">
245+
{plan.trialDays}-day free trial
246+
</div>
247+
)}
248+
249+
<div className="mt-5 rounded-2xl border border-slate-200 bg-slate-50 p-4">
250+
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">
251+
Seats
252+
</div>
253+
254+
<div className="mt-2 flex items-center gap-3">
255+
<button
256+
type="button"
257+
onClick={() => handleDecrement(plan.key)}
258+
className="h-9 w-9 rounded-xl border border-slate-200 bg-white text-lg text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
243259
>
244-
<div className="text-sm font-medium uppercase tracking-[0.18em] text-slate-500">
245-
{plan.name}
246-
</div>
247-
<div className="mt-2 text-3xl font-semibold text-slate-900">
248-
${plan.pricePerSeat}/seat/month
249-
</div>
250-
251-
{plan.hasTrial && (
252-
<div className="mt-1 text-sm font-medium text-sky-700">
253-
{plan.trialDays}-day free trial
254-
</div>
255-
)}
256-
257-
<div className="mt-5 rounded-2xl border border-slate-200 bg-slate-50 p-4">
258-
<div className="text-xs uppercase tracking-[0.18em] text-slate-500">
259-
Seats
260-
</div>
261-
262-
<div className="mt-2 flex items-center gap-3">
263-
<button
264-
type="button"
265-
onClick={() => handleDecrement(plan.key)}
266-
className="h-9 w-9 rounded-xl border border-slate-200 bg-white text-lg text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
267-
>
268-
-
269-
</button>
270-
271-
<span className="min-w-8 text-center text-xl font-semibold text-slate-900">
272-
{seats}
273-
</span>
274-
275-
<button
276-
type="button"
277-
onClick={() => handleIncrement(plan.key)}
278-
className="h-9 w-9 rounded-xl border border-slate-200 bg-white text-lg text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
279-
>
280-
+
281-
</button>
282-
</div>
283-
284-
<div className="mt-3 text-sm text-slate-500">
285-
Estimated monthly total
286-
</div>
287-
<div className="text-2xl font-semibold text-slate-900">
288-
${total} USD
289-
</div>
290-
</div>
291-
292-
<ul className="mt-5 space-y-3 text-sm text-slate-600">
293-
{plan.features.map((feature) => (
294-
<li key={feature} className="flex items-center gap-2">
295-
<CheckIcon className="h-4 w-4 text-sky-600" />
296-
{feature}
297-
</li>
298-
))}
299-
</ul>
300-
301-
<button
302-
type="button"
303-
onClick={() => handleSelectPlan(plan)}
304-
className={`mt-6 rounded-xl px-4 py-3 text-sm font-semibold transition ${
305-
plan.key === "enterprise"
306-
? "border border-slate-200 bg-white text-slate-700 hover:border-sky-300 hover:text-sky-700"
307-
: "bg-slate-900 text-white hover:bg-slate-800"
308-
}`}
309-
>
310-
Select {plan.name}
311-
</button>
312-
</div>
313-
);
314-
})}
315-
</div>
260+
-
261+
</button>
262+
263+
<span className="min-w-8 text-center text-xl font-semibold text-slate-900">
264+
{seats}
265+
</span>
266+
267+
<button
268+
type="button"
269+
onClick={() => handleIncrement(plan.key)}
270+
className="h-9 w-9 rounded-xl border border-slate-200 bg-white text-lg text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
271+
>
272+
+
273+
</button>
274+
</div>
275+
276+
<div className="mt-3 text-sm text-slate-500">
277+
Estimated monthly total
278+
</div>
279+
<div className="text-2xl font-semibold text-slate-900">
280+
${total} USD
281+
</div>
282+
</div>
283+
284+
<ul className="mt-5 space-y-3 text-sm text-slate-600">
285+
{plan.features.map((feature) => (
286+
<li key={feature} className="flex items-center gap-2">
287+
<CheckIcon className="h-4 w-4 text-sky-600" />
288+
{feature}
289+
</li>
290+
))}
291+
</ul>
316292

317-
<div className="mt-10 flex flex-wrap justify-center gap-3">
318-
<button
319-
type="button"
320-
onClick={() => {
321-
navigate("/flows?pricing_bypass=1");
322-
}}
323-
className="rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-slate-800"
324-
>
325-
Go to flows
326-
</button>
327293
<button
328294
type="button"
329-
onClick={() => navigate("/organization")}
330-
className="rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
295+
onClick={() => handleSelectPlan(plan)}
296+
className={`mt-6 rounded-xl px-4 py-3 text-sm font-semibold transition ${
297+
plan.key === "enterprise"
298+
? "border border-slate-200 bg-white text-slate-700 hover:border-sky-300 hover:text-sky-700"
299+
: "bg-slate-900 text-white hover:bg-slate-800"
300+
}`}
331301
>
332-
Back to organization
302+
Select {plan.name}
333303
</button>
334304
</div>
305+
);
306+
})}
307+
</div>
335308

336-
{showEnterpriseContactMessage && (
337-
<p className="mt-5 text-center text-sm font-medium text-sky-700">
338-
For Enterprise plans, please contact us.
339-
</p>
340-
)}
341-
</div>
342-
</div>
309+
<div className="mt-8 flex flex-wrap justify-center gap-3">
310+
<button
311+
type="button"
312+
onClick={() => {
313+
navigate("/flows?pricing_bypass=1");
314+
}}
315+
className="rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white transition hover:bg-slate-800"
316+
>
317+
Go to flows
318+
</button>
319+
<button
320+
type="button"
321+
onClick={() => navigate("/organization")}
322+
className="rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
323+
>
324+
Back to organization
325+
</button>
343326
</div>
327+
328+
{showEnterpriseContactMessage && (
329+
<p className="mt-5 text-center text-sm font-medium text-sky-700">
330+
For Enterprise plans, please contact us.
331+
</p>
332+
)}
344333
</div>
345334
</div>
346335
</div>

0 commit comments

Comments
 (0)