feat(nx-dev): add Nx Cloud Page (#26865)
This commit is contained in:
parent
f1f8ab7fe7
commit
51d5d23eb8
@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import { blogApi } from '../../lib/blog.api';
|
import { blogApi } from '../../lib/blog.api';
|
||||||
import { BlogContainer } from '@nx/nx-dev/ui-blog';
|
import { BlogContainer } from '@nx/nx-dev/ui-blog';
|
||||||
|
import { DefaultLayout } from '@nx/nx-dev/ui-common';
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Nx Blog - Updates from the Nx & Nx Cloud team',
|
title: 'Nx Blog - Updates from the Nx & Nx Cloud team',
|
||||||
@ -29,5 +30,9 @@ async function getBlogs() {
|
|||||||
|
|
||||||
export default async function BlogIndex() {
|
export default async function BlogIndex() {
|
||||||
const blogs = await getBlogs();
|
const blogs = await getBlogs();
|
||||||
return <BlogContainer blogPosts={blogs} />;
|
return (
|
||||||
|
<DefaultLayout>
|
||||||
|
<BlogContainer blogPosts={blogs} />
|
||||||
|
</DefaultLayout>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,9 +81,7 @@ export default function RootLayout({
|
|||||||
</head>
|
</head>
|
||||||
<body className="h-full bg-white text-slate-700 antialiased selection:bg-blue-500 selection:text-white dark:bg-slate-900 dark:text-slate-400 dark:selection:bg-sky-500">
|
<body className="h-full bg-white text-slate-700 antialiased selection:bg-blue-500 selection:text-white dark:bg-slate-900 dark:text-slate-400 dark:selection:bg-sky-500">
|
||||||
<AnnouncementBanner />
|
<AnnouncementBanner />
|
||||||
<Header />
|
|
||||||
{children}
|
{children}
|
||||||
<Footer />
|
|
||||||
<GlobalScripts gaMeasurementId={gaMeasurementId} />
|
<GlobalScripts gaMeasurementId={gaMeasurementId} />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
68
nx-dev/nx-dev/app/nx-cloud/page.tsx
Normal file
68
nx-dev/nx-dev/app/nx-cloud/page.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
Hero,
|
||||||
|
TrustedBy,
|
||||||
|
FasterAndCheaper,
|
||||||
|
UnderstandWorkspace,
|
||||||
|
EnhancedWithAi,
|
||||||
|
AutomatedAgentsManagement,
|
||||||
|
AgentNumberOverTime,
|
||||||
|
Statistics,
|
||||||
|
} from '@nx/nx-dev/ui-cloud';
|
||||||
|
|
||||||
|
import { CallToAction, DefaultLayout } from '@nx/nx-dev/ui-common';
|
||||||
|
|
||||||
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Nx Cloud',
|
||||||
|
description:
|
||||||
|
'Nx Cloud is the end-to-end solution for smart, efficient and maintainable CI.',
|
||||||
|
openGraph: {
|
||||||
|
url: 'https://nx.dev/nx-cloud',
|
||||||
|
title: 'Nx Cloud',
|
||||||
|
description:
|
||||||
|
'Nx Cloud is the end-to-end solution for smart, efficient and maintainable CI.',
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: 'https://nx.dev/socials/nx-media.png',
|
||||||
|
width: 800,
|
||||||
|
height: 421,
|
||||||
|
alt: 'Nx: Smart Monorepos · Fast CI',
|
||||||
|
type: 'image/jpeg',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
siteName: 'NxDev',
|
||||||
|
type: 'website',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NxCloudPage(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<DefaultLayout>
|
||||||
|
<Hero />
|
||||||
|
<TrustedBy />
|
||||||
|
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<FasterAndCheaper />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<UnderstandWorkspace />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<EnhancedWithAi />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<AutomatedAgentsManagement />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<AgentNumberOverTime />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<Statistics />
|
||||||
|
</div>
|
||||||
|
<div className="mt-32 lg:mt-56">
|
||||||
|
<CallToAction />
|
||||||
|
</div>
|
||||||
|
</DefaultLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
BIN
nx-dev/nx-dev/public/images/cloud/nrwl-ocean.avif
Normal file
BIN
nx-dev/nx-dev/public/images/cloud/nrwl-ocean.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
BIN
nx-dev/nx-dev/public/images/cloud/nx-oss-source.avif
Normal file
BIN
nx-dev/nx-dev/public/images/cloud/nx-oss-source.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
@ -480,7 +480,6 @@ const nxCloudUrls = {
|
|||||||
'/ci': '/ci/intro/ci-with-nx',
|
'/ci': '/ci/intro/ci-with-nx',
|
||||||
'/concepts/more-concepts/illustrated-dte':
|
'/concepts/more-concepts/illustrated-dte':
|
||||||
'/ci/concepts/parallelization-distribution',
|
'/ci/concepts/parallelization-distribution',
|
||||||
'/nx-cloud/:path*': '/ci/:path*',
|
|
||||||
'/core-features/:path*': '/features/:path*',
|
'/core-features/:path*': '/features/:path*',
|
||||||
'/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
'/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
||||||
'/ci/intro/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
'/ci/intro/connect-to-cloud': '/ci/intro/connect-to-nx-cloud',
|
||||||
|
|||||||
12
nx-dev/ui-cloud/.babelrc
Normal file
12
nx-dev/ui-cloud/.babelrc
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@nx/react/babel",
|
||||||
|
{
|
||||||
|
"runtime": "automatic",
|
||||||
|
"useBuiltIns": "usage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"plugins": []
|
||||||
|
}
|
||||||
18
nx-dev/ui-cloud/.eslintrc.json
Normal file
18
nx-dev/ui-cloud/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.js", "*.jsx"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
7
nx-dev/ui-cloud/README.md
Normal file
7
nx-dev/ui-cloud/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# ui-cloud
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test ui-cloud` to execute the unit tests via [Jest](https://jestjs.io).
|
||||||
9
nx-dev/ui-cloud/project.json
Normal file
9
nx-dev/ui-cloud/project.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "ui-cloud",
|
||||||
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
|
"sourceRoot": "nx-dev/ui-cloud/src",
|
||||||
|
"projectType": "library",
|
||||||
|
"tags": [],
|
||||||
|
"// targets": "to see all targets run: nx show project ui-cloud --web",
|
||||||
|
"targets": {}
|
||||||
|
}
|
||||||
9
nx-dev/ui-cloud/src/index.ts
Normal file
9
nx-dev/ui-cloud/src/index.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export * from './lib/layout';
|
||||||
|
export * from './lib/hero';
|
||||||
|
export * from './lib/trusted-by';
|
||||||
|
export * from './lib/faster-and-cheaper';
|
||||||
|
export * from './lib/understand-workspace';
|
||||||
|
export * from './lib/enhance-with-ai';
|
||||||
|
export * from './lib/automated-agents-management';
|
||||||
|
export * from './lib/agent-number-over-time';
|
||||||
|
export * from './lib/statistics';
|
||||||
189
nx-dev/ui-cloud/src/lib/agent-number-over-time.tsx
Normal file
189
nx-dev/ui-cloud/src/lib/agent-number-over-time.tsx
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
'use client';
|
||||||
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
interface Agent {
|
||||||
|
date: string;
|
||||||
|
available: number;
|
||||||
|
used: number;
|
||||||
|
}
|
||||||
|
function generateAgents(): Agent[] {
|
||||||
|
const items: { date: string; available: number; used: number }[] = [];
|
||||||
|
for (let i = 1; i <= 30; i++) {
|
||||||
|
const used = Math.floor(Math.random() * 81); // Random percentage from 0 to 80
|
||||||
|
const available =
|
||||||
|
used + Math.floor(Math.random() * (100 - used - 20 + 1) + 20); // Random percentage that is at least 20 more than "used"
|
||||||
|
items.push({ date: `Aug ${String(i).padStart(2, '0')}`, available, used });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i <= 30; i++) {
|
||||||
|
const available = Math.floor(Math.random() * (41 - 5)) + 5; // Random percentage from 5 to 41
|
||||||
|
const lowerBound = available * 0.98; // Lower bound of the 2% error range
|
||||||
|
const used =
|
||||||
|
Math.floor(Math.random() * (available - lowerBound)) + lowerBound;
|
||||||
|
items.push({ date: `Aug ${String(i).padStart(2, '0')}`, available, used });
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AgentNumberOverTime(): JSX.Element {
|
||||||
|
const [agents, setAgents] = useState<Agent[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let ignore = false;
|
||||||
|
if (!ignore) setAgents(generateAgents());
|
||||||
|
return () => {
|
||||||
|
ignore = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Set total number of items and gaps
|
||||||
|
const totalGaps = agents.length - 1;
|
||||||
|
// Set gap width in percent
|
||||||
|
const gapWidthPercent = 0.3;
|
||||||
|
// Calculate total width of gaps
|
||||||
|
const totalGapWidthPercent = totalGaps * gapWidthPercent;
|
||||||
|
// Calculate the remaining length for items
|
||||||
|
const remainingPercent = 100 - totalGapWidthPercent;
|
||||||
|
// Calculate the width of each item
|
||||||
|
const itemWidthPercent = remainingPercent / agents.length;
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
delay: i || 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const itemVariants = {
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.035,
|
||||||
|
duration: 0.65,
|
||||||
|
ease: 'easeOut',
|
||||||
|
when: 'beforeChildren',
|
||||||
|
staggerChildren: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
y: 4,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="agent-number-over-time" className="overflow-hidden">
|
||||||
|
<div className="mx-auto max-w-7xl md:px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<SectionHeading
|
||||||
|
as="h2"
|
||||||
|
variant="title"
|
||||||
|
id="optimize-agent-utilization"
|
||||||
|
>
|
||||||
|
Optimize agent utilization <br /> Save money
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading as="p" variant="subtitle" className="mt-6">
|
||||||
|
Nx Cloud uses historical data and models to optimize the utilization
|
||||||
|
of your agents.
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="relative mt-16 w-full rounded-md border border-slate-100 bg-white p-4 sm:mt-20 dark:border-slate-800 dark:bg-slate-950"
|
||||||
|
>
|
||||||
|
<div className="mt-2 text-base font-medium">
|
||||||
|
Number of idle agents VS used agents over time
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end gap-3 text-xs">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-blue-500" />
|
||||||
|
Available agents count
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-green-500" />
|
||||||
|
Used agents count
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-xs">Agents count</div>
|
||||||
|
<div
|
||||||
|
className="mt-1 flex h-56 flex-row items-end space-y-1 border-b border-l border-slate-200 p-1 dark:border-slate-700"
|
||||||
|
style={{ gap: gapWidthPercent + '%' }}
|
||||||
|
>
|
||||||
|
{agents.map((i, idx) => (
|
||||||
|
<motion.div
|
||||||
|
custom={idx}
|
||||||
|
variants={itemVariants}
|
||||||
|
key={`without-agents-${i.available}-${idx}`}
|
||||||
|
data-tooltip={`${Math.round(i.available - i.used)} agents idle`}
|
||||||
|
className="relative flex h-full flex-col-reverse"
|
||||||
|
style={{ width: itemWidthPercent + '%' }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
custom={idx}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="absolute inset-x-0 bottom-0 w-full rounded-sm bg-blue-500"
|
||||||
|
style={{
|
||||||
|
height: i.available + '%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
custom={idx + 1}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="absolute inset-x-0 bottom-0 w-full bg-green-500"
|
||||||
|
style={{ height: i.used + '%' }}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 flex flex-row justify-between text-xs">
|
||||||
|
<span>Time</span>
|
||||||
|
<span>Day 1</span>
|
||||||
|
<span>Day 2</span>
|
||||||
|
<span>Day 3</span>
|
||||||
|
<span>Day 4</span>
|
||||||
|
<span>Day 5</span>
|
||||||
|
<span>Day 6</span>
|
||||||
|
<span>Day 7</span>
|
||||||
|
<span>Day 8</span>
|
||||||
|
<span>Day 9</span>
|
||||||
|
<span>Day 10</span>
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
custom={4}
|
||||||
|
variants={variants}
|
||||||
|
className="absolute left-1/2 top-1/2 flex h-12 w-auto flex-row items-center gap-2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
role="img"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
className="mt-4 h-8 w-8 -rotate-[18deg] transform text-slate-600 dark:text-slate-400"
|
||||||
|
>
|
||||||
|
<path d="M.348 12.633a1.145 1.145 0 0 1 1.681 0l2.785 2.938c.466-5 3.975-9.317 8.877-10.346 3.608-.757 7.33.419 9.954 3.146.468.486.474 1.28.013 1.774-.46.494-1.213.5-1.68.014-2.064-2.143-4.988-3.068-7.823-2.473-3.873.813-6.64 4.239-6.98 8.194l3.078-3.247a1.145 1.145 0 0 1 1.681 0c.464.49.464 1.284 0 1.774l-4.952 5.226a1.154 1.154 0 0 1-.84.367c-.305 0-.61-.122-.841-.367L.348 14.407a1.304 1.304 0 0 1 0-1.774Z" />
|
||||||
|
</svg>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/10 dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20">
|
||||||
|
Nx Cloud enabled
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
243
nx-dev/ui-cloud/src/lib/automated-agents-management.tsx
Normal file
243
nx-dev/ui-cloud/src/lib/automated-agents-management.tsx
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
'use client';
|
||||||
|
import {
|
||||||
|
AdjustmentsVerticalIcon,
|
||||||
|
ArrowLongDownIcon,
|
||||||
|
CircleStackIcon,
|
||||||
|
CodeBracketSquareIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { NxCloudIcon } from '@nx/nx-dev/ui-icons';
|
||||||
|
|
||||||
|
export function AutomatedAgentsManagement(): JSX.Element {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
delay: i || 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const itemVariants = {
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.035,
|
||||||
|
duration: 0.65,
|
||||||
|
ease: 'easeOut',
|
||||||
|
when: 'beforeChildren',
|
||||||
|
staggerChildren: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
y: 4,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="competitive-compute" className="overflow-hidden">
|
||||||
|
<div className="mx-auto max-w-7xl md:px-6 lg:px-8">
|
||||||
|
<div className="grid grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:grid-cols-2 lg:items-start">
|
||||||
|
<div className="px-6 md:px-0 lg:pr-4 lg:pt-4">
|
||||||
|
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-lg">
|
||||||
|
<SectionHeading
|
||||||
|
as="h2"
|
||||||
|
variant="title"
|
||||||
|
id="seamless-distribution"
|
||||||
|
>
|
||||||
|
Seamless distribution, faster CI
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading as="p" variant="subtitle" className="mt-6">
|
||||||
|
Nx Cloud dynamically adapts to your CI needs - providing an
|
||||||
|
excellent developer experience while minimizing your costs.
|
||||||
|
</SectionHeading>
|
||||||
|
|
||||||
|
<dl className="mt-12 max-w-xl space-y-4 text-base leading-7">
|
||||||
|
<div className="relative pl-9">
|
||||||
|
<dt className="inline font-semibold text-slate-950 dark:text-white">
|
||||||
|
<AdjustmentsVerticalIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute left-1 top-1 h-5 w-5"
|
||||||
|
/>
|
||||||
|
Perfect provisioning.{' '}
|
||||||
|
</dt>
|
||||||
|
<dd className="inline">
|
||||||
|
Instruct Nx Cloud to dynamically allocate the right number
|
||||||
|
of agents for each pull request.
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="relative pl-9">
|
||||||
|
<dt className="inline font-semibold text-slate-950 dark:text-white">
|
||||||
|
<CircleStackIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute left-1 top-1 h-5 w-5"
|
||||||
|
/>
|
||||||
|
Effortless CI integration.{' '}
|
||||||
|
</dt>
|
||||||
|
<dd className="inline">
|
||||||
|
Utilize additional compute from Nx Cloud and enjoy faster
|
||||||
|
and cheaper CI with your existing provider.
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div className="relative pl-9">
|
||||||
|
<dt className="inline font-semibold text-slate-950 dark:text-white">
|
||||||
|
<CodeBracketSquareIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute left-1 top-1 h-5 w-5"
|
||||||
|
/>
|
||||||
|
Simple configuration.{' '}
|
||||||
|
</dt>
|
||||||
|
<dd className="inline">
|
||||||
|
Add a single line to your CI configuration to enable
|
||||||
|
distribution, computation caching, E2E test splitting, and
|
||||||
|
more.
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-col justify-items-stretch gap-4 sm:px-6 lg:px-0">
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="relative rounded-md border border-slate-100 p-4 dark:border-slate-800"
|
||||||
|
>
|
||||||
|
<div className="rounded-lg border border-dashed border-slate-300 p-2 dark:border-slate-500">
|
||||||
|
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
|
Your CI provider
|
||||||
|
</p>
|
||||||
|
<div className="mt-4 grid grid-cols-2 items-center gap-1">
|
||||||
|
<motion.p variants={itemVariants}>
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
nx affected -t lint build test
|
||||||
|
</span>
|
||||||
|
</motion.p>
|
||||||
|
|
||||||
|
<p className="mt-1 text-xs">
|
||||||
|
on job:{' '}
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-slate-900 dark:text-slate-400 dark:ring-slate-700">
|
||||||
|
main-linux
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={0.5}
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="text-center"
|
||||||
|
>
|
||||||
|
<ArrowLongDownIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="inline-flex h-6 w-6"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={1}
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="coding z-10 mx-4 rounded-lg border border-slate-200 p-4 font-mono text-xs leading-normal text-slate-800 subpixel-antialiased dark:border-slate-700 dark:bg-slate-800 dark:text-slate-200"
|
||||||
|
>
|
||||||
|
<div className="flex items-start">
|
||||||
|
<p>
|
||||||
|
<span className="mx-0.5 text-green-600 dark:text-green-400">
|
||||||
|
~
|
||||||
|
</span>{' '}
|
||||||
|
<span>$</span>
|
||||||
|
</p>
|
||||||
|
<p className="typing flex-1 pl-1">
|
||||||
|
npx nx-cloud start-ci-run --distribute-on="15
|
||||||
|
linux-medium-plus-js"
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={1.5}
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="text-center"
|
||||||
|
>
|
||||||
|
<ArrowLongDownIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="inline-flex h-6 w-6"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={2}
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="relative grid grid-cols-2 gap-2 rounded-md border border-slate-100 p-4 dark:border-slate-800"
|
||||||
|
>
|
||||||
|
<div className="rounded-lg border border-dashed border-slate-300 p-2 dark:border-slate-500">
|
||||||
|
<p className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
|
Your CI provider
|
||||||
|
</p>
|
||||||
|
<div className="mt-4">
|
||||||
|
<p>
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
nx affected -t lint build test
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="mt-1 text-xs">
|
||||||
|
on job:{' '}
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-slate-900 dark:text-slate-400 dark:ring-slate-700">
|
||||||
|
main-linux
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg border border-dashed border-slate-500 p-2 dark:border-slate-500">
|
||||||
|
<p className="flex items-center gap-1 text-xs text-slate-600 dark:text-slate-400">
|
||||||
|
<NxCloudIcon className="h-4 w-4" aria-hidden="true" />
|
||||||
|
Nx Cloud
|
||||||
|
</p>
|
||||||
|
<div className="mt-4 flex flex-col items-start gap-1 text-xs">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
nx run build
|
||||||
|
</span>
|
||||||
|
on Agent 1, Agent 2, Agent 3
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
nx run test
|
||||||
|
</span>
|
||||||
|
on Agent 1, Agent 4
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
nx run lint
|
||||||
|
</span>
|
||||||
|
on Agent 1, Agent 2, Agent 4
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
87
nx-dev/ui-cloud/src/lib/call-to-action.tsx
Normal file
87
nx-dev/ui-cloud/src/lib/call-to-action.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function CallToAction(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<section className="relative isolate px-6 py-32 sm:py-40 lg:px-8">
|
||||||
|
<svg
|
||||||
|
className="absolute inset-0 -z-10 h-full w-full stroke-black/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-white/10"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<pattern
|
||||||
|
id="1d4240dd-898f-445f-932d-e2872fd12de3"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
x="50%"
|
||||||
|
y={0}
|
||||||
|
patternUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<path d="M.5 200V.5H200" fill="none" />
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<svg
|
||||||
|
x="50%"
|
||||||
|
y={0}
|
||||||
|
className="overflow-visible fill-slate-200/20 dark:fill-slate-800/20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
|
||||||
|
strokeWidth={0}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<rect
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
strokeWidth={0}
|
||||||
|
fill="url(#1d4240dd-898f-445f-932d-e2872fd12de3)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 top-10 -z-10 flex transform-gpu justify-center overflow-hidden blur-3xl"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="aspect-[1108/632] w-[69.25rem] flex-none bg-gradient-to-r from-[#80caff] to-[#4f46e5] opacity-20"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<h2
|
||||||
|
id="cta"
|
||||||
|
className="text-3xl font-medium tracking-tight text-slate-950 sm:text-5xl dark:text-white"
|
||||||
|
>
|
||||||
|
Boost your productivity
|
||||||
|
<br />
|
||||||
|
Start using Nx Cloud today
|
||||||
|
</h2>
|
||||||
|
<p className="mx-auto mt-6 max-w-xl text-2xl leading-8 text-slate-700 dark:text-slate-300">
|
||||||
|
Experience the next generation of CI tooling.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
|
<a
|
||||||
|
href="https://cloud.nx.app"
|
||||||
|
className="rounded-md bg-slate-950 px-3.5 py-2.5 text-sm font-semibold text-slate-100 shadow-sm hover:bg-slate-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-100"
|
||||||
|
>
|
||||||
|
Get started
|
||||||
|
</a>
|
||||||
|
<Link
|
||||||
|
href="/contact"
|
||||||
|
title="Get in touch"
|
||||||
|
className="group text-sm font-semibold leading-6 text-slate-950 dark:text-white"
|
||||||
|
>
|
||||||
|
Contact us{' '}
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="inline-block transition group-hover:translate-x-1"
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
73
nx-dev/ui-cloud/src/lib/elements/bento-grid.tsx
Normal file
73
nx-dev/ui-cloud/src/lib/elements/bento-grid.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
export const BentoGrid = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
children?: ReactNode;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[24rem] md:grid-cols-2 lg:grid-cols-3',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BentoGridItem = ({
|
||||||
|
className,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
header,
|
||||||
|
url = '',
|
||||||
|
icon,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
title?: string | ReactNode;
|
||||||
|
description?: string | ReactNode;
|
||||||
|
header?: ReactNode;
|
||||||
|
icon?: ReactNode;
|
||||||
|
url?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'group/bento shadow-input row-span-1 flex flex-col justify-between space-y-4 overflow-hidden rounded-xl border border-slate-200 bg-white p-4 transition duration-200 dark:border-slate-800 dark:bg-slate-950 dark:shadow-none',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="pointer-events-none relative">
|
||||||
|
<div className="mb-2 mt-2 flex flex-row items-center gap-2 font-sans font-bold text-slate-600 dark:text-slate-200">
|
||||||
|
{icon} {title}
|
||||||
|
</div>
|
||||||
|
<div className="font-sans text-sm font-normal text-slate-600 dark:text-slate-400">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
{url ? (
|
||||||
|
<Link
|
||||||
|
href={url}
|
||||||
|
title="Learn more"
|
||||||
|
className="float-right text-sm font-medium transition duration-200 group-hover/bento:text-blue-500 group-hover/bento:dark:text-sky-500"
|
||||||
|
>
|
||||||
|
<span className="group absolute inset-0" />
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="inline-block transition duration-200 group-hover/bento:translate-x-2"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
{header}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
56
nx-dev/ui-cloud/src/lib/elements/spotlight.tsx
Normal file
56
nx-dev/ui-cloud/src/lib/elements/spotlight.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
|
||||||
|
export const Spotlight = ({
|
||||||
|
className,
|
||||||
|
fill,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
fill?: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
className={cx(
|
||||||
|
'animate-spotlight pointer-events-none absolute z-[1] h-[169%] w-[138%] opacity-0 lg:w-[84%]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 3787 2842"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<g filter="url(#filter)">
|
||||||
|
<ellipse
|
||||||
|
cx="1924.71"
|
||||||
|
cy="273.501"
|
||||||
|
rx="1924.71"
|
||||||
|
ry="273.501"
|
||||||
|
transform="matrix(-0.822377 -0.568943 -0.568943 0.822377 3631.88 2291.09)"
|
||||||
|
fill={fill || 'white'}
|
||||||
|
fillOpacity="0.21"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter
|
||||||
|
id="filter"
|
||||||
|
x="0.860352"
|
||||||
|
y="0.838989"
|
||||||
|
width="3785.16"
|
||||||
|
height="2840.26"
|
||||||
|
filterUnits="userSpaceOnUse"
|
||||||
|
colorInterpolationFilters="sRGB"
|
||||||
|
>
|
||||||
|
<feFlood floodOpacity="0" result="BackgroundImageFix"></feFlood>
|
||||||
|
<feBlend
|
||||||
|
mode="normal"
|
||||||
|
in="SourceGraphic"
|
||||||
|
in2="BackgroundImageFix"
|
||||||
|
result="shape"
|
||||||
|
/>
|
||||||
|
<feGaussianBlur
|
||||||
|
stdDeviation="151"
|
||||||
|
result="effect1_foregroundBlur_1065_8"
|
||||||
|
/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
71
nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx
Normal file
71
nx-dev/ui-cloud/src/lib/enhance-with-ai.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import {
|
||||||
|
CloudArrowDownIcon,
|
||||||
|
CodeBracketIcon,
|
||||||
|
RectangleGroupIcon,
|
||||||
|
ServerStackIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
|
||||||
|
const features = [
|
||||||
|
{
|
||||||
|
name: 'Task error insights',
|
||||||
|
description:
|
||||||
|
"Debug task errors on your CI pipeline directly in your pipeline's UI.",
|
||||||
|
icon: CodeBracketIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dynamic Nx Agent sizing',
|
||||||
|
description:
|
||||||
|
"Automatically adjust Nx Agents' resource classes and numbers depending on your workspace usage and needs.",
|
||||||
|
icon: ServerStackIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Task cache miss diagnosis',
|
||||||
|
description: 'Understand why a task has a cache miss and how to fix it.',
|
||||||
|
icon: CloudArrowDownIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Organization insights',
|
||||||
|
description:
|
||||||
|
"Understand your teams' workspaces: shared code usage, ownership, bottlenecks.",
|
||||||
|
icon: RectangleGroupIcon,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function EnhancedWithAi(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<section id="ai-for-your-ci">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-3xl text-center">
|
||||||
|
<span className="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-blue-400/30">
|
||||||
|
Coming soon
|
||||||
|
</span>
|
||||||
|
<SectionHeading as="h2" variant="title" id="deep-understanding">
|
||||||
|
AI for your CI
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading as="p" variant="subtitle" className="mt-6">
|
||||||
|
With the knowledge of your workspace structure, your CI and commit
|
||||||
|
history, Nx Cloud can optimize CI resource usage, help resolve
|
||||||
|
issues, provide powerful analytics and suggest refactorings.
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<dl className="mx-auto mt-16 grid max-w-5xl grid-cols-1 gap-6 sm:mt-20 md:grid-cols-2 lg:mt-24 lg:gap-12">
|
||||||
|
{features.map((feature) => (
|
||||||
|
<div key={feature.name} className="relative pl-9">
|
||||||
|
<dt className="text-base font-semibold leading-7 text-slate-950 dark:text-white">
|
||||||
|
<feature.icon
|
||||||
|
className="absolute left-1 top-1 h-5 w-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
{feature.name}
|
||||||
|
</dt>
|
||||||
|
<dd className="mt-2 text-base leading-7">
|
||||||
|
{feature.description}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
97
nx-dev/ui-cloud/src/lib/faster-and-cheaper.tsx
Normal file
97
nx-dev/ui-cloud/src/lib/faster-and-cheaper.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
'use client';
|
||||||
|
import { Variants, motion } from 'framer-motion';
|
||||||
|
import { Spotlight } from './elements/spotlight';
|
||||||
|
import { AnimateValue } from '@nx/nx-dev/ui-animations';
|
||||||
|
|
||||||
|
export function FasterAndCheaper(): JSX.Element {
|
||||||
|
const spotlight: Variants = {
|
||||||
|
offscreen: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
onscreen: {
|
||||||
|
display: 'block',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<motion.div
|
||||||
|
initial="offscreen"
|
||||||
|
whileInView="onscreen"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="relative mx-auto max-w-7xl px-6 lg:px-8"
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
variants={spotlight}
|
||||||
|
className="absolute left-1/2 z-0 h-[50%] w-[50%] transform-gpu"
|
||||||
|
>
|
||||||
|
<Spotlight className="w-full max-w-full" fill="pink" />
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<h2 className="text-3xl font-medium tracking-tight text-slate-950 sm:text-5xl dark:text-white">
|
||||||
|
Both faster & cheaper
|
||||||
|
</h2>
|
||||||
|
<p className="mt-6 text-2xl leading-7 text-slate-700 dark:text-slate-300">
|
||||||
|
Nx Cloud makes your CI significantly faster and cheaper, while also
|
||||||
|
making it more maintainable and reliable.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="relative z-20 mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-5xl">
|
||||||
|
<div className="grid grid-cols-1 gap-x-8 gap-y-16 lg:grid-cols-2">
|
||||||
|
<article className="group/card relative grid transform-gpu cursor-default items-center gap-4 overflow-hidden rounded-xl border border-slate-200 bg-white/50 backdrop-blur-sm transition duration-200 hover:-translate-y-4 hover:shadow-xl md:col-span-1 dark:border-slate-800 dark:bg-slate-950/50 dark:shadow-none">
|
||||||
|
<div className="p-4 text-center">
|
||||||
|
<div className="mt-2 text-3xl font-medium leading-7 text-slate-950 transition duration-200 lg:text-8xl dark:text-white">
|
||||||
|
<AnimateValue num={30} suffix=" - " />
|
||||||
|
<AnimateValue num={70} suffix="%" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-3xl font-medium leading-7 text-slate-950 transition duration-200 lg:text-5xl dark:text-white">
|
||||||
|
Faster CI
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 text-sm text-slate-600 transition duration-200 group-hover/card:text-slate-400 dark:text-slate-400 group-hover/card:dark:text-slate-600">
|
||||||
|
Reported by enterprises using Nx Cloud.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<article className="group/card relative grid transform-gpu cursor-default items-center gap-4 overflow-hidden rounded-xl border border-slate-200 bg-white/50 backdrop-blur-sm transition duration-200 hover:-translate-y-4 hover:shadow-xl md:col-span-1 dark:border-slate-800 dark:bg-slate-950/50 dark:shadow-none">
|
||||||
|
<div className="p-4 text-center">
|
||||||
|
<div className="mt-4 text-center text-3xl font-medium leading-7 text-slate-950 transition duration-200 lg:text-5xl dark:text-white">
|
||||||
|
Halve your CI bill
|
||||||
|
</div>
|
||||||
|
<div className="mt-6">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="w-28 shrink-0 border-r-2 border-slate-200 py-3 pr-2 text-right text-slate-700 transition duration-200 dark:border-slate-800 dark:text-slate-300">
|
||||||
|
CI
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow py-1.5 font-semibold">
|
||||||
|
<div className="w-full flex-grow items-center justify-end rounded-r-lg border border-l-0 border-slate-200 bg-slate-100 px-4 py-2 text-right text-slate-900 transition duration-200 dark:border-slate-800 dark:bg-slate-700 dark:text-white">
|
||||||
|
<span className="drop-shadow-sm">$6k</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="w-28 shrink-0 border-r-2 border-slate-200 py-3 pr-2 text-right font-medium text-slate-700 transition duration-200 dark:border-slate-800 dark:text-slate-300">
|
||||||
|
CI + Nx Cloud
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow py-1.5 font-semibold">
|
||||||
|
<div className="w-1/2 rounded-r-lg border border-l-0 border-slate-200 bg-gradient-to-r from-emerald-500 to-green-500 px-4 py-2 text-right text-white transition duration-200 dark:border-slate-800">
|
||||||
|
<span className="drop-shadow-sm">$3.2k</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="mt-6 text-xs text-slate-400 transition duration-200 dark:text-slate-600">
|
||||||
|
<span className="underline">
|
||||||
|
Cost per month for CI compute.
|
||||||
|
</span>{' '}
|
||||||
|
Data collected based on a typical month of CI runs measured on
|
||||||
|
the Nx OSS monorepo.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
202
nx-dev/ui-cloud/src/lib/hero.tsx
Normal file
202
nx-dev/ui-cloud/src/lib/hero.tsx
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
'use client';
|
||||||
|
import { Dialog, Transition } from '@headlessui/react';
|
||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
import { PlayIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { ComponentProps, Fragment, useState } from 'react';
|
||||||
|
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
import { MovingBorder } from '@nx/nx-dev/ui-animations';
|
||||||
|
import Image from 'next/image';
|
||||||
|
|
||||||
|
export function Hero(): JSX.Element {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-3xl text-center">
|
||||||
|
<SectionHeading as="h1" variant="display">
|
||||||
|
Fast CI <br /> Built for Monorepos
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading
|
||||||
|
as="p"
|
||||||
|
variant="subtitle"
|
||||||
|
className="mx-auto mt-6 max-w-xl"
|
||||||
|
>
|
||||||
|
Nx Cloud is the end-to-end solution for smart, efficient and
|
||||||
|
maintainable CI.
|
||||||
|
</SectionHeading>
|
||||||
|
<div className="mt-10 flex items-center justify-center gap-x-6">
|
||||||
|
<ButtonLink
|
||||||
|
href="https://cloud.nx.app"
|
||||||
|
title="Get started"
|
||||||
|
variant="primary"
|
||||||
|
size="default"
|
||||||
|
>
|
||||||
|
Get started
|
||||||
|
</ButtonLink>
|
||||||
|
<ButtonLink
|
||||||
|
href="/ci/intro/ci-with-nx"
|
||||||
|
title="Learn More"
|
||||||
|
variant="secondary"
|
||||||
|
size="default"
|
||||||
|
>
|
||||||
|
Learn More
|
||||||
|
</ButtonLink>
|
||||||
|
<a
|
||||||
|
href="https://staging.nx.app/orgs/62d013d4d26f260059f7765e/workspaces/62d013ea0852fe0a2df74438/overview"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
title="Live demo"
|
||||||
|
className="group text-sm font-semibold leading-6 text-slate-950 dark:text-white"
|
||||||
|
>
|
||||||
|
Live demo{' '}
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className="inline-block transition group-hover:translate-x-1"
|
||||||
|
>
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative overflow-hidden pt-16">
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="relative mb-6 mt-0 overflow-hidden rounded-xl bg-transparent p-[0.5px] text-xl shadow-lg">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<MovingBorder duration={4500} rx="5%" ry="5%">
|
||||||
|
<div className="h-20 w-20 bg-[radial-gradient(var(--blue-500)_40%,transparent_60%)] opacity-[0.8] dark:bg-[radial-gradient(var(--pink-500)_40%,transparent_60%)]" />
|
||||||
|
</MovingBorder>
|
||||||
|
</div>
|
||||||
|
<picture className="relative flex h-full w-full items-center justify-center overflow-hidden rounded-xl border border-slate-100 bg-slate-50 antialiased backdrop-blur-xl dark:border-slate-900 dark:bg-slate-900/[0.8]">
|
||||||
|
<Image
|
||||||
|
src="/images/cloud/nrwl-ocean.avif"
|
||||||
|
alt="App screenshot: overview"
|
||||||
|
width={2550}
|
||||||
|
height={1622}
|
||||||
|
loading="eager"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</picture>
|
||||||
|
<div className="absolute inset-0 z-10 grid h-full w-full items-center justify-center">
|
||||||
|
<PlayButton onClick={() => setIsOpen(true)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*MODAL*/}
|
||||||
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
|
<Dialog
|
||||||
|
as="div"
|
||||||
|
open={isOpen}
|
||||||
|
onClose={() => setIsOpen(false)}
|
||||||
|
className="relative z-10"
|
||||||
|
>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div className="fixed inset-0 bg-black/25 backdrop-blur-sm" />
|
||||||
|
</Transition.Child>
|
||||||
|
<div className="fixed inset-0 overflow-y-auto">
|
||||||
|
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0 scale-95"
|
||||||
|
enterTo="opacity-100 scale-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100 scale-100"
|
||||||
|
leaveTo="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<Dialog.Panel className="relative w-auto transform overflow-hidden rounded-2xl border border-slate-600 text-left align-middle shadow-xl transition-all focus:outline-none dark:border-slate-800">
|
||||||
|
<iframe
|
||||||
|
width="812"
|
||||||
|
height="468"
|
||||||
|
src="https://www.youtube.com/embed/4VI-q943J3o?si=3tR-EkCKLfLvHYzL"
|
||||||
|
title="YouTube video player"
|
||||||
|
frameBorder="0"
|
||||||
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||||
|
allowFullScreen
|
||||||
|
className="max-w-full"
|
||||||
|
/>
|
||||||
|
</Dialog.Panel>
|
||||||
|
</Transition.Child>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PlayButton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: ComponentProps<'div'>): JSX.Element {
|
||||||
|
const parent = {
|
||||||
|
initial: {
|
||||||
|
width: 82,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
width: 296,
|
||||||
|
transition: {
|
||||||
|
duration: 0.125,
|
||||||
|
type: 'tween',
|
||||||
|
ease: 'easeOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const child = {
|
||||||
|
initial: {
|
||||||
|
opacity: 0,
|
||||||
|
x: -6,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.015,
|
||||||
|
type: 'tween',
|
||||||
|
ease: 'easeOut',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
'group relative overflow-hidden rounded-full bg-transparent p-[1px] shadow-md',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<MovingBorder duration={5000} rx="5%" ry="5%">
|
||||||
|
<div className="h-20 w-20 bg-[radial-gradient(var(--blue-500)_40%,transparent_60%)] opacity-[0.8] dark:bg-[radial-gradient(var(--pink-500)_40%,transparent_60%)]" />
|
||||||
|
</MovingBorder>
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
initial="initial"
|
||||||
|
whileHover="hover"
|
||||||
|
variants={parent}
|
||||||
|
className="relative isolate flex h-20 w-20 cursor-pointer items-center justify-center gap-6 rounded-full border border-slate-100 bg-white/[0.6] p-6 text-sm text-slate-950 antialiased backdrop-blur-xl"
|
||||||
|
>
|
||||||
|
<PlayIcon
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute left-6 top-6 h-8 w-8"
|
||||||
|
/>
|
||||||
|
<motion.div variants={child} className="absolute left-20 top-4 w-48">
|
||||||
|
<p className="text-base font-medium">See how Nx Cloud works</p>
|
||||||
|
<p className="text-slate-700">In under 9 minutes</p>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
25
nx-dev/ui-cloud/src/lib/layout.tsx
Normal file
25
nx-dev/ui-cloud/src/lib/layout.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
export function Layout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="relative isolate pt-14">
|
||||||
|
<div
|
||||||
|
className="absolute inset-x-0 -top-40 -z-10 h-full transform-gpu overflow-hidden blur-3xl sm:-top-80"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[36.125rem] -translate-x-1/2 rotate-[30deg] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-20 sm:left-[calc(50%-30rem)] sm:w-[72.1875rem]"
|
||||||
|
style={{
|
||||||
|
clipPath:
|
||||||
|
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<main className="py-24 sm:py-32">{children}</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
159
nx-dev/ui-cloud/src/lib/statistics.tsx
Normal file
159
nx-dev/ui-cloud/src/lib/statistics.tsx
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
'use client';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the total number of years worth of compute.
|
||||||
|
*
|
||||||
|
* @param {number} millis - The total number of seconds.
|
||||||
|
* @return {number} The total number of years.
|
||||||
|
*/
|
||||||
|
function getTotalYears(millis: number): number {
|
||||||
|
/**
|
||||||
|
* The number of millis in a year is approximately:
|
||||||
|
* 86 400 000 millis/day * 365.25 days/year ≈ 31 557 600 000 seconds/year.
|
||||||
|
*/
|
||||||
|
const yearMillis = Number(31557600000);
|
||||||
|
return Math.round(millis / yearMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the time saved from a remote API.
|
||||||
|
*
|
||||||
|
* @returns {Promise} A promise that resolves to an object containing the time saved data.
|
||||||
|
* @returns {Date} The date the time saved data was retrieved.
|
||||||
|
* @returns {number} The time saved in the last 7 days.
|
||||||
|
* @returns {number} The time saved in the last 30 days.
|
||||||
|
* @returns {number} The time's saved since the start.
|
||||||
|
*/
|
||||||
|
function fetchTimeSaved(): Promise<{
|
||||||
|
date: Date;
|
||||||
|
last7days: number;
|
||||||
|
last30days: number;
|
||||||
|
sinceStart: number;
|
||||||
|
}> {
|
||||||
|
const apiUrl = 'https://cloud.nx.app/time-saved';
|
||||||
|
|
||||||
|
return fetch(apiUrl)
|
||||||
|
.then(
|
||||||
|
(response) =>
|
||||||
|
response.json() as Promise<{
|
||||||
|
date: Date;
|
||||||
|
last7days: number;
|
||||||
|
last30days: number;
|
||||||
|
sinceStart: number;
|
||||||
|
}>
|
||||||
|
)
|
||||||
|
.catch(() => ({
|
||||||
|
date: new Date(),
|
||||||
|
last7days: Math.round(Math.random() * 1000000000),
|
||||||
|
last30days: Math.round(Math.random() * 100000000000),
|
||||||
|
sinceStart: Math.round(Math.random() * 10000000000000),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Developers using Nx',
|
||||||
|
value: 2,
|
||||||
|
suffix: 'M+',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Active workspaces',
|
||||||
|
value: '4k',
|
||||||
|
suffix: '+',
|
||||||
|
},
|
||||||
|
{ id: 2, name: 'Compute time saved', value: 800, suffix: '+ years' },
|
||||||
|
{ id: 4, name: 'Runs daily', value: 100, suffix: 'k+' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function Statistics(): JSX.Element {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
delay: i || 0,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const itemVariants = {
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.25,
|
||||||
|
duration: 0.65,
|
||||||
|
ease: 'easeOut',
|
||||||
|
when: 'beforeChildren',
|
||||||
|
staggerChildren: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
y: 4,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const [timeSaved, setTimeSaved] = useState<number>(800);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let ignore = false;
|
||||||
|
fetchTimeSaved().then((data) => {
|
||||||
|
if (!ignore) {
|
||||||
|
setTimeSaved(getTotalYears(data.sinceStart));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
ignore = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-xl">
|
||||||
|
<SectionHeading as="h2" variant="title" id="statistics">
|
||||||
|
Trusted by startups and Fortune 500 companies
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading as="p" variant="subtitle" className="mt-6">
|
||||||
|
Nx Cloud provides plans for open source projects, startups, and large
|
||||||
|
enterprises.
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
<motion.dl
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-10 text-slate-950 sm:mt-20 sm:grid-cols-2 sm:gap-y-16 lg:mx-0 lg:max-w-none lg:grid-cols-4 dark:text-white"
|
||||||
|
>
|
||||||
|
{stats.map((stat, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={`statistic-${idx}`}
|
||||||
|
custom={idx}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex flex-col gap-y-3 border-l border-black/10 pl-6 dark:border-white/10"
|
||||||
|
>
|
||||||
|
<dt className="text-sm leading-6 text-slate-600 dark:text-slate-500">
|
||||||
|
{stat.name}
|
||||||
|
</dt>
|
||||||
|
<dd className="order-first text-3xl font-semibold tracking-tight">
|
||||||
|
{stat.name === 'Compute time saved' ? timeSaved : stat.value}
|
||||||
|
{stat.suffix}
|
||||||
|
</dd>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</motion.dl>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
47
nx-dev/ui-cloud/src/lib/trusted-by.tsx
Normal file
47
nx-dev/ui-cloud/src/lib/trusted-by.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
AwsAmplifyIcon,
|
||||||
|
CapitalOneIcon,
|
||||||
|
CiscoIcon,
|
||||||
|
RedwoodJsIcon,
|
||||||
|
RoyalBankOfCanadaIcon,
|
||||||
|
ShopifyIcon,
|
||||||
|
StorybookIcon,
|
||||||
|
VmwareIcon,
|
||||||
|
} from '@nx/nx-dev/ui-icons';
|
||||||
|
export function TrustedBy(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<section className="">
|
||||||
|
<div className="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8 lg:pb-16">
|
||||||
|
<h2 className="text-center text-lg font-medium leading-8 text-slate-400">
|
||||||
|
Startups and Fortune 500 companies trust Nx Cloud
|
||||||
|
</h2>
|
||||||
|
<div className="mt-4 grid grid-cols-2 gap-0.5 md:grid-cols-8">
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<RoyalBankOfCanadaIcon className="h-14 w-14 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<AwsAmplifyIcon className="h-14 w-14 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<CapitalOneIcon className="h-28 w-28 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<ShopifyIcon className="h-12 w-12 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<StorybookIcon className="h-12 w-12 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center ">
|
||||||
|
<VmwareIcon className="h-28 w-28 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<RedwoodJsIcon className="h-12 w-12 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 flex items-center justify-center">
|
||||||
|
<CiscoIcon className="h-20 w-20 text-slate-300 dark:text-slate-600" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
826
nx-dev/ui-cloud/src/lib/understand-workspace.tsx
Normal file
826
nx-dev/ui-cloud/src/lib/understand-workspace.tsx
Normal file
@ -0,0 +1,826 @@
|
|||||||
|
'use client';
|
||||||
|
import {
|
||||||
|
ArrowLongRightIcon,
|
||||||
|
ArrowsRightLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
ClipboardDocumentIcon,
|
||||||
|
DocumentIcon,
|
||||||
|
InboxArrowDownIcon,
|
||||||
|
LockClosedIcon,
|
||||||
|
SparklesIcon,
|
||||||
|
} from '@heroicons/react/24/outline';
|
||||||
|
import { SectionHeading } from '@nx/nx-dev/ui-common';
|
||||||
|
import { BentoGrid, BentoGridItem } from './elements/bento-grid';
|
||||||
|
import { cx } from '@nx/nx-dev/ui-primitives';
|
||||||
|
import { animate, motion, useMotionValue, useTransform } from 'framer-motion';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export function UnderstandWorkspace(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||||
|
<div className="mx-auto max-w-2xl text-center">
|
||||||
|
<SectionHeading as="h2" variant="title" id="deep-understanding">
|
||||||
|
Deep understanding of your workspace
|
||||||
|
</SectionHeading>
|
||||||
|
<SectionHeading as="p" variant="subtitle" className="mt-6">
|
||||||
|
Nx Cloud knows your build tools, how your projects relate to each
|
||||||
|
other, and what every task does.
|
||||||
|
</SectionHeading>
|
||||||
|
</div>
|
||||||
|
{/*FEATURES CONTAINER*/}
|
||||||
|
<BentoGrid className="mx-auto mt-20 w-full md:auto-rows-[22rem]">
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<BentoGridItem
|
||||||
|
key={i}
|
||||||
|
title={item.title}
|
||||||
|
description={item.description}
|
||||||
|
header={item.header}
|
||||||
|
className={cx('[&>p:text-lg]', item.className)}
|
||||||
|
icon={item.icon}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</BentoGrid>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Caching = () => {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'build',
|
||||||
|
project: 'website',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'test',
|
||||||
|
project: 'express',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'build',
|
||||||
|
project: 'eslint',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'lint',
|
||||||
|
project: 'autoloan',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'test',
|
||||||
|
project: 'website',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'lint',
|
||||||
|
project: 'website',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'build-base',
|
||||||
|
project: 'express',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'build',
|
||||||
|
project: 'express',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'lint',
|
||||||
|
project: 'express',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cache: 'remote',
|
||||||
|
target: 'test',
|
||||||
|
project: 'autoloan',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
|
||||||
|
>
|
||||||
|
<motion.div className="flex items-center gap-1.5 text-center text-sm italic">
|
||||||
|
<SparklesIcon aria-hidden="true" className="h-4 w-4" />
|
||||||
|
<span className="font-semibold">
|
||||||
|
<Counter value={items.length + 123} duration={5} />
|
||||||
|
</span>
|
||||||
|
tasks replayed instantly with cache
|
||||||
|
</motion.div>
|
||||||
|
<div className="flex flex-1 flex-col divide-y divide-neutral-100 overflow-auto rounded-lg border border-neutral-100 dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-950">
|
||||||
|
{items.map((i, idx) => (
|
||||||
|
<div
|
||||||
|
key={`project-${i}-${idx}`}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-emerald-500" />
|
||||||
|
<div className="flex min-w-[4rem]">
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
{i.cache}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="grow truncate text-left text-sm font-medium"
|
||||||
|
data-testid="task-target"
|
||||||
|
>
|
||||||
|
{i.target}
|
||||||
|
</div>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-500/10 dark:text-green-400 dark:ring-green-500/20">
|
||||||
|
{i.project}
|
||||||
|
</span>
|
||||||
|
<div className="text-xs">< 1s</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlakyTasks = () => {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const itemVariants = {
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.2,
|
||||||
|
duration: 0.275,
|
||||||
|
ease: 'easeOut',
|
||||||
|
when: 'beforeChildren',
|
||||||
|
staggerChildren: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
x: -100,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
|
||||||
|
>
|
||||||
|
<motion.div className="flex items-center gap-1.5 text-center text-sm">
|
||||||
|
Running
|
||||||
|
<ChevronRightIcon aria-hidden="true" className="h-3 w-3" />
|
||||||
|
<code className="text-xs font-medium">
|
||||||
|
nx affected --targets=e2e,test
|
||||||
|
</code>
|
||||||
|
</motion.div>
|
||||||
|
<div className="flex flex-1 flex-col divide-y divide-neutral-100 overflow-auto rounded-lg border border-neutral-100 dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-950">
|
||||||
|
<motion.div
|
||||||
|
custom={1}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-emerald-500" />
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
miss
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
<motion.span
|
||||||
|
custom={3}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="inline-flex cursor-default items-center rounded-md bg-yellow-400/10 px-2 py-1 text-xs font-medium text-yellow-500 ring-1 ring-inset ring-yellow-400/20 dark:bg-yellow-400/10 dark:text-yellow-500 dark:ring-yellow-400/20"
|
||||||
|
>
|
||||||
|
flaky
|
||||||
|
</motion.span>
|
||||||
|
<motion.span
|
||||||
|
custom={2}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20"
|
||||||
|
>
|
||||||
|
1 retry
|
||||||
|
</motion.span>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-500/10 dark:text-green-400 dark:ring-green-500/20">
|
||||||
|
website
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={2}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-red-500"></div>
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="whitespace-nowrap text-sm font-medium">
|
||||||
|
Failed
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">e2e</div>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
1 retry
|
||||||
|
</span>
|
||||||
|
<span className="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-700 ring-1 ring-inset ring-red-600/10 dark:bg-red-400/10 dark:text-red-400 dark:ring-red-400/20">
|
||||||
|
website
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={3}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="whitespace-nowrap text-sm font-medium">
|
||||||
|
In progress
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">
|
||||||
|
test
|
||||||
|
</div>
|
||||||
|
<motion.span
|
||||||
|
custom={15}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="inline-flex cursor-default items-center rounded-md bg-yellow-400/10 px-2 py-1 text-xs font-medium text-yellow-500 ring-1 ring-inset ring-yellow-400/20"
|
||||||
|
>
|
||||||
|
flaky
|
||||||
|
</motion.span>
|
||||||
|
<motion.span
|
||||||
|
custom={10}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20"
|
||||||
|
>
|
||||||
|
1 retry
|
||||||
|
</motion.span>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
ui-cdk
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={4}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-emerald-500" />
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
remote
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">
|
||||||
|
lint
|
||||||
|
</div>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20 dark:bg-green-500/10 dark:text-green-400 dark:ring-green-500/20">
|
||||||
|
website
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={5}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="whitespace-nowrap text-sm font-medium">
|
||||||
|
In progress
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">
|
||||||
|
build
|
||||||
|
</div>
|
||||||
|
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
ui-cdk
|
||||||
|
</span>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SplitE2eTests = () => {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const uiDialogsTests = [
|
||||||
|
'e2e/libs/ui-cdk/spec-1.tsx',
|
||||||
|
'e2e/libs/ui-cdk/spec-2.tsx',
|
||||||
|
];
|
||||||
|
const websiteTests = [
|
||||||
|
'e2e/apps/website/spec-1.tsx',
|
||||||
|
'e2e/apps/website/spec-2.tsx',
|
||||||
|
'e2e/apps/website/spec-3.tsx',
|
||||||
|
'e2e/apps/website/spec-4.tsx',
|
||||||
|
'e2e/apps/website/spec-5.tsx',
|
||||||
|
'e2e/apps/website/spec-6.tsx',
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
|
||||||
|
>
|
||||||
|
<motion.div className="flex items-center gap-1.5 text-center text-sm">
|
||||||
|
Running
|
||||||
|
<ChevronRightIcon aria-hidden="true" className="h-3 w-3" />
|
||||||
|
<code className="text-xs font-medium">nx affected --targets=e2e</code>
|
||||||
|
</motion.div>
|
||||||
|
<div className="flex flex-1 flex-col divide-y divide-neutral-100 overflow-auto rounded-lg border border-neutral-100 bg-white dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-950">
|
||||||
|
<div className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="whitespace-nowrap text-sm font-medium">
|
||||||
|
In progress
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">e2e</div>
|
||||||
|
<span className="inline-flex items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
ui-cdk
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{uiDialogsTests.map((i, idx) => (
|
||||||
|
<div
|
||||||
|
key={`${i}-${idx}`}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="w-4" />
|
||||||
|
<ArrowLongRightIcon aria-hidden="true" className="h-4 w-4" />
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm">{i}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex min-w-[5rem]">
|
||||||
|
<span className="whitespace-nowrap text-sm font-medium">
|
||||||
|
In progress
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm font-medium">e2e</div>
|
||||||
|
<span className="inline-flex items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20">
|
||||||
|
website
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{websiteTests.map((i, idx) => (
|
||||||
|
<div
|
||||||
|
key={`${i}-${idx}`}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
|
||||||
|
>
|
||||||
|
<div className="w-4" />
|
||||||
|
<ArrowLongRightIcon aria-hidden="true" className="h-4 w-4" />
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500" />
|
||||||
|
</div>
|
||||||
|
<div className="grow truncate text-left text-sm">{i}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const TaskDistribution = () => {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const itemVariants = {
|
||||||
|
visible: (i: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: {
|
||||||
|
delay: i * 0.2,
|
||||||
|
duration: 0.275,
|
||||||
|
ease: 'easeOut',
|
||||||
|
when: 'beforeChildren',
|
||||||
|
staggerChildren: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
x: -100,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const agent1Items = ['website:build-base', 'website:build'];
|
||||||
|
const agent2Items = ['docs:lint', 'express:test', 'website:lint'];
|
||||||
|
const agent3Items = ['graph-client:build', 'plugin:test'];
|
||||||
|
const nxReplayItems = [
|
||||||
|
'graph-client:lint',
|
||||||
|
'plugin:lint',
|
||||||
|
'website:test',
|
||||||
|
'vite:test',
|
||||||
|
'vite:build',
|
||||||
|
];
|
||||||
|
|
||||||
|
const notStartedTasks = ['js:build', 'js:lint'];
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="relative flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
|
||||||
|
>
|
||||||
|
<div className="grid max-h-full grid-cols-2 items-stretch gap-8 overflow-hidden p-1 lg:grid-cols-3">
|
||||||
|
<motion.div
|
||||||
|
custom={2}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="relative overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-neutral-100 dark:bg-slate-950 dark:ring-slate-700"
|
||||||
|
>
|
||||||
|
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
|
||||||
|
Main Workflow
|
||||||
|
</p>
|
||||||
|
<div className="mt-2 flex flex-col gap-1">
|
||||||
|
<p className="overflow-x-auto truncate py-2 font-mono text-xs font-medium text-slate-900 dark:text-slate-400">
|
||||||
|
nx affected --target=build,lint,test
|
||||||
|
</p>
|
||||||
|
<div className="flex h-1.5 w-full flex-row rounded-full">
|
||||||
|
<div
|
||||||
|
title="2 tasks in completed"
|
||||||
|
className="cursor-pointer rounded-l-full bg-green-400 dark:bg-green-600"
|
||||||
|
style={{ flexGrow: 8 }}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
title="12 tasks in progress"
|
||||||
|
className="cursor-pointer bg-yellow-400 dark:bg-yellow-600"
|
||||||
|
style={{ flexGrow: 12 }}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
title="24 tasks not started"
|
||||||
|
className="cursor-pointer rounded-r-full bg-slate-100 dark:bg-slate-600"
|
||||||
|
style={{ flexGrow: 24 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 flex flex-1 flex-col divide-y divide-neutral-100 overflow-auto rounded-lg border border-neutral-100 dark:divide-slate-700 dark:border-slate-700">
|
||||||
|
{notStartedTasks.map((i, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={`${i}-${idx}`}
|
||||||
|
custom={idx + 3}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-800/40"
|
||||||
|
>
|
||||||
|
<div className="text-xs">{i}</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={3}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="relative flex flex-col gap-2 overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-neutral-100 dark:bg-slate-950 dark:ring-slate-700"
|
||||||
|
>
|
||||||
|
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
|
||||||
|
Nx Agents
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-1 flex-col overflow-auto">
|
||||||
|
<motion.div custom={4} variants={itemVariants} className="relative">
|
||||||
|
<div className="sticky top-0 z-10 mt-1 rounded-md border border-neutral-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
|
||||||
|
<div className="flex items-center gap-x-2 text-xs font-medium">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
|
||||||
|
</div>
|
||||||
|
Agent 1<span className="flex-grow"></span>
|
||||||
|
<span className="mr-1 opacity-80">{agent1Items.length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className="my-2 overflow-y-auto overflow-x-hidden">
|
||||||
|
{agent1Items.map((i, idx) => (
|
||||||
|
<motion.li
|
||||||
|
key={`agent-${i}-${idx}`}
|
||||||
|
custom={idx + 5}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="truncate p-1 pl-4 text-xs"
|
||||||
|
>
|
||||||
|
{i}
|
||||||
|
</motion.li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div custom={6} variants={itemVariants} className="relative">
|
||||||
|
<div className="sticky top-0 z-10 mt-1 rounded-md border border-neutral-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
|
||||||
|
<div className="flex items-center gap-x-2 text-xs font-medium">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
|
||||||
|
</div>
|
||||||
|
Agent 2<span className="flex-grow"></span>
|
||||||
|
<span className="mr-1 opacity-80">{agent2Items.length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className="my-2 overflow-y-auto overflow-x-hidden">
|
||||||
|
{agent2Items.map((i, idx) => (
|
||||||
|
<motion.li
|
||||||
|
key={`agent-${i}-${idx}`}
|
||||||
|
custom={idx + 7}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="truncate p-1 pl-4 text-xs"
|
||||||
|
>
|
||||||
|
{i}
|
||||||
|
</motion.li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={8}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="relative hidden lg:block"
|
||||||
|
>
|
||||||
|
<div className="sticky top-0 z-10 mt-1 rounded-md border border-neutral-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
|
||||||
|
<div className="flex items-center gap-x-2 text-xs font-medium">
|
||||||
|
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
|
||||||
|
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
|
||||||
|
</div>
|
||||||
|
Agent 3<span className="flex-grow"></span>
|
||||||
|
<span className="mr-1 opacity-80">{agent3Items.length}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul className="my-2 overflow-y-auto overflow-x-hidden">
|
||||||
|
{agent3Items.map((i, idx) => (
|
||||||
|
<motion.li
|
||||||
|
key={`agent-${i}-${idx}`}
|
||||||
|
custom={idx + 9}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="truncate p-1 pl-4 text-xs"
|
||||||
|
>
|
||||||
|
{i}
|
||||||
|
</motion.li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
custom={4}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="relative hidden flex-col gap-2 overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-neutral-100 lg:flex dark:bg-slate-950 dark:ring-slate-700"
|
||||||
|
>
|
||||||
|
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
|
||||||
|
Nx Replay
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-1 flex-col divide-y divide-neutral-100 overflow-auto rounded-lg border border-neutral-100 dark:divide-slate-800 dark:border-slate-800">
|
||||||
|
{nxReplayItems.map((i, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={`replay-${i}-${idx}`}
|
||||||
|
custom={idx + 10}
|
||||||
|
variants={itemVariants}
|
||||||
|
className="flex w-full flex-row items-center gap-2 p-2 text-xs transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-800/40"
|
||||||
|
>
|
||||||
|
{i}
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ActionableErrorFeedback = () => {
|
||||||
|
const variants = {
|
||||||
|
hidden: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
when: 'afterChildren',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
variants={variants}
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-1"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
{/*STATUS*/}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-red-500"></div>
|
||||||
|
<span
|
||||||
|
className="whitespace-nowrap text-lg font-medium"
|
||||||
|
data-testid="status-label"
|
||||||
|
>
|
||||||
|
Failed
|
||||||
|
</span>
|
||||||
|
with code: 1
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*COMPARE & FLAKY*/}
|
||||||
|
<div className="flex items-center justify-end gap-1.5">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="cursor-default rounded bg-white px-2 py-1 text-xs font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:text-slate-400 dark:ring-slate-700"
|
||||||
|
>
|
||||||
|
Compare to similar tasks
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="cursor-default rounded bg-white px-2 py-1 text-xs font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 dark:bg-white/10 dark:text-slate-400 dark:ring-slate-700"
|
||||||
|
>
|
||||||
|
Set as "not flaky"
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="border-b border-neutral-100 dark:border-slate-700">
|
||||||
|
<div className="-mb-px flex space-x-4">
|
||||||
|
<span className="cursor-default whitespace-nowrap border-b-2 border-transparent px-0.5 py-2 text-xs font-medium text-gray-500 dark:text-slate-400">
|
||||||
|
Attempt 1
|
||||||
|
</span>
|
||||||
|
<span className="cursor-default whitespace-nowrap border-b-2 border-blue-500 px-0.5 py-2 text-xs font-medium text-blue-500 dark:border-sky-600 dark:text-sky-600">
|
||||||
|
Attempt 2
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs">Feb 23, 2024 08:57:49 - 08:57:54 (4s)</p>
|
||||||
|
<motion.div className="terminal-output flex max-h-full min-h-[2rem] flex-col overflow-visible rounded-lg border border-slate-200 bg-slate-50 font-mono text-xs leading-normal text-slate-800 subpixel-antialiased dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200">
|
||||||
|
<div className="flex items-center justify-between gap-4 rounded-t-lg border-b border-slate-200 bg-slate-100 px-2 py-1 dark:border-slate-700 dark:bg-slate-800">
|
||||||
|
<div className="font-sans text-sm font-medium">
|
||||||
|
<span className="group relative flex cursor-pointer items-center overflow-hidden whitespace-nowrap subpixel-antialiased dark:text-slate-300">
|
||||||
|
<span>nx run nx-dev:build</span>
|
||||||
|
<span className="transform opacity-0 transition-all">
|
||||||
|
<ClipboardDocumentIcon className="h-4 w-4" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-auto">
|
||||||
|
<pre className="overflow-x-hidden p-1 dark:text-slate-400">
|
||||||
|
{`nx run nx-dev:sitemap ✨ [next-sitemap]
|
||||||
|
Loading next-sitemap config:file:///home/workflows/workspace/nx-dev/nx-dev/next-sitemap.config.js
|
||||||
|
❌ [next-sitemap] Unable to find export-maker.
|
||||||
|
Make sure to build the project using "next build" command
|
||||||
|
node:internal/process/promises:289
|
||||||
|
triggerUncaughtException(err, true /* fromPromise */); ^
|
||||||
|
[Error:ENOENT: no such file or directory, stat '/home/workflows/workspace/dist/nx-dev/nx-dev/.next/export-marker.json']
|
||||||
|
errno: -2, code: 'ENOENT', syscall: 'stat', path: '/home/workflows/workspace/dist/nx-dev/nx-dev/.next/export-marker.json'
|
||||||
|
Node.js v20.9.0 Warning: command "pnpm next-sitemap --config
|
||||||
|
./nx-dev/nx-dev/next-sitemap.config.js" exited with non-zero status code`}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Counter({
|
||||||
|
value,
|
||||||
|
duration = 2,
|
||||||
|
}: {
|
||||||
|
value: number;
|
||||||
|
duration?: number;
|
||||||
|
}) {
|
||||||
|
const count = useMotionValue(0);
|
||||||
|
const rounded = useTransform(count, Math.round);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const animation = animate(count, value, {
|
||||||
|
type: 'tween',
|
||||||
|
ease: 'easeOut',
|
||||||
|
duration,
|
||||||
|
});
|
||||||
|
|
||||||
|
return animation.stop;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <motion.span>{rounded}</motion.span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
title: 'Nx Agents: seamless task distribution',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Nx Cloud manages CI agent machines, offloading complex machine
|
||||||
|
provisioning from your CI provider. Integration is as simple as
|
||||||
|
initiating a distributed run on your provider, with Nx Agents handling
|
||||||
|
the rest - streamlining operations and enhancing cost efficiency.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
header: <TaskDistribution />,
|
||||||
|
className: 'md:col-span-2',
|
||||||
|
icon: <ArrowsRightLeftIcon className="h-4 w-4 text-neutral-500" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Nx Replay: secure computation cache',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Nx Cloud caches and restores task results, across PRs and local
|
||||||
|
machines, drastically accelerating your CI runs.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
header: <Caching />,
|
||||||
|
className: 'md:col-span-1',
|
||||||
|
icon: <LockClosedIcon className="h-4 w-4 text-neutral-500" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Atomizer: E2E test splitting',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Nx Cloud splits large e2e projects into fine-grained test runs, enabling
|
||||||
|
more efficient distribution and dramatically reducing CI times.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
header: <SplitE2eTests />,
|
||||||
|
className: 'md:col-span-1',
|
||||||
|
icon: <ClipboardDocumentIcon className="h-4 w-4 text-neutral-500" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Flaky task detection',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Nx Cloud automatically identifies flaky tasks from any tool and
|
||||||
|
initiates a re-run of those specific tasks, optimizing resource usage
|
||||||
|
for peak efficiency.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
header: <FlakyTasks />,
|
||||||
|
className: 'md:col-span-1',
|
||||||
|
icon: <DocumentIcon className="h-4 w-4 text-neutral-500" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Actionable feedback',
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
Nx Cloud lets you see what went wrong, and how to fix it. It understands
|
||||||
|
your command and lets you find the logs you need.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
header: <ActionableErrorFeedback />,
|
||||||
|
className: 'md:col-span-1',
|
||||||
|
icon: <InboxArrowDownIcon className="h-4 w-4 text-neutral-500" />,
|
||||||
|
},
|
||||||
|
];
|
||||||
17
nx-dev/ui-cloud/tsconfig.json
Normal file
17
nx-dev/ui-cloud/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": false,
|
||||||
|
"esModuleInterop": false,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extends": "../../tsconfig.base.json"
|
||||||
|
}
|
||||||
24
nx-dev/ui-cloud/tsconfig.lib.json
Normal file
24
nx-dev/ui-cloud/tsconfig.lib.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"jest.config.ts",
|
||||||
|
"src/**/*.spec.ts",
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"src/**/*.spec.tsx",
|
||||||
|
"src/**/*.test.tsx",
|
||||||
|
"src/**/*.spec.js",
|
||||||
|
"src/**/*.test.js",
|
||||||
|
"src/**/*.spec.jsx",
|
||||||
|
"src/**/*.test.jsx"
|
||||||
|
],
|
||||||
|
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ export function Footer(): JSX.Element {
|
|||||||
solutions: [
|
solutions: [
|
||||||
{ name: 'Nx Enterprise', href: '/enterprise' },
|
{ name: 'Nx Enterprise', href: '/enterprise' },
|
||||||
{ name: 'Nx', href: 'https://nx.dev' },
|
{ name: 'Nx', href: 'https://nx.dev' },
|
||||||
{ name: 'Nx Cloud', href: 'https://nx.app/?utm_source=nx.dev' },
|
{ name: 'Nx Cloud', href: '/nx-cloud' },
|
||||||
],
|
],
|
||||||
resources: [
|
resources: [
|
||||||
{ name: 'Blog', href: '/blog' },
|
{ name: 'Blog', href: '/blog' },
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export const plans: MenuItem[] = [
|
|||||||
name: 'Nx Cloud',
|
name: 'Nx Cloud',
|
||||||
description:
|
description:
|
||||||
'End-to-end solution for smart, efficient and maintainable CI.',
|
'End-to-end solution for smart, efficient and maintainable CI.',
|
||||||
href: 'https://nx.app',
|
href: '/nx-cloud',
|
||||||
icon: null,
|
icon: null,
|
||||||
isNew: false,
|
isNew: false,
|
||||||
isHighlight: false,
|
isHighlight: false,
|
||||||
|
|||||||
@ -88,6 +88,7 @@
|
|||||||
"@nx/nx-dev/models-package": ["nx-dev/models-package/src/index.ts"],
|
"@nx/nx-dev/models-package": ["nx-dev/models-package/src/index.ts"],
|
||||||
"@nx/nx-dev/ui-animations": ["nx-dev/ui-animations/src/index.ts"],
|
"@nx/nx-dev/ui-animations": ["nx-dev/ui-animations/src/index.ts"],
|
||||||
"@nx/nx-dev/ui-blog": ["nx-dev/ui-blog/src/index.ts"],
|
"@nx/nx-dev/ui-blog": ["nx-dev/ui-blog/src/index.ts"],
|
||||||
|
"@nx/nx-dev/ui-cloud": ["nx-dev/ui-cloud/src/index.ts"],
|
||||||
"@nx/nx-dev/ui-commands": ["nx-dev/ui-commands/src/index.ts"],
|
"@nx/nx-dev/ui-commands": ["nx-dev/ui-commands/src/index.ts"],
|
||||||
"@nx/nx-dev/ui-common": ["nx-dev/ui-common/src/index.ts"],
|
"@nx/nx-dev/ui-common": ["nx-dev/ui-common/src/index.ts"],
|
||||||
"@nx/nx-dev/ui-common/*": ["nx-dev/ui-common/*"],
|
"@nx/nx-dev/ui-common/*": ["nx-dev/ui-common/*"],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user