Updated subscriptions
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { getAuthHeaders } from "./authHeaders";
|
||||
import { API_BASE } from "./config";
|
||||
|
||||
export async function createCheckoutSession(tier = "premium") {
|
||||
export async function createCheckoutSession(tier = "premium", interval = "annual") {
|
||||
const headers = await getAuthHeaders();
|
||||
const res = await fetch(`${API_BASE}/stripe/create-checkout-session`, {
|
||||
method: "POST",
|
||||
headers: { ...headers, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ tier }),
|
||||
body: JSON.stringify({ tier, interval }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json();
|
||||
|
||||
@@ -1,4 +1,45 @@
|
||||
.tier-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tier-picker__toggle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.tier-picker__toggle-btn {
|
||||
padding: 0.75rem 1.5rem;
|
||||
border: 1px solid var(--color-border);
|
||||
background: transparent;
|
||||
color: var(--color-text-dimmed);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tier-picker__toggle-btn:first-child {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.tier-picker__toggle-btn:last-child {
|
||||
border-radius: 0 8px 8px 0;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.tier-picker__toggle-btn--active {
|
||||
background: var(--color-primary);
|
||||
color: white;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.tier-picker__toggle-btn:last-child.tier-picker__toggle-btn--active {
|
||||
border-left: 1px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.tier-picker__cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
@@ -112,7 +153,7 @@
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.tier-picker {
|
||||
.tier-picker__cards {
|
||||
grid-template-columns: 1fr;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import "./TierPicker.css";
|
||||
|
||||
const TIERS = [
|
||||
{
|
||||
id: "free",
|
||||
name: "Trial Monitoring",
|
||||
price: "$0",
|
||||
period: "",
|
||||
price: { monthly: "$0", annual: "$0" },
|
||||
period: { monthly: "", annual: "" },
|
||||
features: [
|
||||
"Monitor 1 blockchain address 24/7",
|
||||
"Configure alerts to fire on trigger events",
|
||||
@@ -16,8 +17,8 @@ const TIERS = [
|
||||
{
|
||||
id: "premium",
|
||||
name: "Premium Monitoring",
|
||||
price: "$1.99",
|
||||
period: "/month",
|
||||
price: { monthly: "$8.78", annual: "$94.78" },
|
||||
period: { monthly: "/month", annual: "/year" },
|
||||
features: [
|
||||
"Monitor 3 blockchain addresses",
|
||||
"Configure two types of rule-based alerts to fire on trigger events for each of the three addresses",
|
||||
@@ -31,8 +32,8 @@ const TIERS = [
|
||||
{
|
||||
id: "pro",
|
||||
name: "Professional Monitoring",
|
||||
price: "$11.99",
|
||||
period: "/month",
|
||||
price: { monthly: "$16.78", annual: "$181.78" },
|
||||
period: { monthly: "/month", annual: "/year" },
|
||||
features: [
|
||||
"Monitor unlimited blockchain addresses",
|
||||
"Configure unlimited alert rules to fire on unlimited events on any address",
|
||||
@@ -47,43 +48,66 @@ const TIERS = [
|
||||
];
|
||||
|
||||
export default function TierPicker({ onSelect, selectedTier }) {
|
||||
const [isAnnual, setIsAnnual] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="tier-picker">
|
||||
{TIERS.map((tier) => (
|
||||
<div
|
||||
key={tier.id}
|
||||
className={`tier-picker__card${tier.highlighted ? " tier-picker__card--highlighted" : ""}${selectedTier === tier.id ? " tier-picker__card--selected" : ""}`}
|
||||
<div className="tier-picker__toggle">
|
||||
<button
|
||||
className={`tier-picker__toggle-btn${!isAnnual ? " tier-picker__toggle-btn--active" : ""}`}
|
||||
onClick={() => setIsAnnual(false)}
|
||||
>
|
||||
{tier.highlighted && (
|
||||
<div className="tier-picker__badge">Most Popular</div>
|
||||
)}
|
||||
<h3 className="tier-picker__name">{tier.name}</h3>
|
||||
<div className="tier-picker__price">
|
||||
<span className="tier-picker__amount">{tier.price}</span>
|
||||
{tier.period && (
|
||||
<span className="tier-picker__period">{tier.period}</span>
|
||||
)}
|
||||
</div>
|
||||
<ul className="tier-picker__features">
|
||||
{tier.features.map((f) => (
|
||||
<li key={f} className="tier-picker__feature">
|
||||
<span className="tier-picker__check">✓</span> {f}
|
||||
</li>
|
||||
))}
|
||||
{tier.disabledFeatures.map((f) => (
|
||||
<li key={f} className="tier-picker__feature tier-picker__feature--disabled">
|
||||
<span className="tier-picker__dash">—</span> {f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button
|
||||
className={`btn tier-picker__btn${selectedTier === tier.id ? " tier-picker__btn--selected" : ""}`}
|
||||
onClick={() => onSelect(tier.id)}
|
||||
Monthly
|
||||
</button>
|
||||
<button
|
||||
className={`tier-picker__toggle-btn${isAnnual ? " tier-picker__toggle-btn--active" : ""}`}
|
||||
onClick={() => setIsAnnual(true)}
|
||||
>
|
||||
Annual
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="tier-picker__cards">
|
||||
{TIERS.map((tier) => (
|
||||
<div
|
||||
key={tier.id}
|
||||
className={`tier-picker__card${tier.highlighted ? " tier-picker__card--highlighted" : ""}${selectedTier === tier.id ? " tier-picker__card--selected" : ""}`}
|
||||
>
|
||||
{selectedTier === tier.id ? "Selected" : "Select"}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{tier.highlighted && (
|
||||
<div className="tier-picker__badge">Most Popular</div>
|
||||
)}
|
||||
<h3 className="tier-picker__name">{tier.name}</h3>
|
||||
<div className="tier-picker__price">
|
||||
<span className="tier-picker__amount">
|
||||
{isAnnual ? tier.price.annual : tier.price.monthly}
|
||||
</span>
|
||||
{(isAnnual ? tier.period.annual : tier.period.monthly) && (
|
||||
<span className="tier-picker__period">
|
||||
{isAnnual ? tier.period.annual : tier.period.monthly}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ul className="tier-picker__features">
|
||||
{tier.features.map((f) => (
|
||||
<li key={f} className="tier-picker__feature">
|
||||
<span className="tier-picker__check">✓</span> {f}
|
||||
</li>
|
||||
))}
|
||||
{tier.disabledFeatures.map((f) => (
|
||||
<li key={f} className="tier-picker__feature tier-picker__feature--disabled">
|
||||
<span className="tier-picker__dash">—</span> {f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<button
|
||||
className={`btn tier-picker__btn${selectedTier === tier.id ? " tier-picker__btn--selected" : ""}`}
|
||||
onClick={() => onSelect(tier.id, isAnnual ? "annual" : "monthly")}
|
||||
>
|
||||
{selectedTier === tier.id ? "Selected" : "Select"}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ export default function Subscribe() {
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
selectedTier: null,
|
||||
billingInterval: "annual",
|
||||
});
|
||||
const [error, setError] = useState(
|
||||
searchParams.get("payment") === "cancelled"
|
||||
@@ -126,7 +127,7 @@ export default function Subscribe() {
|
||||
await refreshAccount();
|
||||
navigate("/addresses", { replace: true });
|
||||
} else {
|
||||
const { url } = await createCheckoutSession(data.selectedTier);
|
||||
const { url } = await createCheckoutSession(data.selectedTier, data.billingInterval);
|
||||
window.location.href = url;
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -245,7 +246,10 @@ export default function Subscribe() {
|
||||
Select the plan that works best for you. You can upgrade anytime.
|
||||
</p>
|
||||
<TierPicker
|
||||
onSelect={(tier) => set("selectedTier", tier)}
|
||||
onSelect={(tier, interval) => {
|
||||
set("selectedTier", tier);
|
||||
set("billingInterval", interval);
|
||||
}}
|
||||
selectedTier={data.selectedTier}
|
||||
/>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user