diff --git a/pages/applications/[repo].tsx b/pages/applications/[repo].tsx index adfccb24..f11f4a21 100644 --- a/pages/applications/[repo].tsx +++ b/pages/applications/[repo].tsx @@ -18,6 +18,7 @@ import { type MergeDeep } from 'type-fest' // import { ProductValueSection } from '@pages/plural-stacks/ProductValueSection' import client, { directusClient } from '@src/apollo-client' import { mqs } from '@src/breakpoints' +import { BasicMarkdoc } from '@src/components/BasicMarkdoc' import Embed from '@src/components/Embed' import { FooterVariant } from '@src/components/FooterFull' import { Columns, EqualColumn } from '@src/components/layout/Columns' @@ -107,16 +108,18 @@ const AppPageTitle = styled( return (
- - +
+ +
+
+ +
{app.displayName}
@@ -126,15 +129,15 @@ const AppPageTitle = styled( display: 'flex', alignItems: 'center', gap: theme.spacing.small, - '.icon > *:nth-child(2)': { + '.icon > .largeIcon': { display: 'none', }, [mqs.md]: { '.icon': { - '& > *:nth-child(1)': { + '& > .smallIcon': { display: 'none', }, - '& > *:nth-child(2)': { + '& > .largeIcon': { display: 'flex', }, }, @@ -150,6 +153,9 @@ export type ProviderProps = { export default function App({ repo, heroVideo, + heroText, + secondaryTitle, + secondaryText, caseStudy, quotes, recipes, @@ -190,10 +196,18 @@ export default function App({ - - {repo.description} - {/* Orchestrate all your applications to work in harmony with{' '} - {repo.displayName} on Plural. */} + + {heroText ? ( + + ) : ( +

{repo.description}

+ )}
Available providers @@ -236,17 +250,17 @@ export default function App({ className="mb-large" as="h2" > - Why use {repo.displayName} on Plural? + - - You’re likely spending time weighing the benefits of - self-hosting with the convenience and cost of managed - services. Skip the pro-con discussions and get the best of - both worlds with Plural. Automate and orchestrate your ETL, ML - jobs, and DevOps tasks without taking on the Ops burden or - managed service cost. Especially if you’re handling PII data, - you’ll need everything to stay within your own VPC, which is - best done with self-hosting open-source. + +
@@ -258,9 +272,10 @@ export default function App({
Deploying {repo.displayName} is a matter of executing these - 2 commands: + 3 commands: + plural build {`plural deploy --commit "deploying ${repo.name}"`} @@ -350,6 +365,18 @@ export type AppPageProps = { ReturnType['heroVideo'], undefined > + heroText: Exclude< + ReturnType['hero_text'], + undefined + > + secondaryTitle: Exclude< + ReturnType['secondary_title'], + undefined + > + secondaryText: Exclude< + ReturnType['secondary_text'], + undefined + > caseStudy: Exclude< ReturnType['case_study'], undefined @@ -364,7 +391,9 @@ export type AppPageProps = { const normalizeAppExtras = (extras: AppExtrasQuery) => ({ ...(extras.app_defaults || {}), - ...(extras.apps?.[0] || {}), + ...Object.fromEntries( + Object.entries(extras.apps?.[0] || {}).filter(([_, val]) => !!val) + ), } as MergeDeep) export const getStaticProps: GetStaticProps = async (context) => { @@ -431,6 +460,9 @@ export const getStaticProps: GetStaticProps = async (context) => { : null, caseStudy: appExtras.case_study || null, heroVideo: appExtras.heroVideo || null, + heroText: appExtras.hero_text || null, + secondaryText: appExtras.secondary_text || null, + secondaryTitle: appExtras.secondary_title || null, quotes: normalizeQuotes(appExtras.quotes), recipes, ...getAppMeta(thisRepo), diff --git a/public/images/product/architecture.png b/public/images/product/architecture.png index 3a7f3488..b56d9982 100644 Binary files a/public/images/product/architecture.png and b/public/images/product/architecture.png differ diff --git a/public/images/product/lifecycle.png b/public/images/product/lifecycle.png new file mode 100644 index 00000000..1dd660d0 Binary files /dev/null and b/public/images/product/lifecycle.png differ diff --git a/src/components/BasicMarkdoc.tsx b/src/components/BasicMarkdoc.tsx new file mode 100644 index 00000000..f20f976c --- /dev/null +++ b/src/components/BasicMarkdoc.tsx @@ -0,0 +1,66 @@ +import React, { useMemo } from 'react' + +import { InlineCode, isExternalUrl } from '@pluralsh/design-system' +import Link from 'next/link' + +import Markdoc, { nodes } from '@markdoc/markdoc' + +import { BasicP, BasicUl, InlineLink } from './Typography' + +const transformConfig = ({ renderP = true }: { renderP?: boolean }) => ({ + nodes: { + document: { ...nodes.document, render: 'NoWrapper' }, + list: { ...nodes.list, render: 'List' }, + paragraph: { ...nodes.paragraph, render: renderP ? 'P' : 'NoWrapper' }, + link: { ...nodes.link, render: 'Link' }, + code: { ...nodes.code, render: 'Code' }, + }, +}) + +const renderOpts = { + components: { + // eslint-disable-next-line react/jsx-no-useless-fragment + NoWrapper: ({ children }) => <>{children}, + List: BasicUl, + P: (props) => , + Link: (props) => ( + + ), + Code: InlineCode, + }, +} + +export function BasicMarkdoc({ + text, + variables, + renderP = true, +}: { + text?: string | null + variables?: Record + renderP?: boolean +}) { + text = text?.replace(/^\s+|\s+$/g, '') + const content = useMemo(() => { + if (!text) { + return null + } + const ast = Markdoc.parse(text) + + return Markdoc.transform(ast, { + ...transformConfig({ renderP }), + variables, + }) + }, [renderP, text, variables]) + + if (!content) { + return null + } + + return <>{Markdoc.renderers.react(content, React, renderOpts)} +} diff --git a/src/components/FooterNav.tsx b/src/components/FooterNav.tsx index bd5292bb..d96bc31d 100644 --- a/src/components/FooterNav.tsx +++ b/src/components/FooterNav.tsx @@ -26,7 +26,7 @@ const navItems = [ }, { children: 'GitHub', - href: 'https://github.com/plural.sh', + href: 'https://github.com/pluralsh/plural', target: '_blank', }, { diff --git a/src/components/QuoteCards.tsx b/src/components/QuoteCards.tsx index 242298d8..bb9a5b0d 100644 --- a/src/components/QuoteCards.tsx +++ b/src/components/QuoteCards.tsx @@ -246,7 +246,7 @@ export function TestimonialsSection({ return ( - What companies are saying about Plural + Developers love us diff --git a/src/components/page-sections/FAQList.tsx b/src/components/page-sections/FAQList.tsx index 46949784..2a1ebe84 100644 --- a/src/components/page-sections/FAQList.tsx +++ b/src/components/page-sections/FAQList.tsx @@ -12,7 +12,10 @@ export function FAQList({ faqs }: { faqs: (FaqItemFragment | null)[] }) {
{faqs.map((faq) => faq ? ( - + ) : null diff --git a/src/components/page-sections/WhatIsPluralSection.tsx b/src/components/page-sections/WhatIsPluralSection.tsx index 4f7be6ff..9f4aa7d9 100644 --- a/src/components/page-sections/WhatIsPluralSection.tsx +++ b/src/components/page-sections/WhatIsPluralSection.tsx @@ -99,21 +99,32 @@ export function WhatIsPluralSection() { Explore the docs -
+
+
+ +
) } diff --git a/src/data/getPricing.tsx b/src/data/getPricing.tsx index 991123f2..7269819f 100644 --- a/src/data/getPricing.tsx +++ b/src/data/getPricing.tsx @@ -49,7 +49,6 @@ const proPlanBase: Plan = { { label: 'Multi-cluster management' }, { label: 'Dev → Prod promotion flows' }, { label: 'Advanced user management' }, - { label: 'Backup/Restore' }, { label: 'Audit logs' }, { label: 'VPN' }, { label: 'Emergency hotfixes' }, diff --git a/src/generated/graphqlDirectus.ts b/src/generated/graphqlDirectus.ts index f74fd0b5..d8ba668c 100644 --- a/src/generated/graphqlDirectus.ts +++ b/src/generated/graphqlDirectus.ts @@ -2582,16 +2582,16 @@ export type SiteSettingsQuery = { __typename?: 'Query', site_settings?: { __type export type CaseStudyFragment = { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null }; -export type AppExtrasFragment = { __typename?: 'apps', name?: string | null, heroVideo?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null }; +export type AppExtrasFragment = { __typename?: 'apps', name?: string | null, heroVideo?: string | null, hero_text?: string | null, secondary_title?: string | null, secondary_text?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null }; -export type AppDefaultsFragment = { __typename?: 'app_defaults', case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, quotes?: { __typename?: 'quote_lists', slug: string, items?: Array<{ __typename?: 'quote_lists_items', item?: { __typename?: 'quotes', id: string, quote?: string | null, name?: string | null, title?: string | null, company?: string | null, portrait?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null, logo?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null } | null> | null } | null }; +export type AppDefaultsFragment = { __typename?: 'app_defaults', secondary_title?: string | null, secondary_text?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, quotes?: { __typename?: 'quote_lists', slug: string, items?: Array<{ __typename?: 'quote_lists_items', item?: { __typename?: 'quotes', id: string, quote?: string | null, name?: string | null, title?: string | null, company?: string | null, portrait?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null, logo?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null } | null> | null } | null }; export type AppExtrasQueryVariables = Exact<{ name?: InputMaybe; }>; -export type AppExtrasQuery = { __typename?: 'Query', apps: Array<{ __typename?: 'apps', name?: string | null, heroVideo?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null }>, app_defaults?: { __typename?: 'app_defaults', case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, quotes?: { __typename?: 'quote_lists', slug: string, items?: Array<{ __typename?: 'quote_lists_items', item?: { __typename?: 'quotes', id: string, quote?: string | null, name?: string | null, title?: string | null, company?: string | null, portrait?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null, logo?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null } | null> | null } | null } | null }; +export type AppExtrasQuery = { __typename?: 'Query', apps: Array<{ __typename?: 'apps', name?: string | null, heroVideo?: string | null, hero_text?: string | null, secondary_title?: string | null, secondary_text?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null }>, app_defaults?: { __typename?: 'app_defaults', secondary_title?: string | null, secondary_text?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null, quotes?: { __typename?: 'quote_lists', slug: string, items?: Array<{ __typename?: 'quote_lists_items', item?: { __typename?: 'quotes', id: string, quote?: string | null, name?: string | null, title?: string | null, company?: string | null, portrait?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null, logo?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null } | null> | null } | null } | null }; export type StackExtrasFragment = { __typename?: 'stacks', name?: string | null, heroVideo?: string | null, case_study?: { __typename?: 'case_studies', id: string, slug?: string | null, label?: string | null, title?: string | null, content?: string | null, ctas?: any | null, stack_label?: string | null, stack_apps?: any | null, hero_image?: { __typename?: 'directus_files', id: string, title?: string | null, description?: string | null, tags?: any | null, filename_disk?: string | null, filename_download: string, metadata?: any | null, type?: string | null, filesize?: any | null } | null } | null }; @@ -2831,6 +2831,9 @@ export const AppExtrasFragmentDoc = gql` fragment AppExtras on apps { name heroVideo + hero_text + secondary_title + secondary_text case_study { ...CaseStudy } @@ -2863,6 +2866,8 @@ export const QuoteListFragmentDoc = gql` ${QuoteFragmentDoc}`; export const AppDefaultsFragmentDoc = gql` fragment AppDefaults on app_defaults { + secondary_title + secondary_text case_study { ...CaseStudy } diff --git a/src/generated/graphqlPlural.ts b/src/generated/graphqlPlural.ts index 34218833..57b69c06 100644 --- a/src/generated/graphqlPlural.ts +++ b/src/generated/graphqlPlural.ts @@ -415,6 +415,8 @@ export type Cluster = { /** The ID of the cluster. */ id: Scalars['ID']['output']; insertedAt?: Maybe; + /** whether any installation in the cluster has been locked */ + locked?: Maybe; /** The name of the cluster. */ name: Scalars['String']['output']; /** The user that owns the cluster. */ @@ -427,6 +429,8 @@ export type Cluster = { queue?: Maybe; /** The source of the cluster. */ source?: Maybe; + /** whether all installations in the cluster have been synced */ + synced?: Maybe; updatedAt?: Maybe; /** pending upgrades for each installed app */ upgradeInfo?: Maybe>>; diff --git a/src/graph/directus/cms.graphql b/src/graph/directus/cms.graphql index 4117473e..013c714f 100644 --- a/src/graph/directus/cms.graphql +++ b/src/graph/directus/cms.graphql @@ -109,11 +109,16 @@ fragment CaseStudy on case_studies { fragment AppExtras on apps { name heroVideo + hero_text + secondary_title + secondary_text case_study { ...CaseStudy } } fragment AppDefaults on app_defaults { + secondary_title + secondary_text case_study { ...CaseStudy }