forked from coracle/caravel
Add contact option on main pricing page
This commit is contained in:
@@ -28,6 +28,7 @@ pub struct Plan {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub amount: i64,
|
pub amount: i64,
|
||||||
|
pub hidden: bool,
|
||||||
pub members: Option<i64>,
|
pub members: Option<i64>,
|
||||||
pub blossom: bool,
|
pub blossom: bool,
|
||||||
pub livekit: bool,
|
pub livekit: bool,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ pub fn list_plans() -> Vec<Plan> {
|
|||||||
id: "free".to_string(),
|
id: "free".to_string(),
|
||||||
name: "Free".to_string(),
|
name: "Free".to_string(),
|
||||||
amount: 0,
|
amount: 0,
|
||||||
|
hidden: false,
|
||||||
members: Some(10),
|
members: Some(10),
|
||||||
blossom: false,
|
blossom: false,
|
||||||
livekit: false,
|
livekit: false,
|
||||||
@@ -31,6 +32,7 @@ pub fn list_plans() -> Vec<Plan> {
|
|||||||
id: "basic".to_string(),
|
id: "basic".to_string(),
|
||||||
name: "Basic".to_string(),
|
name: "Basic".to_string(),
|
||||||
amount: 500,
|
amount: 500,
|
||||||
|
hidden: false,
|
||||||
members: Some(100),
|
members: Some(100),
|
||||||
blossom: true,
|
blossom: true,
|
||||||
livekit: true,
|
livekit: true,
|
||||||
@@ -39,6 +41,7 @@ pub fn list_plans() -> Vec<Plan> {
|
|||||||
id: "growth".to_string(),
|
id: "growth".to_string(),
|
||||||
name: "Growth".to_string(),
|
name: "Growth".to_string(),
|
||||||
amount: 2500,
|
amount: 2500,
|
||||||
|
hidden: false,
|
||||||
members: None,
|
members: None,
|
||||||
blossom: true,
|
blossom: true,
|
||||||
livekit: true,
|
livekit: true,
|
||||||
|
|||||||
@@ -38,9 +38,24 @@ type PricingTableProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function PricingTable(props: PricingTableProps) {
|
export default function PricingTable(props: PricingTableProps) {
|
||||||
|
// Hidden plans (e.g. the legacy "basic" tier) stay resolvable for billing via
|
||||||
|
// the backend but never show up in the public pricing table or plan selector.
|
||||||
|
// This is the only place the hidden flag is honored.
|
||||||
|
const visiblePlans = () => plans().filter((plan) => !plan.hidden)
|
||||||
|
|
||||||
|
// The full pricing page also shows the synthetic "Custom" card, so the column
|
||||||
|
// count tracks the real number of cards (literal classes so Tailwind keeps them).
|
||||||
|
const cardCount = () => visiblePlans().length + (props.selectable ? 0 : 1)
|
||||||
|
const gridCols: Record<number, string> = {
|
||||||
|
1: "lg:grid-cols-1",
|
||||||
|
2: "lg:grid-cols-2",
|
||||||
|
3: "lg:grid-cols-3",
|
||||||
|
4: "lg:grid-cols-4",
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 items-start">
|
<div class={`grid grid-cols-1 gap-6 items-start ${gridCols[cardCount()] ?? "lg:grid-cols-4"}`}>
|
||||||
<For each={plans()}>
|
<For each={visiblePlans()}>
|
||||||
{(plan) => {
|
{(plan) => {
|
||||||
const isPopular = plan.id === "basic"
|
const isPopular = plan.id === "basic"
|
||||||
const isSelected = () => props.selectable && props.selectedPlanId === plan.id
|
const isSelected = () => props.selectable && props.selectedPlanId === plan.id
|
||||||
@@ -99,6 +114,27 @@ export default function PricingTable(props: PricingTableProps) {
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
|
{!props.selectable && (
|
||||||
|
<div class="relative bg-white rounded-2xl p-8 border border-gray-200">
|
||||||
|
<h3 class="text-lg font-bold text-gray-900 mb-1">Custom</h3>
|
||||||
|
<div class="mb-8">
|
||||||
|
<span class="text-4xl font-extrabold text-gray-900">Let's talk</span>
|
||||||
|
</div>
|
||||||
|
<ul class="mb-8 text-sm text-gray-600 space-y-3">
|
||||||
|
<li class="flex items-start gap-2"><CheckIcon />White-labeled app</li>
|
||||||
|
<li class="flex items-start gap-2"><CheckIcon />Dedicated support</li>
|
||||||
|
<li class="flex items-start gap-2"><CheckIcon />Custom feature development</li>
|
||||||
|
</ul>
|
||||||
|
<a
|
||||||
|
href="https://cal.com/coracle.social/30min"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="block w-full text-center py-2.5 px-4 text-sm rounded-xl font-semibold transition-colors border border-gray-200 text-gray-700 hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
Contact us
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export default function RelayForm(props: RelayFormProps) {
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 mb-3">Plan</label>
|
<label class="block text-sm font-medium text-gray-700 mb-3">Plan</label>
|
||||||
<div class="grid grid-cols-3 gap-3">
|
<div class="grid grid-cols-3 gap-3">
|
||||||
<For each={plans()}>
|
<For each={plans().filter((p) => !p.hidden)}>
|
||||||
{(p) => (
|
{(p) => (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export type Plan = {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
amount: number
|
amount: number
|
||||||
|
hidden: boolean
|
||||||
members: number | null
|
members: number | null
|
||||||
blossom: boolean
|
blossom: boolean
|
||||||
livekit: boolean
|
livekit: boolean
|
||||||
|
|||||||
Reference in New Issue
Block a user