docs: add cloud plans & pricing page (#13420)
* docs: add cloud plans & pricing page * fix buttons * simplify condition * remove test frontmatter * design fixes and changes * styling fixes * change node version for tests * fix build error * fix tests * fix github pipeline
This commit is contained in:
5
.github/workflows/docs-test.yml
vendored
5
.github/workflows/docs-test.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- name: Setup Node.js environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -43,6 +43,9 @@ jobs:
|
||||
NEXT_PUBLIC_DOCS_URL: "https://medusa-docs.vercel.app"
|
||||
NODE_ENV: production
|
||||
NEXT_PUBLIC_RESOURCES_URL: "http://medusa-types-nine.vercel.app"
|
||||
NEXT_PUBLIC_SANITY_PROJECT_ID: temp
|
||||
NEXT_PUBLIC_SANITY_DATASET: temp
|
||||
NEXT_PUBLIC_ENV: CI
|
||||
# TODO change once we have actual URLs
|
||||
NEXT_PUBLIC_USER_GUIDE_URL: "http://example.com"
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Yes, your code is hosted on your GitHub repository. You can also export a dump o
|
||||
|
||||
## Can I deploy multiple projects on Cloud?
|
||||
|
||||
Yes, if your plan supports multiple projects. Learn more in the [Pricing](https://medusajs.com/pricing/) page.
|
||||
Yes, if your plan supports multiple projects. Learn more in the [Plans & Pricing](../pricing/page.mdx) guide.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ In this window, you can view the members of the organization, their roles, and t
|
||||
|
||||
Based on your organization's plan, you may have a limited number of seats available for members. You can view the number of available seats at the top of the Team tab.
|
||||
|
||||
If you need more seats, learn more about [other available plans](https://medusajs.com/pricing).
|
||||
If you need more seats, learn more about [other available plans](../pricing/page.mdx) or [purchasing add-on resources](../billing/page.mdx#purchase-add-on-resources).
|
||||
|
||||

|
||||
|
||||
|
||||
68
www/apps/cloud/app/pricing/content.tsx
Normal file
68
www/apps/cloud/app/pricing/content.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
"use server"
|
||||
|
||||
import { sanityClient } from "../../utils/sanity-client"
|
||||
import { PricingQueryResult } from "../../utils/types"
|
||||
import HeroPricing from "../../components/Pricing/HeroPricing"
|
||||
import { notFound } from "next/navigation"
|
||||
import FeatureSections from "../../components/Pricing/FeatureSections"
|
||||
import { H2, Hr, Loading } from "docs-ui"
|
||||
import { cache, Suspense } from "react"
|
||||
|
||||
export default async function PricingPage() {
|
||||
if (process.env.NEXT_PUBLIC_ENV === "CI") {
|
||||
return <div>Pricing page is not available in the CI environment.</div>
|
||||
}
|
||||
const data = await loadPricingData()
|
||||
|
||||
const heroPricingData = data.find((item) => item._type === "heroPricing")
|
||||
const featureTableData = data.find((item) => item._type === "featureTable")
|
||||
|
||||
// Ensure both data pieces are present
|
||||
if (
|
||||
!featureTableData?.featureTableFields ||
|
||||
!heroPricingData?.heroPricingFields
|
||||
) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<Loading />}>
|
||||
<H2 id="cloud-plans">Cloud Plans</H2>
|
||||
<HeroPricing data={heroPricingData.heroPricingFields} />
|
||||
<Hr />
|
||||
<H2 id="plans-features">Plans Features</H2>
|
||||
<FeatureSections
|
||||
featureSections={featureTableData.featureTableFields.featureSections}
|
||||
columnCount={featureTableData.featureTableFields.columnHeaders.length}
|
||||
columns={featureTableData.featureTableFields.columnHeaders}
|
||||
/>
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
const loadPricingData = cache(async () => {
|
||||
const data: PricingQueryResult = await sanityClient.fetch(
|
||||
`*[
|
||||
(_type == "featureTable" && _id == "9cb4e359-786a-4cdb-9334-88ad4ce44f05") ||
|
||||
(_type == "heroPricing" && _id == "8d8f33e1-7f18-4b2f-8686-5bc57da697db")
|
||||
]{
|
||||
_type,
|
||||
_id,
|
||||
// For featureTable
|
||||
"featureTableFields": select(
|
||||
_type == "featureTable" => {
|
||||
columnHeaders,
|
||||
featureSections,
|
||||
links
|
||||
}
|
||||
),
|
||||
// For heroPricing
|
||||
"heroPricingFields": select(
|
||||
_type == "heroPricing" => {
|
||||
options
|
||||
}
|
||||
)
|
||||
}`
|
||||
)
|
||||
return data
|
||||
})
|
||||
15
www/apps/cloud/app/pricing/page.mdx
Normal file
15
www/apps/cloud/app/pricing/page.mdx
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
generate_toc: true
|
||||
---
|
||||
|
||||
import Page from "./content"
|
||||
|
||||
export const metadata = {
|
||||
title: `Plans & Pricing`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this guide, find details about the different Cloud plans and pricing.
|
||||
|
||||
<Page />
|
||||
218
www/apps/cloud/components/Pricing/FeatureSections/index.tsx
Normal file
218
www/apps/cloud/components/Pricing/FeatureSections/index.tsx
Normal file
@@ -0,0 +1,218 @@
|
||||
import React from "react"
|
||||
import clsx from "clsx"
|
||||
import {
|
||||
FeatureTableFields,
|
||||
Block,
|
||||
Span,
|
||||
TooltipBlock,
|
||||
} from "../../../utils/types"
|
||||
import { BorderedIcon, H3, MarkdownContent, MDXComponents } from "docs-ui"
|
||||
import slugify from "slugify"
|
||||
import {
|
||||
CodePullRequest,
|
||||
CurrencyDollar,
|
||||
ServerStack,
|
||||
Shopping,
|
||||
WIP,
|
||||
} from "@medusajs/icons"
|
||||
|
||||
const P = MDXComponents.p
|
||||
|
||||
interface FeatureSectionsProps {
|
||||
featureSections: FeatureTableFields["featureSections"]
|
||||
columnCount: number
|
||||
columns: string[]
|
||||
}
|
||||
|
||||
const featureLinks: Record<string, string> = {
|
||||
Orders: "https://docs.medusajs.com/commerce-modules/order",
|
||||
Products: "https://docs.medusajs.com/commerce-modules/product",
|
||||
"Sales Channels": "https://docs.medusajs.com/commerce-modules/sales-channels",
|
||||
"Regions & currencies": "https://docs.medusajs.com/commerce-modules/region",
|
||||
"GitHub integration":
|
||||
"https://docs.medusajs.com/cloud/projects#2-create-project-from-an-existing-application",
|
||||
"Push-to-deploy flow":
|
||||
"https://docs.medusajs.com/cloud/deployments#how-are-deployments-created",
|
||||
Previews: "https://docs.medusajs.com/cloud/environments/preview",
|
||||
"Auto configuration:":
|
||||
"https://docs.medusajs.com/cloud/projects#prerequisite-medusa-application-configurations",
|
||||
Postgres: "https://docs.medusajs.com/cloud/database",
|
||||
Redis: "https://docs.medusajs.com/cloud/redis",
|
||||
S3: "https://docs.medusajs.com/cloud/s3",
|
||||
"Environment variables":
|
||||
"https://docs.medusajs.com/cloud/environments/environment-variables",
|
||||
"Data import/export":
|
||||
"https://docs.medusajs.com/cloud/database#importexport-database-dumps",
|
||||
Logs: "https://docs.medusajs.com/cloud/logs",
|
||||
"Multiple Long-Lived Environments":
|
||||
"https://docs.medusajs.com/cloud/environments/long-lived",
|
||||
"Cloud seats":
|
||||
"https://docs.medusajs.com/cloud/organizations#view-organization-members",
|
||||
}
|
||||
|
||||
const featureIcons: Record<string, React.FC> = {
|
||||
"Commerce features": Shopping,
|
||||
"Development Platform": CodePullRequest,
|
||||
"Hosting & Deployment": ServerStack,
|
||||
"Compute & Resources": WIP,
|
||||
"Organization & Billing": CurrencyDollar,
|
||||
}
|
||||
|
||||
// Helper function to render Block content (Sanity rich text)
|
||||
const renderBlockContent = (blocks: Block[]) => {
|
||||
if (!blocks || blocks.length === 0) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return blocks
|
||||
.map((block) => {
|
||||
if (block._type === "block" && block.children) {
|
||||
return block.children
|
||||
.map((child: Span | TooltipBlock) => {
|
||||
if (child._type === "span") {
|
||||
const key = child.text.trim()
|
||||
return featureLinks[key]
|
||||
? "[" + child.text + "](" + featureLinks[key] + ")"
|
||||
: child.text
|
||||
}
|
||||
return ""
|
||||
})
|
||||
.join(" \n")
|
||||
}
|
||||
return ""
|
||||
})
|
||||
.join(" \n")
|
||||
.replaceAll("-", "\\-")
|
||||
}
|
||||
|
||||
const FeatureSections: React.FC<FeatureSectionsProps> = ({
|
||||
featureSections,
|
||||
columnCount,
|
||||
columns,
|
||||
}) => {
|
||||
if (!featureSections || featureSections.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Calculate consistent column widths
|
||||
// Use fractional units to ensure all grids have matching column sizes
|
||||
const featureNameFraction = 2 // Feature name gets 2 units
|
||||
const featureColumnFraction = 1 // Each feature column gets 1 unit
|
||||
const gridTemplate = `${featureNameFraction}fr repeat(${columnCount}, ${featureColumnFraction}fr)`
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col rounded shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark">
|
||||
{/* Header */}
|
||||
<div
|
||||
className="w-full grid gap-0 rounded-t"
|
||||
style={{
|
||||
gridTemplateColumns: gridTemplate,
|
||||
}}
|
||||
>
|
||||
{/* Features label column */}
|
||||
<div className="flex items-center justify-start px-1.5 py-1 border-solid border-r border-medusa-border-base">
|
||||
<p className="txt-large text-medusa-fg-subtle">Features</p>
|
||||
</div>
|
||||
|
||||
{/* Column headers */}
|
||||
{columns.map((column, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={clsx(
|
||||
"flex items-center justify-center px-1 py-1 bg-medusa-bg-base",
|
||||
index !== columns.length - 1 &&
|
||||
"border-solid border-r border-medusa-border-base"
|
||||
)}
|
||||
>
|
||||
<p className="txt-large text-medusa-fg-base text-left w-full">
|
||||
{column}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{/* Feature Sections */}
|
||||
{featureSections.map((section) => (
|
||||
<div key={section._key} className="w-full">
|
||||
{/* Section Header */}
|
||||
<div className="w-full p-1.5 bg-medusa-bg-component flex gap-1 border-medusa-border-base border-y items-center">
|
||||
{featureIcons[section.header.subtitle] && (
|
||||
<BorderedIcon
|
||||
IconComponent={featureIcons[section.header.subtitle]}
|
||||
wrapperClassName="p-[7.5px] bg-medusa-bg-component rounded-[5px]"
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<H3
|
||||
id={slugify(section.header.subtitle, { lower: true })}
|
||||
className="!my-0"
|
||||
>
|
||||
{section.header.subtitle}
|
||||
</H3>
|
||||
{/* @ts-expect-error this is a React component */}
|
||||
<P className="text-medusa-fg-subtle">{section.header.title}</P>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section Rows */}
|
||||
<div className="w-full">
|
||||
{section.rows.map((row, index) => (
|
||||
<React.Fragment key={row._key}>
|
||||
<div
|
||||
className={clsx(
|
||||
"w-full grid gap-0 border-solid border-medusa-border-base",
|
||||
index !== section.rows.length - 1 && "border-b"
|
||||
)}
|
||||
style={{
|
||||
gridTemplateColumns: gridTemplate,
|
||||
}}
|
||||
>
|
||||
{/* Feature name column */}
|
||||
<div className="px-1 py-1 flex items-center justify-start border-solid border-r border-medusa-border-base">
|
||||
<p className="txt-medium-plus text-medusa-fg-base">
|
||||
<MarkdownContent
|
||||
allowedElements={["br", "a"]}
|
||||
unwrapDisallowed
|
||||
>
|
||||
{renderBlockContent(row.column1)}
|
||||
</MarkdownContent>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Feature value columns */}
|
||||
{Array.from({ length: columnCount }, (_, colIndex) => {
|
||||
const columnKey = `column${
|
||||
colIndex + 2
|
||||
}` as keyof typeof row
|
||||
const columnData = row[columnKey] as Block[]
|
||||
|
||||
return (
|
||||
<div
|
||||
key={colIndex}
|
||||
className={clsx(
|
||||
"px-1 py-1 flex items-center justify-center",
|
||||
colIndex !== columnCount - 1 &&
|
||||
"border-solid border-r border-medusa-border-base"
|
||||
)}
|
||||
>
|
||||
<p className="txt-medium text-medusa-fg-base text-left w-full">
|
||||
<MarkdownContent
|
||||
allowedElements={["br", "a"]}
|
||||
unwrapDisallowed
|
||||
>
|
||||
{renderBlockContent(columnData)}
|
||||
</MarkdownContent>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FeatureSections
|
||||
162
www/apps/cloud/components/Pricing/HeroPricing/index.tsx
Normal file
162
www/apps/cloud/components/Pricing/HeroPricing/index.tsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import React from "react"
|
||||
import { CheckCircleSolid } from "@medusajs/icons"
|
||||
import { HeroPricingFields } from "../../../utils/types"
|
||||
import { H3, Button } from "docs-ui"
|
||||
import clsx from "clsx"
|
||||
import slugify from "slugify"
|
||||
import Link from "next/link"
|
||||
|
||||
interface HeroPricingProps {
|
||||
data: HeroPricingFields
|
||||
}
|
||||
|
||||
const HeroPricing: React.FC<HeroPricingProps> = ({ data }) => {
|
||||
if (!data?.options) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative w-full rounded shadow-elevation-card-rest dark:shadow-elevation-card-rest-dark">
|
||||
{/* Header Row */}
|
||||
<div className="flex items-start justify-start">
|
||||
{/* Main content area */}
|
||||
<div className="w-full flex items-center justify-start">
|
||||
{data.options.map((option, index) => (
|
||||
<React.Fragment key={option._key}>
|
||||
<div
|
||||
className={clsx(
|
||||
`flex-1 min-w-0 p-1`,
|
||||
index !== data.options.length - 1 &&
|
||||
"border-solid border-r border-medusa-border-base",
|
||||
index === data.options.length - 1 &&
|
||||
"bg-medusa-bg-subtle rounded-tr"
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col w-full">
|
||||
<div className="flex flex-col items-start justify-start w-full">
|
||||
<div className="flex flex-col justify-end w-full">
|
||||
<H3
|
||||
className="!mb-0 txt-xlarge"
|
||||
id={slugify(option.title, {
|
||||
lower: true,
|
||||
})}
|
||||
>
|
||||
{option.title}
|
||||
</H3>
|
||||
</div>
|
||||
<div className="flex flex-col justify-end w-full">
|
||||
<p className="txt-xlarge text-medusa-fg-subtle">
|
||||
{option.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Features and Buttons Row */}
|
||||
<div
|
||||
className="w-full grid gap-0"
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${data.options.length}, 1fr)`,
|
||||
gridTemplateRows: "auto 1fr auto",
|
||||
}}
|
||||
>
|
||||
{/* Description row - all descriptions at same height */}
|
||||
{data.options.map((option, index) => (
|
||||
<div
|
||||
key={`description-${option._key}`}
|
||||
className={clsx(
|
||||
`p-1 flex items-start border-b border-medusa-border-base`,
|
||||
index !== data.options.length - 1 &&
|
||||
"border-solid border-r border-medusa-border-base",
|
||||
index === data.options.length - 1 && "bg-medusa-bg-subtle"
|
||||
)}
|
||||
>
|
||||
<div className="w-full flex items-start">
|
||||
{option.description && (
|
||||
<p className="text-medusa-fg-base txt-small-plus text-balance">
|
||||
{option.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Features row - all features sections at same level */}
|
||||
{data.options.map((option, index) => (
|
||||
<div
|
||||
key={`features-${option._key}`}
|
||||
className={clsx(
|
||||
`p-1 flex flex-col gap-0.75`,
|
||||
index !== data.options.length - 1 &&
|
||||
"border-solid border-r border-medusa-border-base",
|
||||
index === data.options.length - 1 && "bg-medusa-bg-subtle"
|
||||
)}
|
||||
>
|
||||
{option.pre_features && (
|
||||
<div className="flex items-center w-full">
|
||||
<span className="text-medusa-fg-base txt-small-plus">
|
||||
{option.pre_features}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{option.features.map((feature, featureIndex) => (
|
||||
<div
|
||||
key={featureIndex}
|
||||
className="flex gap-0.75 items-center w-full text-medusa-fg-base"
|
||||
>
|
||||
<div className="flex gap-0.5 items-center">
|
||||
<CheckCircleSolid />
|
||||
</div>
|
||||
<span className="txt-medium-plus">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* Buttons row - all buttons at same level */}
|
||||
{data.options.map((option, index) => (
|
||||
<div
|
||||
key={`buttons-${option._key}`}
|
||||
className={clsx(
|
||||
`p-1`,
|
||||
index !== data.options.length - 1 &&
|
||||
"border-solid border-r border-medusa-border-base",
|
||||
index === data.options.length - 1 &&
|
||||
"bg-medusa-bg-subtle rounded-br"
|
||||
)}
|
||||
>
|
||||
<div className="w-full">
|
||||
{option.buttons.map((button) => (
|
||||
<Link
|
||||
key={button._key}
|
||||
href={`https://medusajs.com${button.link.path}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button
|
||||
variant={
|
||||
button.variant === "primary" || button.variant === "dark"
|
||||
? "primary"
|
||||
: "secondary"
|
||||
}
|
||||
className="w-full txt-compact-xsmall-plus"
|
||||
>
|
||||
{button.link.label}
|
||||
</Button>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeroPricing
|
||||
@@ -4,7 +4,7 @@ export const generatedEditDates = {
|
||||
"app/projects/page.mdx": "2025-09-09T06:45:36.785Z",
|
||||
"app/environments/page.mdx": "2025-06-25T08:00:05.550Z",
|
||||
"app/deployments/page.mdx": "2025-06-25T07:57:13.059Z",
|
||||
"app/organizations/page.mdx": "2025-08-25T07:15:17.593Z",
|
||||
"app/organizations/page.mdx": "2025-09-11T14:26:31.848Z",
|
||||
"app/notifications/page.mdx": "2025-06-25T07:27:37.642Z",
|
||||
"app/database/page.mdx": "2025-08-15T15:30:37.814Z",
|
||||
"app/redis/page.mdx": "2025-06-25T07:57:23.246Z",
|
||||
@@ -16,8 +16,9 @@ export const generatedEditDates = {
|
||||
"app/environments/environment-variables/page.mdx": "2025-08-15T15:35:24.085Z",
|
||||
"app/environments/long-lived/page.mdx": "2025-08-25T07:23:59.858Z",
|
||||
"app/environments/preview/page.mdx": "2025-08-20T12:15:33.952Z",
|
||||
"app/faq/page.mdx": "2025-08-15T14:32:23.372Z",
|
||||
"app/faq/page.mdx": "2025-09-11T14:26:01.400Z",
|
||||
"app/billing/page.mdx": "2025-09-04T15:25:50.586Z",
|
||||
"app/usage/page.mdx": "2025-08-25T07:25:54.703Z",
|
||||
"app/billing/manage/page.mdx": "2025-09-04T14:50:46.747Z"
|
||||
"app/billing/manage/page.mdx": "2025-09-04T14:50:46.747Z",
|
||||
"app/pricing/page.mdx": "2025-09-05T10:31:59.059Z"
|
||||
}
|
||||
@@ -171,6 +171,14 @@ export const generatedSidebars = [
|
||||
"path": "/billing",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"title": "Plans & Pricing",
|
||||
"path": "/pricing",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@medusajs/icons": "2.10.1",
|
||||
"@next/mdx": "15.3.5",
|
||||
"@sanity/client": "^7.11.0",
|
||||
"@stefanprobst/rehype-extract-toc": "^3.0.0",
|
||||
"clsx": "^2.1.0",
|
||||
"docs-ui": "*",
|
||||
@@ -28,7 +29,8 @@
|
||||
"rehype-slug": "^6.0.0",
|
||||
"remark-directive": "^3.0.0",
|
||||
"remark-frontmatter": "^5.0.0",
|
||||
"remark-rehype-plugins": "*"
|
||||
"remark-rehype-plugins": "*",
|
||||
"slugify": "^1.6.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdx": "^2.0.13",
|
||||
|
||||
@@ -118,6 +118,11 @@ export const sidebar = [
|
||||
title: "Overview",
|
||||
path: "/billing",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
title: "Plans & Pricing",
|
||||
path: "/pricing",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
title: "Manage Billing Details",
|
||||
|
||||
8
www/apps/cloud/utils/sanity-client.ts
Normal file
8
www/apps/cloud/utils/sanity-client.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createClient } from "@sanity/client"
|
||||
|
||||
export const sanityClient = createClient({
|
||||
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID || "",
|
||||
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || "production",
|
||||
apiVersion: "2025-02-19",
|
||||
useCdn: false,
|
||||
})
|
||||
90
www/apps/cloud/utils/types.ts
Normal file
90
www/apps/cloud/utils/types.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
export type PricingQueryResult = Array<{
|
||||
_id: string
|
||||
_type: "heroPricing" | "featureTable"
|
||||
featureTableFields: FeatureTableFields | null
|
||||
heroPricingFields: HeroPricingFields | null
|
||||
}>
|
||||
|
||||
export type HeroPricingFields = {
|
||||
options: Array<{
|
||||
_key: string
|
||||
_type: "option"
|
||||
buttons: Array<{
|
||||
_key: string
|
||||
_type: "button"
|
||||
link: {
|
||||
_type: "link"
|
||||
label: string
|
||||
path: string
|
||||
type: string
|
||||
}
|
||||
size: string
|
||||
variant: string
|
||||
}>
|
||||
description: string
|
||||
features: string[]
|
||||
subtitle: string
|
||||
title: string
|
||||
pre_features?: string
|
||||
titleLine2?: string
|
||||
}>
|
||||
}
|
||||
|
||||
export type FeatureTableFields = {
|
||||
columnHeaders: string[]
|
||||
featureSections: Array<{
|
||||
_key: string
|
||||
header: {
|
||||
_type: "sectionHeader"
|
||||
icon: {
|
||||
_type: "image"
|
||||
asset: {
|
||||
_ref: string
|
||||
_type: "reference"
|
||||
}
|
||||
}
|
||||
subtitle: string
|
||||
title: string
|
||||
}
|
||||
rows: Array<{
|
||||
_key: string
|
||||
column1: Block[]
|
||||
column2: Block[]
|
||||
column3: Block[]
|
||||
column4: Block[]
|
||||
}>
|
||||
}>
|
||||
links: Array<{
|
||||
_key: string
|
||||
_type: "button"
|
||||
link: {
|
||||
_type: "link"
|
||||
label: string
|
||||
path: string
|
||||
type: string
|
||||
}
|
||||
size: string
|
||||
variant: string
|
||||
}>
|
||||
}
|
||||
|
||||
export type Block = {
|
||||
_key: string
|
||||
_type: "block"
|
||||
children: Array<Span | TooltipBlock>
|
||||
markDefs: any[]
|
||||
style: string
|
||||
}
|
||||
|
||||
export type Span = {
|
||||
_key: string
|
||||
_type: "span"
|
||||
marks: string[]
|
||||
text: string
|
||||
}
|
||||
|
||||
export type TooltipBlock = {
|
||||
_key: string
|
||||
_type: "tooltipBlock"
|
||||
text: string
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import plugin from "tailwindcss/plugin"
|
||||
import presets from "./theme-presets"
|
||||
const plugin = require("tailwindcss/plugin")
|
||||
const presets = require("./theme-presets")
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This config file includes the base configurations with the modified configs for UI docs
|
||||
import coreConfig from "./base.tailwind.config"
|
||||
const coreConfig = require("./base.tailwind.config")
|
||||
|
||||
// modify core spacing to have "docs" prefix
|
||||
const modifiedSpacing = {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from "path"
|
||||
import coreConfig from "./modified.tailwind.config"
|
||||
const path = require("path")
|
||||
const coreConfig = require("./modified.tailwind.config")
|
||||
|
||||
const root = path.join(require.resolve("docs-ui"), "../..")
|
||||
const files = path.join(root, "**/*.{js,ts,jsx,tsx,mdx}")
|
||||
|
||||
177
www/yarn.lock
177
www/yarn.lock
@@ -4392,6 +4392,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sanity/client@npm:^7.11.0":
|
||||
version: 7.11.0
|
||||
resolution: "@sanity/client@npm:7.11.0"
|
||||
dependencies:
|
||||
"@sanity/eventsource": ^5.0.2
|
||||
get-it: ^8.6.9
|
||||
nanoid: ^3.3.11
|
||||
rxjs: ^7.0.0
|
||||
checksum: a171aa5248838096ea55a6cd04038d8f662fa1eb06f3653a0f58d47f26beff9740f896693e2b06ba0feb1f4eaccbb0abf03aa5547162e958e1d0e7077c6c31d4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@sanity/eventsource@npm:^5.0.2":
|
||||
version: 5.0.2
|
||||
resolution: "@sanity/eventsource@npm:5.0.2"
|
||||
dependencies:
|
||||
"@types/event-source-polyfill": 1.0.5
|
||||
"@types/eventsource": 1.1.15
|
||||
event-source-polyfill: 1.0.31
|
||||
eventsource: 2.0.2
|
||||
checksum: 7896e5bf1afd1811db22479ea6c529bf2042c2ef1d17fede26b5eafba839235cff35a53fdb68fa53b90975dba993f884d13a262ca9a729e9d97020f958743cee
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@segment/analytics-core@npm:1.8.0":
|
||||
version: 1.8.0
|
||||
resolution: "@segment/analytics-core@npm:1.8.0"
|
||||
@@ -4727,6 +4751,29 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/event-source-polyfill@npm:1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "@types/event-source-polyfill@npm:1.0.5"
|
||||
checksum: 62f06c58312097c17f8489771057bb9e859269243a815ef27c8b50b48e084412fce4f90cb18252f807ed9016028321ce2713c90b77e77abc9e84a21bce5db724
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/eventsource@npm:1.1.15":
|
||||
version: 1.1.15
|
||||
resolution: "@types/eventsource@npm:1.1.15"
|
||||
checksum: afee5c6c1185e365802349878411324569c77ae9878317b6e34bd633bdb5ee0a6eada6d1b358fec57640fe610eb43535344076db199895e1d9ab81511bb6ed0e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/follow-redirects@npm:^1.14.4":
|
||||
version: 1.14.4
|
||||
resolution: "@types/follow-redirects@npm:1.14.4"
|
||||
dependencies:
|
||||
"@types/node": "*"
|
||||
checksum: 5e0d09e6c9a8bee09b1af9e1fce80fcc2e22f082d786b2f25aa5ccb3be996cf8b9ba866024e17817e01e961586aa2aad13c38c6c3a0dabbe8654d4b47d07977c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/google.maps@npm:^3.55.12":
|
||||
version: 3.55.12
|
||||
resolution: "@types/google.maps@npm:3.55.12"
|
||||
@@ -6011,6 +6058,7 @@ __metadata:
|
||||
"@mdx-js/react": ^3.1.0
|
||||
"@medusajs/icons": 2.10.1
|
||||
"@next/mdx": 15.3.5
|
||||
"@sanity/client": ^7.11.0
|
||||
"@stefanprobst/rehype-extract-toc": ^3.0.0
|
||||
"@types/mdx": ^2.0.13
|
||||
"@types/node": ^20
|
||||
@@ -6032,6 +6080,7 @@ __metadata:
|
||||
remark-directive: ^3.0.0
|
||||
remark-frontmatter: ^5.0.0
|
||||
remark-rehype-plugins: "*"
|
||||
slugify: ^1.6.6
|
||||
tailwind: "*"
|
||||
tailwindcss: ^3.3.0
|
||||
tsconfig: "*"
|
||||
@@ -6818,6 +6867,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decompress-response@npm:^7.0.0":
|
||||
version: 7.0.0
|
||||
resolution: "decompress-response@npm:7.0.0"
|
||||
dependencies:
|
||||
mimic-response: ^3.1.0
|
||||
checksum: 925d5a34c9ae2c5026748feff4cbf1310b044be31618b188f1fe9f4ddfda4e013ebeeb0a7ec6d251095542ed3b3b879dac168e54cc65e5a82fb88ea0b287a788
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"deep-is@npm:^0.1.3":
|
||||
version: 0.1.4
|
||||
resolution: "deep-is@npm:0.1.4"
|
||||
@@ -8242,6 +8300,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"event-source-polyfill@npm:1.0.31":
|
||||
version: 1.0.31
|
||||
resolution: "event-source-polyfill@npm:1.0.31"
|
||||
checksum: 79966f5084796e14f9a9dec315a2ccc220dedc51ff5f2b198dc80e3cb2ae01428d39d9bf66ed679f1944be086b9f6e84ea3dc933b81b0411c07f99672135679b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource@npm:2.0.2":
|
||||
version: 2.0.2
|
||||
resolution: "eventsource@npm:2.0.2"
|
||||
checksum: 0b8c70b35e45dd20f22ff64b001be9d530e33b92ca8bdbac9e004d0be00d957ab02ef33c917315f59bf2f20b178c56af85c52029bc8e6cc2d61c31d87d943573
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"execa@npm:^5.0.0":
|
||||
version: 5.1.1
|
||||
resolution: "execa@npm:5.1.1"
|
||||
@@ -8402,6 +8474,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.15.9":
|
||||
version: 1.15.11
|
||||
resolution: "follow-redirects@npm:1.15.11"
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
checksum: d301f430542520a54058d4aeeb453233c564aaccac835d29d15e050beb33f339ad67d9bddbce01739c5dc46a6716dbe3d9d0d5134b1ca203effa11a7ef092343
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"for-each@npm:^0.3.3":
|
||||
version: 0.3.3
|
||||
resolution: "for-each@npm:0.3.3"
|
||||
@@ -8597,6 +8679,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-it@npm:^8.6.9":
|
||||
version: 8.6.10
|
||||
resolution: "get-it@npm:8.6.10"
|
||||
dependencies:
|
||||
"@types/follow-redirects": ^1.14.4
|
||||
decompress-response: ^7.0.0
|
||||
follow-redirects: ^1.15.9
|
||||
is-retry-allowed: ^2.2.0
|
||||
through2: ^4.0.2
|
||||
tunnel-agent: ^0.6.0
|
||||
checksum: b0fcb42fdd3e07c329f633bc09e555d7c39847b837397d4e4ca4b01c66447bcd4e5003630d03622782afc49cbb9ac8d352e87143501a9bad1747c8198b1f5074
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-nonce@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "get-nonce@npm:1.0.1"
|
||||
@@ -9142,7 +9238,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"inherits@npm:^2.0.4":
|
||||
"inherits@npm:^2.0.3, inherits@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "inherits@npm:2.0.4"
|
||||
checksum: 4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
||||
@@ -9632,6 +9728,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-retry-allowed@npm:^2.2.0":
|
||||
version: 2.2.0
|
||||
resolution: "is-retry-allowed@npm:2.2.0"
|
||||
checksum: 013be4f8a0a06a49ed1fe495242952e898325d496202a018f6f9fb3fb9ac8fe3b957a9bd62463d68299ae35dbbda680473c85a9bcefca731b49d500d3ccc08ff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-set@npm:^2.0.1":
|
||||
version: 2.0.2
|
||||
resolution: "is-set@npm:2.0.2"
|
||||
@@ -11182,6 +11285,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mimic-response@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "mimic-response@npm:3.1.0"
|
||||
checksum: 0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"min-indent@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "min-indent@npm:1.0.1"
|
||||
@@ -11385,6 +11495,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.11":
|
||||
version: 3.3.11
|
||||
resolution: "nanoid@npm:3.3.11"
|
||||
bin:
|
||||
nanoid: bin/nanoid.cjs
|
||||
checksum: 40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nanoid@npm:^3.3.6, nanoid@npm:^3.3.7":
|
||||
version: 3.3.7
|
||||
resolution: "nanoid@npm:3.3.7"
|
||||
@@ -12682,6 +12801,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readable-stream@npm:3":
|
||||
version: 3.6.2
|
||||
resolution: "readable-stream@npm:3.6.2"
|
||||
dependencies:
|
||||
inherits: ^2.0.3
|
||||
string_decoder: ^1.1.1
|
||||
util-deprecate: ^1.0.1
|
||||
checksum: e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"readdirp@npm:~3.6.0":
|
||||
version: 3.6.0
|
||||
resolution: "readdirp@npm:3.6.0"
|
||||
@@ -13158,6 +13288,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"rxjs@npm:^7.0.0":
|
||||
version: 7.8.2
|
||||
resolution: "rxjs@npm:7.8.2"
|
||||
dependencies:
|
||||
tslib: ^2.1.0
|
||||
checksum: 1fcd33d2066ada98ba8f21fcbbcaee9f0b271de1d38dc7f4e256bfbc6ffcdde68c8bfb69093de7eeb46f24b1fb820620bf0223706cff26b4ab99a7ff7b2e2c45
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sade@npm:^1.7.3":
|
||||
version: 1.8.1
|
||||
resolution: "sade@npm:1.8.1"
|
||||
@@ -13204,6 +13343,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
|
||||
version: 5.2.1
|
||||
resolution: "safe-buffer@npm:5.2.1"
|
||||
checksum: 6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"safe-push-apply@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "safe-push-apply@npm:1.0.0"
|
||||
@@ -13839,6 +13985,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"string_decoder@npm:^1.1.1":
|
||||
version: 1.3.0
|
||||
resolution: "string_decoder@npm:1.3.0"
|
||||
dependencies:
|
||||
safe-buffer: ~5.2.0
|
||||
checksum: 810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stringify-entities@npm:^4.0.0":
|
||||
version: 4.0.3
|
||||
resolution: "stringify-entities@npm:4.0.3"
|
||||
@@ -14190,6 +14345,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"through2@npm:^4.0.2":
|
||||
version: 4.0.2
|
||||
resolution: "through2@npm:4.0.2"
|
||||
dependencies:
|
||||
readable-stream: 3
|
||||
checksum: 3741564ae99990a4a79097fe7a4152c22348adc4faf2df9199a07a66c81ed2011da39f631e479fdc56483996a9d34a037ad64e76d79f18c782ab178ea9b6778c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tldts-core@npm:^7.0.9":
|
||||
version: 7.0.9
|
||||
resolution: "tldts-core@npm:7.0.9"
|
||||
@@ -14459,6 +14623,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tunnel-agent@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "tunnel-agent@npm:0.6.0"
|
||||
dependencies:
|
||||
safe-buffer: ^5.0.1
|
||||
checksum: 4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"turbo-darwin-64@npm:1.12.4":
|
||||
version: 1.12.4
|
||||
resolution: "turbo-darwin-64@npm:1.12.4"
|
||||
@@ -15137,7 +15310,7 @@ turbo@latest:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"util-deprecate@npm:^1.0.2":
|
||||
"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "util-deprecate@npm:1.0.2"
|
||||
checksum: 41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942
|
||||
|
||||
Reference in New Issue
Block a user