chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
export {
|
||||
PricingCard,
|
||||
type PricingCardCta,
|
||||
type PricingCardProps,
|
||||
type PricingCardSection,
|
||||
} from './pricing-card'
|
||||
@@ -0,0 +1,138 @@
|
||||
import { Check, ChipLink, ChipTag, cn } from '@sim/emcn'
|
||||
import { SlackIcon } from '@/components/icons'
|
||||
import type { CellValue } from '@/app/workspace/[workspaceId]/upgrade/components/comparison-table/comparison-data'
|
||||
|
||||
/** Maps a cell-icon identifier to its brand icon component. */
|
||||
const CELL_ICONS = { slack: SlackIcon } as const
|
||||
|
||||
/** Resolved CTA for a pricing card - label, chip variant, and destination href. */
|
||||
export interface PricingCardCta {
|
||||
label: string
|
||||
variant: 'primary' | 'border-shadow'
|
||||
href: string
|
||||
}
|
||||
|
||||
/** A labelled group of feature rows for one plan, transposed from the comparison data. */
|
||||
export interface PricingCardSection {
|
||||
/** Stable key for the section, used for React reconciliation. */
|
||||
key: string
|
||||
/** Section header, e.g. `"Rate limits (runs/min)"`. Omit to render the rows without a header. */
|
||||
title?: string
|
||||
/** Feature rows - this plan's value for each comparison row in the section. */
|
||||
rows: { label: string; value: CellValue }[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for {@link PricingCard}.
|
||||
*/
|
||||
export interface PricingCardProps {
|
||||
/** Plan name, e.g. `"Pro"`. */
|
||||
name: string
|
||||
/** Headline price, e.g. `"$25"`, `"$0"`, or `"Custom"`. */
|
||||
price: string
|
||||
/** Small line under the price, e.g. `"per user/month, billed monthly"`. */
|
||||
priceSubtext?: string
|
||||
/** Optional discount pill next to the price, e.g. `"15% off"`. */
|
||||
discountLabel?: string
|
||||
/** Resolved CTA - label, variant, and destination href. */
|
||||
cta: PricingCardCta
|
||||
/** Full feature breakdown for this plan, grouped by section. */
|
||||
sections: PricingCardSection[]
|
||||
/** Extra outer classes. */
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders one plan's value for a feature row: `true` → check, `false` → em-dash,
|
||||
* an icon reference → its brand icon, a string → right-aligned text.
|
||||
*/
|
||||
function FeatureValue({ value }: { value: CellValue }) {
|
||||
if (value === true) {
|
||||
return <Check className='size-[14px] flex-shrink-0 text-[var(--text-icon)]' />
|
||||
}
|
||||
if (value === false) {
|
||||
return <span className='select-none text-[var(--text-muted)]'>–</span>
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
const Icon = CELL_ICONS[value.icon]
|
||||
return <Icon className='size-[14px] flex-shrink-0' />
|
||||
}
|
||||
return (
|
||||
<span className='whitespace-nowrap text-right text-[var(--text-primary)] text-sm tabular-nums'>
|
||||
{value}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Public pricing card - one plan rendered as a self-contained spec sheet: the
|
||||
* header (name, price, CTA, segment) above the full comparison breakdown, grouped
|
||||
* by section with a hairline rule under each section header.
|
||||
*
|
||||
* The radius (`rounded-lg`) matches the landing hero's visual panel so the cards
|
||||
* line up with the rest of the landing chrome. Every card carries the identical
|
||||
* row structure (only values differ) so a grid of them renders at exactly the
|
||||
* same height. Feature rows are transposed from the shared comparison data, so
|
||||
* the card can never drift from the platform.
|
||||
*/
|
||||
export function PricingCard({
|
||||
name,
|
||||
price,
|
||||
priceSubtext,
|
||||
discountLabel,
|
||||
cta,
|
||||
sections,
|
||||
className,
|
||||
}: PricingCardProps) {
|
||||
return (
|
||||
<article
|
||||
className={cn(
|
||||
'flex h-full flex-col gap-[22px] rounded-lg border border-[var(--border-1)] bg-[var(--surface-2)] p-5',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className='flex flex-col gap-[22px]'>
|
||||
<h2 className='text-[24px] text-[var(--text-primary)]'>{name}</h2>
|
||||
|
||||
<div className='flex flex-col'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='text-[20px] text-[var(--text-primary)] tabular-nums'>{price}</span>
|
||||
{discountLabel && <ChipTag variant='mono'>{discountLabel}</ChipTag>}
|
||||
</div>
|
||||
<p className='text-[var(--text-muted)] text-base'>{priceSubtext ?? ' '}</p>
|
||||
</div>
|
||||
|
||||
<ChipLink
|
||||
href={cta.href}
|
||||
variant={cta.variant}
|
||||
fullWidth
|
||||
flush
|
||||
className='w-full justify-center'
|
||||
>
|
||||
{cta.label}
|
||||
</ChipLink>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col gap-5'>
|
||||
{sections.map((section) => (
|
||||
<div key={section.key} className='flex flex-col'>
|
||||
{section.title && (
|
||||
<>
|
||||
<span className='text-[var(--text-primary)] text-small'>{section.title}</span>
|
||||
<div className='mt-2 mb-2.5 h-px bg-[var(--border)]' />
|
||||
</>
|
||||
)}
|
||||
<div className='flex flex-col gap-2.5'>
|
||||
{section.rows.map((row) => (
|
||||
<div key={row.label} className='flex items-center justify-between gap-3'>
|
||||
<span className='text-[var(--text-body)] text-sm'>{row.label}</span>
|
||||
<FeatureValue value={row.value} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user