diff --git a/codegen.ts b/codegen.ts index 11b0c67a..60a4ad28 100644 --- a/codegen.ts +++ b/codegen.ts @@ -50,6 +50,23 @@ const config: CodegenConfig = { }, ], }, + 'src/generated/graphqlDirectusSystem.ts': { + schema: `https://directus.plural.sh/graphql/system${ + directusToken ? `?access_token=${directusToken}` : '' + }`, + documents: './src/graph/directus/system/*.graphql', + plugins: [ + 'typescript', + 'typescript-operations', + 'typescript-react-apollo', + { + add: { + content: '/* eslint-disable */\n// prettier-ignore', + config: null, + }, + }, + ], + }, }, hooks: { afterAllFileWrite: ['eslint --fix'], diff --git a/package.json b/package.json index 3288ac46..6b020ae2 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@markdoc/markdoc": "0.3.0", "@markdoc/next.js": "0.2.2", "@open-draft/until": "2.1.0", - "@pluralsh/design-system": "1.344.1", + "@pluralsh/design-system": "2.0.0", "@react-types/shared": "3.18.1", "@tanstack/react-table": "8.9.2", "@tanstack/react-virtual": "3.0.0-beta.48", diff --git a/pages/404.tsx b/pages/404.tsx index 4cb4f2c3..897ab990 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -1,13 +1,13 @@ import Link from '@pluralsh/design-system/dist/markdoc/components/Link' -import { propsWithGlobalSettings } from '@src/components/getGlobalProps' +import { FullPage } from '@src/components/layout/FullPage' +import { HeaderPad } from '@src/components/layout/HeaderPad' import { Body1, Heading1 } from '@src/components/Typography' - -import { FullPage } from './_app' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' export default function Plural404() { return ( - +
Page not found diff --git a/pages/500.tsx b/pages/500.tsx index 959b98ac..97d27b44 100644 --- a/pages/500.tsx +++ b/pages/500.tsx @@ -1,6 +1,6 @@ import Error from 'next/error' -import { propsWithGlobalSettings } from '@src/components/getGlobalProps' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' export default function Plural500({ statusCode }) { return diff --git a/pages/_app.tsx b/pages/_app.tsx index e1f950c3..3b59732b 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -16,17 +16,16 @@ import { useRouter } from 'next/router' import { MarkdocContextProvider } from '@pluralsh/design-system/dist/markdoc' import { SSRProvider } from 'react-aria' -import styled, { ThemeProvider as StyledThemeProvider } from 'styled-components' +import { ThemeProvider as StyledThemeProvider } from 'styled-components' import { SWRConfig } from 'swr' import { BreakpointProvider } from '@src/components/BreakpointProvider' import DocSearchStyles from '@src/components/DocSearchStyles' import { type FooterVariant } from '@src/components/FooterFull' -import { type GlobalProps } from '@src/components/getGlobalProps' import GlobalStyles from '@src/components/GlobalStyles' import { usePosthog } from '@src/components/hooks/usePosthog' -import { PageMaxWidthLimiter } from '@src/components/MaxWidthLimiter' import PrimaryPage from '@src/components/PrimaryPage' +import { type GlobalProps } from '@src/utils/getGlobalProps' // Styles import '@src/styles/globals.css' @@ -50,10 +49,6 @@ export type MarkdocHeading = { const docsStyledTheme = { ...styledTheme, ...{ docs: { topNavHeight: 72 } } } -export const FullPage = styled(PageMaxWidthLimiter)((_) => ({ - width: '100%', -})) - const useNavigate = () => { const router = useRouter() diff --git a/pages/applications/[repo].tsx b/pages/applications/[repo].tsx index e5664d49..ca78ef81 100644 --- a/pages/applications/[repo].tsx +++ b/pages/applications/[repo].tsx @@ -5,11 +5,9 @@ import { BrowserIcon, Button, CertificateIcon, - CheckRoundedIcon, Code, DocumentIcon, GitHubIcon, - Tooltip, } from '@pluralsh/design-system' import { type GetStaticPaths, @@ -24,13 +22,15 @@ import classNames from 'classnames' import { isEmpty } from 'lodash-es' import styled, { useTheme } from 'styled-components' -import { FullPage } from '@pages/_app' import client, { directusClient } from '@src/apollo-client' import { mqs } from '@src/breakpoints' import BuildStack, { getStackTabData } from '@src/components/BuildStack' +import { Checklist, ChecklistItem } from '@src/components/Checklist' import Embed from '@src/components/Embed' import { FooterVariant } from '@src/components/FooterFull' -import { propsWithGlobalSettings } from '@src/components/getGlobalProps' +import { Col, Columns2 } from '@src/components/layout/Columns' +import { FullPage } from '@src/components/layout/FullPage' +import { TextLimiter } from '@src/components/layout/TextLimiter' import { BackButton } from '@src/components/Nav' import { QuotesCarousel } from '@src/components/QuoteCards' import RepoReadmeMd from '@src/components/RepoReadme/RepoReadmeMd' @@ -66,9 +66,12 @@ import { type RecipesQuery, type RecipesQueryVariables, } from '@src/generated/graphqlPlural' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' import { CompanyLogos } from '../../src/components/CompanyLogos' -import { HeaderPad } from '../../src/components/GradientBGs' +import { GradientBG } from '../../src/components/layout/GradientBG' +import { HeaderPad } from '../../src/components/layout/HeaderPad' +import { ProviderIcon } from '../../src/components/ProviderIcon' const DEFAULT_HERO_VIDEO = 'https://www.youtube.com/watch?v=mFDA-718RhI' @@ -125,46 +128,12 @@ const AppPageTitle = styled( }, })) -type ProviderProps = { +export type ProviderProps = { label?: string | null | undefined iconDark: string iconLight: string } -const GradientBG = styled( - ({ children, position: _position, image: _image, ...props }) => ( -
-
-
{children}
-
- ) -)<{ position?: string; image?: string }>( - ({ - theme, - position = 'top center', - image = '/images/gradients/gradient-bg-1.jpg', - }) => ({ - position: 'relative', - '.bg': { - content: '""', - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - backgroundImage: `url(${image})`, - backgroundPosition: position, - backgroundSize: 'contain', - backgroundRepeat: 'no-repeat', - backgroundColor: theme.colors['fill-zero'], - filter: 'blur(10px)', - }, - '.content': { - position: 'relative', - }, - }) -) - export default function App({ repo, appExtras, @@ -399,68 +368,6 @@ export default function App({ ) } -export const TextLimiter = styled.div(({ theme: _ }) => ({ - maxWidth: 600, - - [mqs.columns]: { - maxWidth: 600, - }, -})) - -export function Col({ className, ...props }: ComponentProps<'div'>) { - return ( -
- ) -} - -export function Columns2({ className, ...props }: ComponentProps<'div'>) { - return ( -
- ) -} - -export const Checklist = styled.ul(({ theme }) => ({ - display: 'flex', - flexDirection: 'column', - gap: theme.spacing.medium, -})) - -export const ChecklistItem = styled(({ children, ...props }) => { - const theme = useTheme() - - return ( -
  • - - {children} -
  • - ) -})(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - gap: theme.spacing.small, - ...theme.partials.marketingText.body1Bold, - color: theme.colors.text, -})) - export const getStaticPaths: GetStaticPaths = async () => { if (process.env.NODE_ENV === 'development') { return { @@ -510,8 +417,6 @@ export const getStaticProps: GetStaticProps = async (context) => { variables: { name: repoName }, }) - console.log(appData?.apps?.[0] || null) - const { data: recipesData, error: recipesError } = await client.query< RecipesQuery, RecipesQueryVariables @@ -552,44 +457,3 @@ export const getStaticProps: GetStaticProps = async (context) => { ], }) } - -const ProviderIcon = styled( - ({ - iconLight, - iconDark, - label, - ...props - }: ComponentProps<'div'> & ProviderProps) => ( - -
    - - -
    -
    - ) -)(({ theme }) => ({ - border: theme.borders['fill-two'], - background: theme.colors['fill-zero'], - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - padding: theme.spacing.xsmall, - borderRadius: theme.borderRadiuses.large, - width: 44, - height: 44, - '.iconLight': { - display: theme.mode === 'light' ? 'block' : 'none', - }, - '.iconDark': { - display: theme.mode === 'light' ? 'none' : 'block', - }, -})) diff --git a/pages/community.tsx b/pages/community.tsx index f55a9656..f4a18fe3 100644 --- a/pages/community.tsx +++ b/pages/community.tsx @@ -1,12 +1,12 @@ import { useRouter } from 'next/router' import { directusClient } from '@src/apollo-client' -import { propsWithGlobalSettings } from '@src/components/getGlobalProps' import { EventsDocument, type EventsQuery, type EventsQueryVariables, } from '@src/generated/graphqlDirectus' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' export default function Community({ events }) { const locale = useRouter().locale || 'en-us' diff --git a/pages/index.tsx b/pages/index.tsx index c99b0d70..bb261955 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,11 +1,12 @@ -import { propsWithGlobalSettings } from '@src/components/getGlobalProps' +import { HeaderPad } from '@src/components/layout/HeaderPad' import { Body1, Heading1 } from '@src/components/Typography' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' -import { FullPage } from './_app' +import { FullPage } from '../src/components/layout/FullPage' export default function Index() { return ( - +
    Home page This is some body text diff --git a/pages/marketplace.tsx b/pages/marketplace.tsx index c46a3179..dd9c9274 100644 --- a/pages/marketplace.tsx +++ b/pages/marketplace.tsx @@ -21,16 +21,12 @@ import { isEmpty, orderBy, upperFirst } from 'lodash-es' import styled, { useTheme } from 'styled-components' import { mqs } from '@src/breakpoints' -import { - type GlobalProps, - propsWithGlobalSettings, -} from '@src/components/getGlobalProps' -import { HeaderPad } from '@src/components/GradientBGs' +import { MarketplacePage } from '@src/components/layout/BasicPage' +import { HeaderPad } from '@src/components/layout/HeaderPad' import { MarketplaceCarousel } from '@src/components/MarketplaceCarousel' import { MarketplaceExtras } from '@src/components/MarketplaceExtras' import MarketplaceFilters from '@src/components/MarketplaceFilters' import StackHero, { Cta } from '@src/components/MarketplaceStackHero' -import { MarketplacePage } from '@src/components/PageGrid' import { RepoCard, RepoCardList, StackCard } from '@src/components/RepoCardList' import { type MinRepo, getRepos, reposCache } from '@src/data/getRepos' import { @@ -40,11 +36,16 @@ import { } from '@src/data/getSearchMetadata' import { type MinStack, getStacks, stacksCache } from '@src/data/getStacks' import { type MinRepoFragment } from '@src/generated/graphqlPlural' +import { + type GlobalProps, + propsWithGlobalSettings, +} from '@src/utils/getGlobalProps' import { type SetSearchParams, useSearchParams, } from '../src/components/hooks/useSearchParams' +import { FullPage } from '../src/components/layout/FullPage' import { MarketSearchTabKey, SearchBar, @@ -52,8 +53,6 @@ import { } from '../src/components/MarketplaceSearchBar' import { Body1, Heading1, Subtitle } from '../src/components/Typography' -import { FullPage } from './_app' - type PageProps = { repositories: MinRepo[] stacks: MinStack[] diff --git a/pages/plural-stacks/[stack].tsx b/pages/plural-stacks/[stack].tsx new file mode 100644 index 00000000..3a34df2b --- /dev/null +++ b/pages/plural-stacks/[stack].tsx @@ -0,0 +1,363 @@ +import { useEffect, useRef, useState } from 'react' + +import { Button, TabList, TabPanel } from '@pluralsh/design-system' +import { + type GetStaticPaths, + type GetStaticProps, + type InferGetStaticPropsType, +} from 'next' +import { useRouter } from 'next/router' + +import { until } from '@open-draft/until' +import { providerToProviderName } from '@pluralsh/design-system/dist/markdoc/utils/text' +import classNames from 'classnames' +import { isEmpty } from 'lodash-es' +import styled from 'styled-components' + +import { directusClient } from '@src/apollo-client' +// import { mqs } from '@src/breakpoints' +import BuildStack, { + AppCard, + getStackTabData, +} from '@src/components/BuildStack' +import { Checklist, ChecklistItem } from '@src/components/Checklist' +import Embed from '@src/components/Embed' +import { FooterVariant } from '@src/components/FooterFull' +import { FullPage } from '@src/components/layout/FullPage' +import { GradientBG } from '@src/components/layout/GradientBG' +import { BackButton } from '@src/components/Nav' +import { ProviderIcon } from '@src/components/ProviderIcon' +import { QuotesCarousel } from '@src/components/QuoteCards' +import { + AppTitle, + Body1, + Body2, + Cta, + Heading1, + Overline, + ResponsiveText, + Title2, +} from '@src/components/Typography' +import { getProviderIcon, getStackMeta } from '@src/consts' +import { getRepos, normalizeRepo } from '@src/data/getRepos' +import { type FullStack, getFullStack, getStacks } from '@src/data/getStacks' +import { + StackExtrasDocument, + type StackExtrasFragment, + type StackExtrasQuery, + type StackExtrasQueryVariables, +} from '@src/generated/graphqlDirectus' +import { + type MinRepoFragment, + type StackCollectionFragment, +} from '@src/generated/graphqlPlural' +import { propsWithGlobalSettings } from '@src/utils/getGlobalProps' +import { startsWithVowel } from '@src/utils/text' + +import { CompanyLogos } from '../../src/components/CompanyLogos' +import { Col, Columns2 } from '../../src/components/layout/Columns' +import { HeaderPad } from '../../src/components/layout/HeaderPad' +import { TextLimiter } from '../../src/components/layout/TextLimiter' + +const DEFAULT_HERO_VIDEO = 'https://www.youtube.com/watch?v=LOUshNTgPV0' + +function isCollection( + collection: StackCollectionFragment | null | undefined +): collection is StackCollectionFragment { + return !!collection +} + +const StackAppsTabList = styled(TabList)(({ theme }) => ({ + flexDirection: 'column', + border: theme.borders['fill-two'], + padding: theme.spacing.xsmall, + rowGap: theme.spacing.xsmall, + borderRadius: theme.borderRadiuses.medium, +})) + +const StackAppsTabPanel = styled(TabPanel)((_) => ({})) + +export default function Stack({ + stack, + stackExtras, + buildStackTabs, +}: InferGetStaticPropsType) { + const router = useRouter() + const providers = + stack?.collections?.filter(isCollection).map((collection) => ({ + key: collection.id, + label: + providerToProviderName[collection?.provider?.toUpperCase() || ''] || + collection.provider, + // language: 'shell', + // content: `plural stack install ${stack?.name}`, + iconLight: getProviderIcon({ + provider: collection?.provider, + mode: 'light', + }), + iconDark: getProviderIcon({ + provider: collection?.provider, + mode: 'dark', + }), + })) || [] + + const apps = stack?.collections?.[0]?.bundles + ?.map((bundle) => bundle?.recipe.repository) + .filter((repo): repo is MinRepoFragment => !!repo) + .map((repo) => normalizeRepo(repo)) + + const appsTabStateRef = useRef() + const [curAppTabKey, setCurTabKey] = useState(apps?.[0].name ?? '') + // const curApp = apps?.find((app) => app.name === curTabKey) + + useEffect(() => { + if (!stack) { + // router.push('/marketplace') + } + }, [stack, router]) + if (!stack) { + return null + } + console.log('curTabKey', curAppTabKey) + + return ( + + +
    + +
    + + + + + Build {startsWithVowel(stack.displayName) ? 'an' : 'a'}{' '} + {stack.displayName} Stack with Plural + + + {stack.description} + {/* Orchestrate all your applications to work in harmony with{' '} + {repo.displayName} on Plural. */} + +
    + Available providers + {!isEmpty(providers) && ( +
    + {providers.map((provider) => ( + + ))} +
    + )} +
    +
    + +
    +
    + + + + +
    +
    + + + + The stack + + + + + + + setCurTabKey(key as string), + }} + > + {apps?.map((repo) => { + console.log('repo', repo.name) + + if (!repo || !repo.name) { + return null + } + + return ( + + {repo.name} + + ) + })} + + + + + + Read the install documentation + + + + +
    +
    + +
    + + + + Open-source and free to use + + Plural automates the deployment and operation of{' '} + {stack.displayName} in your cloud. Get up and running with + your {stack.displayName} instance in minutes and let Plural + deploy {stack.displayName} and all its dependencies into your + cloud with all of the day-2 operations handled out of the box. + + + Explore {stack.displayName} on Plural in live demo environment + + + + + + Automated upgrades + + Transparent pricing and cost management{' '} + + Prebuilt dashboards, extendable + Prebuilt runbooks, extendable + Log management + + + +
    + Screenshots of the Plural Console app, showing dashboards for Applications, Nodes and cost +
    +
    +
    + {buildStackTabs && } + + + + +
    + + What companies are saying about Plural + + +
    +
    + {/* */} +
    + ) +} + +export const getStaticPaths: GetStaticPaths = async () => { + if (process.env.NODE_ENV === 'development') { + return { + paths: [], + fallback: 'blocking', + } + } + + const repos = (await getRepos()) || [] + + return { + paths: repos.map((repo) => ({ params: { repo: repo?.name } })), + fallback: true, + } +} + +export type StackPageProps = { + stack?: FullStack | null + stackExtras?: StackExtrasFragment + buildStackTabs?: ReturnType +} + +export const getStaticProps: GetStaticProps = async ( + context +) => { + const stackName = context?.params?.stack + + if (!stackName || typeof stackName !== 'string') { + return { notFound: true } + } + const { data: repos, error: reposError } = await until(() => getRepos()) + + // const { data: repo, error: repoError } = await until(() => + // getFullRepo(`${repoName}`) + // ) + + const { data: stacks, error: stacksError } = await until(() => getStacks()) + const { data: stack, error: stackError } = await until(() => + getFullStack(stackName) + ) + + const thisStack = stack // stacks?.find((r) => r.name === stackName) + + if (!thisStack || !stackName || typeof stackName !== 'string') { + return { notFound: true } + } + + const { data: stackData, error: appError } = await directusClient.query< + StackExtrasQuery, + StackExtrasQueryVariables + >({ + query: StackExtrasDocument, + variables: { name: stackName }, + }) + + const buildStackTabs = getStackTabData({ repos, stacks }) + + return propsWithGlobalSettings({ + stack: thisStack + ? { + ...thisStack, + } + : null, + stackExtras: stackData?.stacks?.[0] || {}, + ...getStackMeta(thisStack), + footerVariant: FooterVariant.kitchenSink, + buildStackTabs, + errors: [ + ...(reposError ? [reposError] : []), + ...(stacksError ? [stacksError] : []), + ...(stackError ? [stackError] : []), + ...(appError ? [appError] : []), + ], + }) +} diff --git a/src/components/BuildStack.tsx b/src/components/BuildStack.tsx index 31f17ba1..3209b2bc 100644 --- a/src/components/BuildStack.tsx +++ b/src/components/BuildStack.tsx @@ -15,10 +15,10 @@ import Link from 'next/link' import styled from 'styled-components' -import { FullPage } from '@pages/_app' import { mqs } from '@src/breakpoints' -import { type getRepos } from '@src/data/getRepos' -import { type getStacks } from '@src/data/getStacks' +import { FullPage } from '@src/components/layout/FullPage' +import { type MinRepo, type getRepos } from '@src/data/getRepos' +import { type MinStack, type getStacks } from '@src/data/getStacks' import { Cta, ResponsiveText } from './Typography' @@ -75,6 +75,83 @@ export const getStackTabData = ({ }, ] +function StackCard({ + stack, + size = 'medium', + ...props +}: { + stack: MinStack + size?: ComponentProps['$size'] +} & ComponentProps) { + return ( + +
    +
    {stack.displayName}
    +
    + } + > + Stack + +
    +
    +
    + {(stack.collections?.[0]?.bundles || []).map((b) => { + const repo = b?.recipe.repository + + return ( + + ) + })} +
    +
    + ) +} + +export const AppCard = forwardRef( + ( + { + app, + size = 'medium', + active = false, + ...props + }: { + active?: boolean + app: MinRepo + size?: ComponentProps['$size'] + } & ComponentProps, + ref + ) => ( + +
    + +
    +
    {app.displayName}
    +
    {app.category}
    +
    +
    +
    + ) +) + export default function BuildStack({ tabs, }: { @@ -122,39 +199,18 @@ export default function BuildStack({ className="grid gap-large grid-cols-1 md:grid-cols-3" > {curTab?.stacks?.map((stack) => ( - -
    -
    {stack.displayName}
    -
    - } - > - Stack - -
    -
    -
    - {(stack.collections?.[0]?.bundles || []).map((b) => { - const repo = b?.recipe.repository - - return ( - - ) - })} -
    -
    + /> ))} {curTab?.apps?.map((app) => ( - {app.category}
    - + ))} ( - ({ theme, $variant = 'app' }) => ({ +const AppOrStackCard = styled.div<{ + $variant: 'app' | 'stack' + $size: 'medium' | 'small' + $active: 'boolean' +}>(({ theme, $variant = 'app', $active = false, $size = 'medium' }) => ({ + display: 'flex', + rowGap: theme.spacing.xxsmall, + flexDirection: 'column', + justifyContent: 'center', + padding: $size === 'small' ? theme.spacing.small : theme.spacing.large, + paddingBottom: $variant === 'stack' ? theme.spacing.small : undefined, + '&, &:focus-visible': { + backgroundColor: $active + ? theme.colors['fill-one-selected'] + : theme.colors['fill-one'], + }, + + color: theme.colors.text, + borderRadius: theme.borderRadiuses.large, + ...(theme.mode === 'light' + ? { + boxShadow: `0px 1px 3px rgba(74, 81, 242, 0.04), 0px 2px 10px rgba(74, 81, 242, 0.04)`, + '&:hover': { + boxShadow: `0px 2px 7px 1px rgba(74, 81, 242, 0.1), 0px 2px 10px rgba(74, 81, 242, 0.08)`, + }, + } + : { + '&:hover': { + backgroundColor: $active + ? theme.colors['fill-one-selected'] + : theme.colors['fill-one-hover'], + }, + }), + '.stackTitleBox, .appBox': { + display: 'flex', + gap: theme.spacing.small, + }, + '.stackApps': { + display: 'flex', + gap: theme.spacing.xxsmall, + }, + '.appTitleBox': { display: 'flex', - rowGap: theme.spacing.xxsmall, flexDirection: 'column', - justifyContent: 'center', - padding: theme.spacing.large, - paddingBottom: $variant === 'stack' ? theme.spacing.small : undefined, - backgroundColor: theme.colors['fill-one'], + gap: 0, + overflow: 'hidden', + }, + '.title': { + ...theme.partials.text.title2, color: theme.colors.text, - borderRadius: theme.borderRadiuses.large, - boxShadow: `0px 1px 3px rgba(74, 81, 242, 0.04), 0px 2px 10px rgba(74, 81, 242, 0.04)`, - '&:hover': { - boxShadow: `0px 2px 7px 1px rgba(74, 81, 242, 0.1), 0px 2px 10px rgba(74, 81, 242, 0.08)`, - }, - '.stackTitleBox, .appBox': { - display: 'flex', - gap: theme.spacing.small, - }, - '.stackApps': { - display: 'flex', - gap: theme.spacing.xxsmall, - }, - '.appTitleBox': { - display: 'flex', - flexDirection: 'column', - gap: 0, - overflow: 'hidden', - }, - '.title': { - ...theme.partials.text.title2, - color: theme.colors.text, - overflow: 'hidden', - flexShrink: 1, - flexGrow: 1, - textOverflow: 'ellipsis', - }, - '.category': { - ...theme.partials.text.caption, - color: theme.colors['text-light'], - }, - [mqs.columns]: {}, - }) -) + overflow: 'hidden', + flexShrink: 1, + flexGrow: 1, + textOverflow: 'ellipsis', + }, + '.category': { + ...theme.partials.text.caption, + color: theme.colors['text-light'], + }, + [mqs.columns]: {}, +})) const StackTabList = styled(TabList)<{ $active: boolean }>(({ theme }) => ({ // '&&': { @@ -250,9 +323,11 @@ const StackTabBase = styled(Button)<{ $active: boolean }>( '&&, &&:focus-visible': { ...($active ? { - backgroundColor: theme.colors['action-link-inline-visited'], - border: `1px solid ${theme.colors['action-link-inline-visited']}`, - color: theme.colors.grey[50], + '&, &:hover': { + backgroundColor: theme.colors['action-link-inline-visited'], + border: `1px solid ${theme.colors['action-link-inline-visited']}`, + color: theme.colors.grey[50], + }, } : {}), [mqs.sm]: { @@ -266,6 +341,7 @@ const StackTabBase = styled(Button)<{ $active: boolean }>( }, }) ) + const StackTab = forwardRef( ( { active, children, textValue: _textValue, ...props }: StackTabProps, diff --git a/src/components/Checklist.tsx b/src/components/Checklist.tsx new file mode 100644 index 00000000..e6ebba6f --- /dev/null +++ b/src/components/Checklist.tsx @@ -0,0 +1,29 @@ +import { CheckRoundedIcon } from '@pluralsh/design-system' + +import styled, { useTheme } from 'styled-components' + +export const Checklist = styled.ul(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing.medium, +})) + +export const ChecklistItem = styled(({ children, ...props }) => { + const theme = useTheme() + + return ( +
  • + + {children} +
  • + ) +})(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing.small, + ...theme.partials.marketingText.body1Bold, + color: theme.colors.text, +})) diff --git a/src/components/CompanyLogos.tsx b/src/components/CompanyLogos.tsx index 2d7045fc..bd03d5c5 100644 --- a/src/components/CompanyLogos.tsx +++ b/src/components/CompanyLogos.tsx @@ -47,7 +47,7 @@ export const CompanyLogos = styled(({ ...props }) => (
      {partnerLogos.map((logo) => ( -
      +
      {logo.name}) => ( @@ -103,14 +104,14 @@ function ValueProp({ textStyles={{ '': 'mSubtitle1' }} className="self-center mt-[-0.15em]" > - {title} + {title} - {children} + {children}
      ) diff --git a/src/components/GlobalStyles.tsx b/src/components/GlobalStyles.tsx index 75e92620..aaf8af9e 100644 --- a/src/components/GlobalStyles.tsx +++ b/src/components/GlobalStyles.tsx @@ -39,7 +39,7 @@ const GlobalStyles = createGlobalStyle(({ theme }) => boxShadow: 'none', }, 'a:any-link': { - color: 'unset', + // color: 'unset', textDecoration: 'unset', }, html: { diff --git a/src/components/GradientBGs.tsx b/src/components/GradientBGs.tsx index e7559023..57f53b1d 100644 --- a/src/components/GradientBGs.tsx +++ b/src/components/GradientBGs.tsx @@ -4,10 +4,6 @@ import styled from 'styled-components' import { breakpoints, mqs } from '@src/breakpoints' -export const HeaderPad = styled.div(() => ({ - paddingTop: `var(--top-nav-height)`, -})) - export const GradientBG = styled.div(() => ({ background: 'url(/images/gradients/gradient-blue-1.png)', backgroundSize: 'cover', diff --git a/src/components/PrimaryPage.tsx b/src/components/PrimaryPage.tsx index 275828fc..1c389b10 100644 --- a/src/components/PrimaryPage.tsx +++ b/src/components/PrimaryPage.tsx @@ -4,9 +4,10 @@ import { type GlobalPageProps } from '@pages/_app' import { PAGE_TITLE_PREFIX, PAGE_TITLE_SUFFIX, ROOT_TITLE } from '@src/consts' import { NavDataProvider } from '@src/contexts/NavDataContext' +import { type GlobalProps } from '../utils/getGlobalProps' + import ExternalScripts from './ExternalScripts' import { FullFooter } from './FooterFull' -import { type GlobalProps } from './getGlobalProps' import HtmlHead from './HtmlHead' import { PageHeader } from './PageHeader' import { PagePropsContext } from './PagePropsContext' diff --git a/src/components/ProviderIcon.tsx b/src/components/ProviderIcon.tsx new file mode 100644 index 00000000..09943ad8 --- /dev/null +++ b/src/components/ProviderIcon.tsx @@ -0,0 +1,48 @@ +import { type ComponentProps } from 'react' + +import { Tooltip } from '@pluralsh/design-system' + +import styled from 'styled-components' + +import { type ProviderProps } from '../../pages/applications/[repo]' + +export const ProviderIcon = styled( + ({ + iconLight, + iconDark, + label, + ...props + }: ComponentProps<'div'> & ProviderProps) => ( + +
      + + +
      +
      + ) +)(({ theme }) => ({ + border: theme.borders['fill-two'], + background: theme.colors['fill-zero'], + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + padding: theme.spacing.xsmall, + borderRadius: theme.borderRadiuses.large, + width: 44, + height: 44, + '.iconLight': { + display: theme.mode === 'light' ? 'block' : 'none', + }, + '.iconDark': { + display: theme.mode === 'light' ? 'none' : 'block', + }, +})) diff --git a/src/components/RepoReadme/RepoReadmeMd.tsx b/src/components/RepoReadme/RepoReadmeMd.tsx index 44ccb94d..a51375b1 100644 --- a/src/components/RepoReadme/RepoReadmeMd.tsx +++ b/src/components/RepoReadme/RepoReadmeMd.tsx @@ -1,7 +1,6 @@ import { Children, memo } from 'react' import { - A, Blockquote, Code, Div, @@ -23,6 +22,12 @@ import styled from 'styled-components' import MultilineCode from './Code' +const MdA = styled.a(({ theme }) => ({ + '&, &:any-link': { + ...theme.partials.text.inlineLink, + }, +})) + const MdImg = styled(({ src, gitUrl, mainBranch, ...props }) => { // Convert local image paths to full path on github // Only works if primary git branch is named "master" @@ -215,12 +220,11 @@ export default memo( props: { body2: true, marginBottom: 'medium' }, }), a: toReactMarkdownComponent({ - component: A, + component: MdA, props: { - inline: true, - display: 'inline', + // inline: true, + // display: 'inline', target: '_blank', - // display: 'inline', color: 'text-light', size: 'small', target: '_blank', }, }), span: toReactMarkdownComponent({ diff --git a/src/components/Typography.tsx b/src/components/Typography.tsx index 2f153772..fb88b967 100644 --- a/src/components/Typography.tsx +++ b/src/components/Typography.tsx @@ -6,7 +6,7 @@ import { useNavigationContext, } from '@pluralsh/design-system' -import { isEmpty, lowerFirst } from 'lodash-es' +import { lowerFirst } from 'lodash-es' import styled, { type DefaultTheme } from 'styled-components' import { type PascalCase } from 'type-fest' @@ -47,7 +47,7 @@ const textPropFilter = { const APP_PREFIX = 'a' const MARKETING_PREFIX = 'm' -function getTextStylesFromString(theme: DefaultTheme, styleName: string) { +function getStylesFromShortname(theme: DefaultTheme, styleName: string) { let prefix = APP_PREFIX let textStyles: Record = theme.partials.text @@ -66,11 +66,9 @@ export const ResponsiveText = styled.h2.withConfig(textPropFilter)<{ }>(({ theme, color, textStyles: styles }) => { const parts = Object.fromEntries( Object.entries(styles || {}) - .map(([breakpoint, styleName]) => { - if (!breakpoint || !mqs[breakpoint]) { - return [] - } - const textStyles = getTextStylesFromString(theme, styleName) + .filter(([breakpoint]) => !!breakpoint && !!mqs[breakpoint]) + .map(([breakpoint, shortname]) => { + const textStyles = getStylesFromShortname(theme, shortname) return [ [mqs[breakpoint]], @@ -79,13 +77,14 @@ export const ResponsiveText = styled.h2.withConfig(textPropFilter)<{ }, ] }) - .filter((val) => !isEmpty(val)) ) return { - ...(styles?.[''] ? getTextStylesFromString(theme, styles['']) || {} : {}), + ...(styles?.[''] ? getStylesFromShortname(theme, styles['']) || {} : {}), ...parts, - ...(color ? { color: theme.colors[color] } : { color: theme.colors.text }), + ...(color + ? { color: theme.colors[color] || 'red' } + : { color: theme.colors.text }), } }) diff --git a/src/components/PageGrid.tsx b/src/components/layout/BasicPage.tsx similarity index 100% rename from src/components/PageGrid.tsx rename to src/components/layout/BasicPage.tsx diff --git a/src/components/layout/Columns.tsx b/src/components/layout/Columns.tsx new file mode 100644 index 00000000..26457584 --- /dev/null +++ b/src/components/layout/Columns.tsx @@ -0,0 +1,31 @@ +import { type ComponentProps } from 'react' + +import classNames from 'classnames' + +export function Col({ className, ...props }: ComponentProps<'div'>) { + return ( +
      + ) +} + +export function Columns2({ className, ...props }: ComponentProps<'div'>) { + return ( +
      + ) +} diff --git a/src/components/layout/FullPage.tsx b/src/components/layout/FullPage.tsx new file mode 100644 index 00000000..91e03da1 --- /dev/null +++ b/src/components/layout/FullPage.tsx @@ -0,0 +1,7 @@ +import styled from 'styled-components' + +import { PageMaxWidthLimiter } from '@src/components/MaxWidthLimiter' + +export const FullPage = styled(PageMaxWidthLimiter)((_) => ({ + width: '100%', +})) diff --git a/src/components/layout/GradientBG.tsx b/src/components/layout/GradientBG.tsx new file mode 100644 index 00000000..e503cefa --- /dev/null +++ b/src/components/layout/GradientBG.tsx @@ -0,0 +1,35 @@ +import styled from 'styled-components' + +export const GradientBG = styled( + ({ children, position: _position, image: _image, ...props }) => ( +
      +
      +
      {children}
      +
      + ) +)<{ position?: string; image?: string }>( + ({ + theme, + position = 'top center', + image = '/images/gradients/gradient-bg-1.jpg', + }) => ({ + position: 'relative', + '.bg': { + content: '""', + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundImage: `url(${image})`, + backgroundPosition: position, + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundColor: theme.colors['fill-zero'], + filter: 'blur(10px)', + }, + '.content': { + position: 'relative', + }, + }) +) diff --git a/src/components/layout/HeaderPad.tsx b/src/components/layout/HeaderPad.tsx new file mode 100644 index 00000000..f5a3288f --- /dev/null +++ b/src/components/layout/HeaderPad.tsx @@ -0,0 +1,5 @@ +import styled from 'styled-components' + +export const HeaderPad = styled.div(() => ({ + paddingTop: `var(--top-nav-height)`, +})) diff --git a/src/components/layout/TextLimiter.tsx b/src/components/layout/TextLimiter.tsx new file mode 100644 index 00000000..3f1c64fb --- /dev/null +++ b/src/components/layout/TextLimiter.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components' + +import { mqs } from '@src/breakpoints' + +export const TextLimiter = styled.div(({ theme: _ }) => ({ + maxWidth: 600, + + [mqs.columns]: { + maxWidth: 600, + }, +})) diff --git a/src/consts.ts b/src/consts.ts index 338d6290..ff1de6c7 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -1,6 +1,7 @@ import { type GlobalPageProps } from '@pages/_app' import { type MinRepo } from './data/getRepos' +import { type MinStack } from './data/getStacks' export const ROOT_TITLE = 'Plural | Deploy, maintain, and scale the open-source applications you love' @@ -18,6 +19,17 @@ export const getAppMeta = (repo: MinRepo): GlobalPageProps => { } } +export const getStackMeta = (stack: MinStack): GlobalPageProps => { + const displayName = stack.displayName || stack.name + + if (!displayName) return {} + + return { + metaTitleFull: `Deploying ${displayName} stack on Kubernetes`, + metaDescription: `Use Plural to deploy and manage the ${displayName} stack on Kubernetes, in your cloud.`, + } +} + export const REVALIDATE_TIME = 600 const PROVIDER_ICON_DIR = '/images/providers' diff --git a/src/data/getRepos.tsx b/src/data/getRepos.tsx index 7c564d82..60a171bc 100644 --- a/src/data/getRepos.tsx +++ b/src/data/getRepos.tsx @@ -71,7 +71,7 @@ function inRemoveList(repoName?: string) { return !!REMOVE_LIST.find((name) => name === repoName) } -function normalizeRepo< +export function normalizeRepo< T extends SetOptional> >(repo: T) { const { recipes, ...props } = repo diff --git a/src/data/getStacks.tsx b/src/data/getStacks.tsx index bb31bbf6..877c37df 100644 --- a/src/data/getStacks.tsx +++ b/src/data/getStacks.tsx @@ -1,3 +1,4 @@ +import { upperFirst } from 'lodash-es' import memoizeOne from 'memoize-one' import { filterMapNodes } from '@src/utils/graphql' @@ -35,16 +36,15 @@ function inRemoveList(stackName?: string) { } function fakeDisplayName(name?: string) { - return name || '' + return upperFirst(name) || '' } -function normalizeStack(stack: T) { - return stack - +function normalizeStack(stack: T) { return { ...stack, displayName: - ((stack as any).displayName as string) || fakeDisplayName(stack?.name), + ((stack as any).displayName as string) || + fakeDisplayName(stack?.name || ''), } } @@ -81,7 +81,7 @@ export async function getStacks(): Promise { const fullStackCache: Record = {} -export async function getStack(repoName: string): Promise { +export async function getFullStack(repoName: string): Promise { const { data, error } = await client.query({ query: StackDocument, variables: { name: repoName }, diff --git a/src/generated/graphqlDirectus.ts b/src/generated/graphqlDirectus.ts index 7d660e6f..d3a570ca 100644 --- a/src/generated/graphqlDirectus.ts +++ b/src/generated/graphqlDirectus.ts @@ -12,7 +12,7 @@ export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | const defaultOptions = {} as const; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: { input: string | number; output: string; } + ID: { input: string; output: string; } String: { input: string; output: string; } Boolean: { input: boolean; output: boolean; } Int: { input: number; output: number; } @@ -37,6 +37,9 @@ export type Query = { nav_list_aggregated: Array; nav_list_by_id?: Maybe; site_settings?: Maybe; + stacks: Array; + stacks_aggregated: Array; + stacks_by_id?: Maybe; }; @@ -143,13 +146,39 @@ export type QueryNav_List_By_IdArgs = { id: Scalars['ID']['input']; }; + +export type QueryStacksArgs = { + filter?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + + +export type QueryStacks_AggregatedArgs = { + filter?: InputMaybe; + groupBy?: InputMaybe>>; + limit?: InputMaybe; + offset?: InputMaybe; + page?: InputMaybe; + search?: InputMaybe; + sort?: InputMaybe>>; +}; + + +export type QueryStacks_By_IdArgs = { + id: Scalars['ID']['input']; +}; + export type Apps = { __typename?: 'apps'; date_updated?: Maybe; date_updated_func?: Maybe; heroVideo?: Maybe; id: Scalars['ID']['output']; - name?: Maybe; + name: Scalars['String']['output']; user_updated?: Maybe; }; @@ -480,6 +509,55 @@ export type Site_SettingsMain_NavArgs = { sort?: InputMaybe>>; }; +export type Stacks = { + __typename?: 'stacks'; + date_updated?: Maybe; + date_updated_func?: Maybe; + heroVideo?: Maybe; + id: Scalars['ID']['output']; + name?: Maybe; + user_updated?: Maybe; +}; + +export type Stacks_Aggregated = { + __typename?: 'stacks_aggregated'; + avg?: Maybe; + avgDistinct?: Maybe; + count?: Maybe; + countAll?: Maybe; + countDistinct?: Maybe; + group?: Maybe; + max?: Maybe; + min?: Maybe; + sum?: Maybe; + sumDistinct?: Maybe; +}; + +export type Stacks_Aggregated_Count = { + __typename?: 'stacks_aggregated_count'; + date_updated?: Maybe; + heroVideo?: Maybe; + id?: Maybe; + name?: Maybe; + user_updated?: Maybe; +}; + +export type Stacks_Aggregated_Fields = { + __typename?: 'stacks_aggregated_fields'; + id?: Maybe; +}; + +export type Stacks_Filter = { + _and?: InputMaybe>>; + _or?: InputMaybe>>; + date_updated?: InputMaybe; + date_updated_func?: InputMaybe; + heroVideo?: InputMaybe; + id?: InputMaybe; + name?: InputMaybe; + user_updated?: InputMaybe; +}; + export type String_Filter_Operators = { _contains?: InputMaybe; _empty?: InputMaybe; @@ -518,14 +596,23 @@ export type SiteSettingsQueryVariables = Exact<{ [key: string]: never; }>; export type SiteSettingsQuery = { __typename?: 'Query', site_settings?: { __typename?: 'site_settings', og_description?: string | null, main_nav?: { __typename?: 'nav_list', id: string, flatten?: boolean | null, mobile_only?: boolean | null, subnav?: Array<{ __typename?: 'nav_list', id: string, flatten?: boolean | null, mobile_only?: boolean | null, subnav?: Array<{ __typename?: 'nav_list', id: string, flatten?: boolean | null, mobile_only?: boolean | null, link?: { __typename?: 'nav_link', id: string, title?: string | null, url?: string | null } | null } | null> | null, link?: { __typename?: 'nav_link', id: string, title?: string | null, url?: string | null } | null } | null> | null, link?: { __typename?: 'nav_link', id: string, title?: string | null, url?: string | null } | null } | null } | null }; -export type AppExtrasFragment = { __typename?: 'apps', name?: string | null, heroVideo?: string | null }; +export type AppExtrasFragment = { __typename?: 'apps', name: string, heroVideo?: string | null }; export type AppExtrasQueryVariables = Exact<{ name?: InputMaybe; }>; -export type AppExtrasQuery = { __typename?: 'Query', apps: Array<{ __typename?: 'apps', name?: string | null, heroVideo?: string | null }> }; +export type AppExtrasQuery = { __typename?: 'Query', apps: Array<{ __typename?: 'apps', name: string, heroVideo?: string | null }> }; + +export type StackExtrasFragment = { __typename?: 'stacks', name?: string | null, heroVideo?: string | null }; + +export type StackExtrasQueryVariables = Exact<{ + name?: InputMaybe; +}>; + + +export type StackExtrasQuery = { __typename?: 'Query', stacks: Array<{ __typename?: 'stacks', name?: string | null, heroVideo?: string | null }> }; export const EventFragmentDoc = gql` fragment Event on events { @@ -578,6 +665,12 @@ export const AppExtrasFragmentDoc = gql` heroVideo } `; +export const StackExtrasFragmentDoc = gql` + fragment StackExtras on stacks { + name + heroVideo +} + `; export const EventsDocument = gql` query Events { events { @@ -680,4 +773,39 @@ export function useAppExtrasLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions< } export type AppExtrasQueryHookResult = ReturnType; export type AppExtrasLazyQueryHookResult = ReturnType; -export type AppExtrasQueryResult = Apollo.QueryResult; \ No newline at end of file +export type AppExtrasQueryResult = Apollo.QueryResult; +export const StackExtrasDocument = gql` + query StackExtras($name: String) { + stacks(filter: {name: {_eq: $name}}) { + ...StackExtras + } +} + ${StackExtrasFragmentDoc}`; + +/** + * __useStackExtrasQuery__ + * + * To run a query within a React component, call `useStackExtrasQuery` and pass it any options that fit your needs. + * When your component renders, `useStackExtrasQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useStackExtrasQuery({ + * variables: { + * name: // value for 'name' + * }, + * }); + */ +export function useStackExtrasQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(StackExtrasDocument, options); + } +export function useStackExtrasLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(StackExtrasDocument, options); + } +export type StackExtrasQueryHookResult = ReturnType; +export type StackExtrasLazyQueryHookResult = ReturnType; +export type StackExtrasQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/src/generated/graphqlDirectusSystem.ts b/src/generated/graphqlDirectusSystem.ts new file mode 100644 index 00000000..d4292eca --- /dev/null +++ b/src/generated/graphqlDirectusSystem.ts @@ -0,0 +1,298 @@ +/* eslint-disable */ +// prettier-ignore +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +const defaultOptions = {} as const; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + GraphQLBigInt: { input: any; output: any; } + JSON: { input: any; output: any; } + Void: { input: any; output: any; } +}; + +export type Mutation = { + __typename?: 'Mutation'; + auth_login?: Maybe; + auth_logout?: Maybe; + auth_password_request?: Maybe; + auth_password_reset?: Maybe; + auth_refresh?: Maybe; + users_invite_accept?: Maybe; + users_me_tfa_disable?: Maybe; + users_me_tfa_enable?: Maybe; + users_me_tfa_generate?: Maybe; + utils_cache_clear?: Maybe; + utils_hash_generate?: Maybe; + utils_hash_verify?: Maybe; + utils_random_string?: Maybe; + utils_revert?: Maybe; + utils_sort?: Maybe; +}; + + +export type MutationAuth_LoginArgs = { + email: Scalars['String']['input']; + mode?: InputMaybe; + otp?: InputMaybe; + password: Scalars['String']['input']; +}; + + +export type MutationAuth_LogoutArgs = { + refresh_token?: InputMaybe; +}; + + +export type MutationAuth_Password_RequestArgs = { + email: Scalars['String']['input']; + reset_url?: InputMaybe; +}; + + +export type MutationAuth_Password_ResetArgs = { + password: Scalars['String']['input']; + token: Scalars['String']['input']; +}; + + +export type MutationAuth_RefreshArgs = { + mode?: InputMaybe; + refresh_token?: InputMaybe; +}; + + +export type MutationUsers_Invite_AcceptArgs = { + password: Scalars['String']['input']; + token: Scalars['String']['input']; +}; + + +export type MutationUsers_Me_Tfa_DisableArgs = { + otp: Scalars['String']['input']; +}; + + +export type MutationUsers_Me_Tfa_EnableArgs = { + otp: Scalars['String']['input']; + secret: Scalars['String']['input']; +}; + + +export type MutationUsers_Me_Tfa_GenerateArgs = { + password: Scalars['String']['input']; +}; + + +export type MutationUtils_Hash_GenerateArgs = { + string: Scalars['String']['input']; +}; + + +export type MutationUtils_Hash_VerifyArgs = { + hash: Scalars['String']['input']; + string: Scalars['String']['input']; +}; + + +export type MutationUtils_Random_StringArgs = { + length?: InputMaybe; +}; + + +export type MutationUtils_RevertArgs = { + revision: Scalars['ID']['input']; +}; + + +export type MutationUtils_SortArgs = { + collection: Scalars['String']['input']; + item: Scalars['ID']['input']; + to: Scalars['ID']['input']; +}; + +export type Query = { + __typename?: 'Query'; + /** There's no data to query. */ + _empty?: Maybe; + extensions?: Maybe; + fields: Array; + fields_by_name?: Maybe; + fields_in_collection: Array; + server_health?: Maybe; + server_info?: Maybe; + server_ping?: Maybe; + server_specs_graphql?: Maybe; + server_specs_oas?: Maybe; +}; + + +export type QueryFields_By_NameArgs = { + collection: Scalars['String']['input']; + field: Scalars['String']['input']; +}; + + +export type QueryFields_In_CollectionArgs = { + collection: Scalars['String']['input']; +}; + + +export type QueryServer_Specs_GraphqlArgs = { + scope?: InputMaybe; +}; + +export enum Auth_Mode { + Cookie = 'cookie', + Json = 'json' +} + +export type Auth_Tokens = { + __typename?: 'auth_tokens'; + access_token?: Maybe; + expires?: Maybe; + refresh_token?: Maybe; +}; + +export type Directus_Fields = { + __typename?: 'directus_fields'; + collection?: Maybe; + field?: Maybe; + meta?: Maybe; + schema?: Maybe; + type?: Maybe; +}; + +export type Directus_Fields_Meta = { + __typename?: 'directus_fields_meta'; + collection: Scalars['String']['output']; + conditions?: Maybe; + display?: Maybe; + display_options?: Maybe; + field: Scalars['String']['output']; + group?: Maybe; + hidden: Scalars['Boolean']['output']; + id: Scalars['Int']['output']; + interface?: Maybe; + note?: Maybe; + options?: Maybe; + readonly: Scalars['Boolean']['output']; + required?: Maybe; + sort?: Maybe; + special?: Maybe>>; + translations?: Maybe; + validation?: Maybe; + validation_message?: Maybe; + width?: Maybe; +}; + +export type Directus_Fields_Schema = { + __typename?: 'directus_fields_schema'; + comment?: Maybe; + data_type?: Maybe; + default_value?: Maybe; + foreign_key_column?: Maybe; + foreign_key_table?: Maybe; + has_auto_increment?: Maybe; + is_nullable?: Maybe; + is_primary_key?: Maybe; + is_unique?: Maybe; + max_length?: Maybe; + name?: Maybe; + numeric_precision?: Maybe; + numeric_scale?: Maybe; + table?: Maybe; +}; + +export type Extensions = { + __typename?: 'extensions'; + displays?: Maybe>>; + interfaces?: Maybe>>; + layouts?: Maybe>>; + modules?: Maybe>>; +}; + +export enum Graphql_Sdl_Scope { + Items = 'items', + System = 'system' +} + +export type Server_Info = { + __typename?: 'server_info'; + project?: Maybe; +}; + +export type Server_Info_Project = { + __typename?: 'server_info_project'; + custom_css?: Maybe; + default_language?: Maybe; + project_color?: Maybe; + project_descriptor?: Maybe; + project_logo?: Maybe; + project_name?: Maybe; + public_background?: Maybe; + public_foreground?: Maybe; + public_note?: Maybe; +}; + +export type Users_Me_Tfa_Generate_Data = { + __typename?: 'users_me_tfa_generate_data'; + otpauth_url?: Maybe; + secret?: Maybe; +}; + +export type FieldDefaultsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type FieldDefaultsQuery = { __typename?: 'Query', fields: Array<{ __typename?: 'directus_fields', collection?: string | null, field?: string | null, schema?: { __typename?: 'directus_fields_schema', default_value?: string | null } | null }> }; + + +export const FieldDefaultsDocument = gql` + query FieldDefaults { + fields { + collection + field + schema { + default_value + } + } +} + `; + +/** + * __useFieldDefaultsQuery__ + * + * To run a query within a React component, call `useFieldDefaultsQuery` and pass it any options that fit your needs. + * When your component renders, `useFieldDefaultsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useFieldDefaultsQuery({ + * variables: { + * }, + * }); + */ +export function useFieldDefaultsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(FieldDefaultsDocument, options); + } +export function useFieldDefaultsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(FieldDefaultsDocument, options); + } +export type FieldDefaultsQueryHookResult = ReturnType; +export type FieldDefaultsLazyQueryHookResult = ReturnType; +export type FieldDefaultsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/src/generated/graphqlPlural.ts b/src/generated/graphqlPlural.ts index 853aaf9b..3a64058b 100644 --- a/src/generated/graphqlPlural.ts +++ b/src/generated/graphqlPlural.ts @@ -12,7 +12,7 @@ export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | const defaultOptions = {} as const; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: { input: string | number; output: string; } + ID: { input: string; output: string; } String: { input: string; output: string; } Boolean: { input: boolean; output: boolean; } Int: { input: number; output: number; } @@ -5026,12 +5026,28 @@ export type RepoQuery = { __typename?: 'RootQueryType', repository?: { __typenam export type CategoryFragment = { __typename?: 'CategoryInfo', category?: Category | null, count?: number | null }; +export type PageInfoFragment = { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean }; + +export type CategoriesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type CategoriesQuery = { __typename?: 'RootQueryType', categories?: Array<{ __typename?: 'CategoryInfo', category?: Category | null, count?: number | null } | null> | null }; + +export type TagFragment = { __typename?: 'GroupedTag', tag: string, count: number }; + +export type TagsQueryVariables = Exact<{ + cursor?: InputMaybe; +}>; + + +export type TagsQuery = { __typename?: 'RootQueryType', tags?: { __typename?: 'GroupedTagConnection', pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean }, edges?: Array<{ __typename?: 'GroupedTagEdge', node?: { __typename?: 'GroupedTag', tag: string, count: number } | null } | null> | null } | null }; + +export type StackCollectionFragment = { __typename?: 'StackCollection', id: string, provider: Provider, bundles?: Array<{ __typename?: 'StackRecipe', recipe: { __typename?: 'Recipe', repository?: { __typename?: 'Repository', category?: Category | null, darkIcon?: string | null, description?: string | null, icon?: string | null, id: string, name: string, private?: boolean | null, releaseStatus?: ReleaseStatus | null, trending?: boolean | null, verified?: boolean | null, readme?: string | null, mainBranch?: string | null, homepage?: string | null, gitUrl?: string | null, tags?: Array<{ __typename?: 'Tag', tag: string } | null> | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null } | null, recipes?: Array<{ __typename?: 'Recipe', name: string, private?: boolean | null } | null> | null, license?: { __typename?: 'License', name?: string | null, url?: string | null } | null, community?: { __typename?: 'Community', discord?: string | null, slack?: string | null, homepage?: string | null, gitUrl?: string | null, twitter?: string | null } | null } | null } } | null> | null }; + export type MinStackFragment = { __typename?: 'Stack', id: string, name: string, displayName?: string | null, description?: string | null, featured?: boolean | null, creator?: { __typename?: 'User', id: string, name: string } | null, collections?: Array<{ __typename?: 'StackCollection', id: string, provider: Provider, bundles?: Array<{ __typename?: 'StackRecipe', recipe: { __typename?: 'Recipe', repository?: { __typename?: 'Repository', category?: Category | null, darkIcon?: string | null, description?: string | null, icon?: string | null, id: string, name: string, private?: boolean | null, releaseStatus?: ReleaseStatus | null, trending?: boolean | null, verified?: boolean | null, readme?: string | null, mainBranch?: string | null, homepage?: string | null, gitUrl?: string | null, tags?: Array<{ __typename?: 'Tag', tag: string } | null> | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null } | null, recipes?: Array<{ __typename?: 'Recipe', name: string, private?: boolean | null } | null> | null, license?: { __typename?: 'License', name?: string | null, url?: string | null } | null, community?: { __typename?: 'Community', discord?: string | null, slack?: string | null, homepage?: string | null, gitUrl?: string | null, twitter?: string | null } | null } | null } } | null> | null } | null> | null }; export type FullStackFragment = { __typename?: 'Stack', id: string, name: string, displayName?: string | null, description?: string | null, featured?: boolean | null, creator?: { __typename?: 'User', id: string, name: string } | null, collections?: Array<{ __typename?: 'StackCollection', id: string, provider: Provider, bundles?: Array<{ __typename?: 'StackRecipe', recipe: { __typename?: 'Recipe', repository?: { __typename?: 'Repository', category?: Category | null, darkIcon?: string | null, description?: string | null, icon?: string | null, id: string, name: string, private?: boolean | null, releaseStatus?: ReleaseStatus | null, trending?: boolean | null, verified?: boolean | null, readme?: string | null, mainBranch?: string | null, homepage?: string | null, gitUrl?: string | null, tags?: Array<{ __typename?: 'Tag', tag: string } | null> | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null } | null, recipes?: Array<{ __typename?: 'Recipe', name: string, private?: boolean | null } | null> | null, license?: { __typename?: 'License', name?: string | null, url?: string | null } | null, community?: { __typename?: 'Community', discord?: string | null, slack?: string | null, homepage?: string | null, gitUrl?: string | null, twitter?: string | null } | null } | null } } | null> | null } | null> | null }; -export type PageInfoFragment = { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean }; - export type StacksQueryVariables = Exact<{ featured?: InputMaybe; }>; @@ -5041,26 +5057,12 @@ export type StacksQuery = { __typename?: 'RootQueryType', stacks?: { __typename? export type StackQueryVariables = Exact<{ name: Scalars['String']['input']; - provider?: Provider; + provider?: InputMaybe; }>; export type StackQuery = { __typename?: 'RootQueryType', stack?: { __typename?: 'Stack', id: string, name: string, displayName?: string | null, description?: string | null, featured?: boolean | null, creator?: { __typename?: 'User', id: string, name: string } | null, collections?: Array<{ __typename?: 'StackCollection', id: string, provider: Provider, bundles?: Array<{ __typename?: 'StackRecipe', recipe: { __typename?: 'Recipe', repository?: { __typename?: 'Repository', category?: Category | null, darkIcon?: string | null, description?: string | null, icon?: string | null, id: string, name: string, private?: boolean | null, releaseStatus?: ReleaseStatus | null, trending?: boolean | null, verified?: boolean | null, readme?: string | null, mainBranch?: string | null, homepage?: string | null, gitUrl?: string | null, tags?: Array<{ __typename?: 'Tag', tag: string } | null> | null, publisher?: { __typename?: 'Publisher', id?: string | null, name: string, phone?: string | null, avatar?: string | null, description?: string | null, backgroundColor?: string | null } | null, recipes?: Array<{ __typename?: 'Recipe', name: string, private?: boolean | null } | null> | null, license?: { __typename?: 'License', name?: string | null, url?: string | null } | null, community?: { __typename?: 'Community', discord?: string | null, slack?: string | null, homepage?: string | null, gitUrl?: string | null, twitter?: string | null } | null } | null } } | null> | null } | null> | null } | null }; -export type CategoriesQueryVariables = Exact<{ [key: string]: never; }>; - - -export type CategoriesQuery = { __typename?: 'RootQueryType', categories?: Array<{ __typename?: 'CategoryInfo', category?: Category | null, count?: number | null } | null> | null }; - -export type TagFragment = { __typename?: 'GroupedTag', tag: string, count: number }; - -export type TagsQueryVariables = Exact<{ - cursor?: InputMaybe; -}>; - - -export type TagsQuery = { __typename?: 'RootQueryType', tags?: { __typename?: 'GroupedTagConnection', pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean }, edges?: Array<{ __typename?: 'GroupedTagEdge', node?: { __typename?: 'GroupedTag', tag: string, count: number } | null } | null> | null } | null }; - export const ArtifactFragmentDoc = gql` fragment Artifact on Artifact { id @@ -5189,6 +5191,34 @@ export const CategoryFragmentDoc = gql` count } `; +export const PageInfoFragmentDoc = gql` + fragment PageInfo on PageInfo { + endCursor + hasNextPage +} + `; +export const TagFragmentDoc = gql` + fragment Tag on GroupedTag { + tag + count +} + `; +export const StackCollectionFragmentDoc = gql` + fragment StackCollection on StackCollection { + id + provider + bundles { + recipe { + repository { + ...MinRepo + tags { + tag + } + } + } + } +} + ${MinRepoFragmentDoc}`; export const MinStackFragmentDoc = gql` fragment MinStack on Stack { id @@ -5201,38 +5231,15 @@ export const MinStackFragmentDoc = gql` name } collections { - id - provider - bundles { - recipe { - repository { - ...MinRepo - tags { - tag - } - } - } - } + ...StackCollection } } - ${MinRepoFragmentDoc}`; + ${StackCollectionFragmentDoc}`; export const FullStackFragmentDoc = gql` fragment FullStack on Stack { ...MinStack } ${MinStackFragmentDoc}`; -export const PageInfoFragmentDoc = gql` - fragment PageInfo on PageInfo { - endCursor - hasNextPage -} - `; -export const TagFragmentDoc = gql` - fragment Tag on GroupedTag { - tag - count -} - `; export const ListArtifactsDocument = gql` query ListArtifacts($id: ID!) { repository(id: $id) { @@ -5382,6 +5389,83 @@ export function useRepoLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions; export type RepoLazyQueryHookResult = ReturnType; export type RepoQueryResult = Apollo.QueryResult; +export const CategoriesDocument = gql` + query Categories { + categories { + ...Category + } +} + ${CategoryFragmentDoc}`; + +/** + * __useCategoriesQuery__ + * + * To run a query within a React component, call `useCategoriesQuery` and pass it any options that fit your needs. + * When your component renders, `useCategoriesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useCategoriesQuery({ + * variables: { + * }, + * }); + */ +export function useCategoriesQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(CategoriesDocument, options); + } +export function useCategoriesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(CategoriesDocument, options); + } +export type CategoriesQueryHookResult = ReturnType; +export type CategoriesLazyQueryHookResult = ReturnType; +export type CategoriesQueryResult = Apollo.QueryResult; +export const TagsDocument = gql` + query Tags($cursor: String) { + tags(type: REPOSITORIES, first: 5000, after: $cursor) { + pageInfo { + ...PageInfo + } + edges { + node { + ...Tag + } + } + } +} + ${PageInfoFragmentDoc} +${TagFragmentDoc}`; + +/** + * __useTagsQuery__ + * + * To run a query within a React component, call `useTagsQuery` and pass it any options that fit your needs. + * When your component renders, `useTagsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useTagsQuery({ + * variables: { + * cursor: // value for 'cursor' + * }, + * }); + */ +export function useTagsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(TagsDocument, options); + } +export function useTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(TagsDocument, options); + } +export type TagsQueryHookResult = ReturnType; +export type TagsLazyQueryHookResult = ReturnType; +export type TagsQueryResult = Apollo.QueryResult; export const StacksDocument = gql` query Stacks($featured: Boolean) { stacks(featured: $featured, first: 50) { @@ -5426,7 +5510,7 @@ export type StacksQueryHookResult = ReturnType; export type StacksLazyQueryHookResult = ReturnType; export type StacksQueryResult = Apollo.QueryResult; export const StackDocument = gql` - query Stack($name: String!, $provider: Provider! = AWS) { + query Stack($name: String!, $provider: Provider = AWS) { stack(name: $name, provider: $provider) { ...FullStack } @@ -5460,81 +5544,4 @@ export function useStackLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions; export type StackLazyQueryHookResult = ReturnType; -export type StackQueryResult = Apollo.QueryResult; -export const CategoriesDocument = gql` - query Categories { - categories { - ...Category - } -} - ${CategoryFragmentDoc}`; - -/** - * __useCategoriesQuery__ - * - * To run a query within a React component, call `useCategoriesQuery` and pass it any options that fit your needs. - * When your component renders, `useCategoriesQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useCategoriesQuery({ - * variables: { - * }, - * }); - */ -export function useCategoriesQuery(baseOptions?: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(CategoriesDocument, options); - } -export function useCategoriesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(CategoriesDocument, options); - } -export type CategoriesQueryHookResult = ReturnType; -export type CategoriesLazyQueryHookResult = ReturnType; -export type CategoriesQueryResult = Apollo.QueryResult; -export const TagsDocument = gql` - query Tags($cursor: String) { - tags(type: REPOSITORIES, first: 5000, after: $cursor) { - pageInfo { - ...PageInfo - } - edges { - node { - ...Tag - } - } - } -} - ${PageInfoFragmentDoc} -${TagFragmentDoc}`; - -/** - * __useTagsQuery__ - * - * To run a query within a React component, call `useTagsQuery` and pass it any options that fit your needs. - * When your component renders, `useTagsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useTagsQuery({ - * variables: { - * cursor: // value for 'cursor' - * }, - * }); - */ -export function useTagsQuery(baseOptions?: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(TagsDocument, options); - } -export function useTagsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(TagsDocument, options); - } -export type TagsQueryHookResult = ReturnType; -export type TagsLazyQueryHookResult = ReturnType; -export type TagsQueryResult = Apollo.QueryResult; \ No newline at end of file +export type StackQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/src/graph/directus/cms.graphql b/src/graph/directus/cms.graphql index ef9ef370..567d09be 100644 --- a/src/graph/directus/cms.graphql +++ b/src/graph/directus/cms.graphql @@ -61,3 +61,14 @@ query AppExtras($name: String) { ...AppExtras } } + +fragment StackExtras on stacks { + name + heroVideo +} + +query StackExtras($name: String) { + stacks(filter: { name: { _eq: $name } }) { + ...StackExtras + } +} diff --git a/src/graph/directus/system/fieldDefaults.graphql b/src/graph/directus/system/fieldDefaults.graphql new file mode 100644 index 00000000..d1c1b5ba --- /dev/null +++ b/src/graph/directus/system/fieldDefaults.graphql @@ -0,0 +1,9 @@ +query FieldDefaults { + fields { + collection + field + schema { + default_value + } + } +} diff --git a/src/graph/plural/repos.graphql b/src/graph/plural/repos.graphql index 5dc34a7a..232ab83f 100644 --- a/src/graph/plural/repos.graphql +++ b/src/graph/plural/repos.graphql @@ -99,60 +99,11 @@ fragment Category on CategoryInfo { count } -fragment MinStack on Stack { - id - name - displayName - description - featured - creator { - id - name - } - collections { - id - provider - bundles { - recipe { - repository { - ...MinRepo - tags { - tag - } - } - } - } - } -} - -fragment FullStack on Stack { - ...MinStack -} - fragment PageInfo on PageInfo { endCursor hasNextPage } -query Stacks($featured: Boolean) { - stacks(featured: $featured, first: 50) { - pageInfo { - ...PageInfo - } - edges { - node { - ...MinStack - } - } - } -} - -query Stack($name: String!, $provider: Provider! = AWS) { - stack(name: $name, provider: $provider) { - ...FullStack - } -} - query Categories { categories { ...Category diff --git a/src/graph/plural/stacks.graphql b/src/graph/plural/stacks.graphql new file mode 100644 index 00000000..74afbe67 --- /dev/null +++ b/src/graph/plural/stacks.graphql @@ -0,0 +1,52 @@ +fragment StackCollection on StackCollection { + id + provider + bundles { + recipe { + repository { + ...MinRepo + tags { + tag + } + } + } + } +} + +fragment MinStack on Stack { + id + name + displayName + description + featured + creator { + id + name + } + collections { + ...StackCollection + } +} + +fragment FullStack on Stack { + ...MinStack +} + +query Stacks($featured: Boolean) { + stacks(featured: $featured, first: 50) { + pageInfo { + ...PageInfo + } + edges { + node { + ...MinStack + } + } + } +} + +query Stack($name: String!, $provider: Provider = AWS) { + stack(name: $name, provider: $provider) { + ...FullStack + } +} diff --git a/src/utils/defaultPageFunctions.tsx b/src/utils/defaultPageFunctions.tsx index 45d3ee21..ef4cb386 100644 --- a/src/utils/defaultPageFunctions.tsx +++ b/src/utils/defaultPageFunctions.tsx @@ -1,4 +1,4 @@ -import { propsWithGlobalSettings } from '../components/getGlobalProps' +import { propsWithGlobalSettings } from './getGlobalProps' export async function getStaticProps() { return propsWithGlobalSettings({}) diff --git a/src/components/getGlobalProps.tsx b/src/utils/getGlobalProps.tsx similarity index 97% rename from src/components/getGlobalProps.tsx rename to src/utils/getGlobalProps.tsx index b0359033..91fa56cd 100644 --- a/src/components/getGlobalProps.tsx +++ b/src/utils/getGlobalProps.tsx @@ -2,14 +2,13 @@ import { type GetStaticPropsResult } from 'next' import { until } from '@open-draft/until' -import { REVALIDATE_TIME } from '@src/consts' -import { getSiteSettings } from '@src/data/getSiteSettings' - import { GITHUB_DATA_URL, getGithubDataServer, isGithubRepoData, -} from './GithubStars' +} from '@src/components/GithubStars' +import { REVALIDATE_TIME } from '@src/consts' +import { getSiteSettings } from '@src/data/getSiteSettings' async function getGlobalProps() { const { data: githubData, error: githubError } = await until(() => diff --git a/src/utils/text.ts b/src/utils/text.ts index 6edd8638..475ba6b8 100644 --- a/src/utils/text.ts +++ b/src/utils/text.ts @@ -57,3 +57,7 @@ export function toHtmlId(str) { // make sure the id starts with a letter or underscore return id.match(/^[A-Za-z]/) ? id : `_${id}` } + +export function startsWithVowel(str: string) { + return !!str.match(/^[aeiou]/i) +} diff --git a/yarn.lock b/yarn.lock index a04a02b5..e30f8726 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3149,9 +3149,9 @@ __metadata: languageName: node linkType: hard -"@pluralsh/design-system@npm:1.344.1": - version: 1.344.1 - resolution: "@pluralsh/design-system@npm:1.344.1" +"@pluralsh/design-system@npm:2.0.0": + version: 2.0.0 + resolution: "@pluralsh/design-system@npm:2.0.0" dependencies: "@floating-ui/react-dom-interactions": 0.13.3 "@loomhq/loom-embed": 1.5.0 @@ -3199,7 +3199,7 @@ __metadata: react-dom: ">=18.2.0" react-transition-group: ">=4.4.5" styled-components: ">=5.3.11" - checksum: 14569437aa4445f62389adc2b2df8e4f3910987b88d8e38bb5cf10c8de816567e59b125b4d5f1e8066bc3503655dc8358dce934bf87ac924d20605f5e413c7fc + checksum: da001df5770876cafda494b510dbb6f43fab951b08eaa56752d50bcc4fee8ae7edfcbb537a19e4ba226b7a8d000f0996dc8b1f9ba10378d70354d0b5b3489b2c languageName: node linkType: hard @@ -11447,7 +11447,7 @@ __metadata: "@markdoc/next.js": 0.2.2 "@next/bundle-analyzer": 13.4.6 "@open-draft/until": 2.1.0 - "@pluralsh/design-system": 1.344.1 + "@pluralsh/design-system": 2.0.0 "@pluralsh/eslint-config-typescript": 2.5.41 "@pluralsh/stylelint-config": 1.1.3 "@react-types/shared": 3.18.1