nx/nx-dev/ui-home/src/lib/nx-with-ci.tsx
2024-03-13 10:34:55 +01:00

265 lines
12 KiB
TypeScript

import { SparklesIcon } from '@heroicons/react/24/outline';
import { SectionHeading } from '@nx/nx-dev/ui-common';
import {
animate,
motion,
MotionValue,
useAnimation,
useMotionValue,
useTransform,
} from 'framer-motion';
import Link from 'next/link';
import { useEffect, useRef } from 'react';
import { useInView } from 'react-intersection-observer';
function Counter({
from = 0,
to = 10,
round = 0,
progress,
}: {
from: number;
to: number;
round: number;
progress: MotionValue<number>;
}): JSX.Element {
const ref = useRef<any>();
const value = useTransform(progress, [0, 1000], [from, to], {
clamp: false,
});
const { format: formatNumber } = new Intl.NumberFormat('en-US', {
minimumFractionDigits: round,
maximumFractionDigits: round,
});
useEffect(() => {
return value.onChange((v) => {
if (ref !== undefined && ref.current !== undefined)
ref.current.firstChild.data = formatNumber(
round === 0 ? Math.round(v) : Number(v.toFixed(round))
);
});
}, [formatNumber, round, value]);
return <span ref={ref}>{formatNumber(value.get())}</span>;
}
export function NxWithCi(): JSX.Element {
const progress: MotionValue<number> = useMotionValue(0);
const controls = useAnimation();
const [ref, inView] = useInView({ triggerOnce: true });
useEffect(() => {
if (!inView) return;
controls.start('visible');
animate(progress, 1000, {
type: 'spring',
damping: 100,
});
}, [controls, inView, progress]);
return (
<article id="nx-is-fast" className="relative pt-12 sm:pt-28">
<motion.div
ref={ref}
animate={controls}
className="mx-auto max-w-7xl px-4 pb-12 sm:grid sm:grid-cols-2 sm:gap-8 sm:px-6 lg:px-8 lg:pb-16"
>
<div>
<header>
<SectionHeading as="h1" variant="title" id="nx-is-fast">
From 90 to 10 minutes
</SectionHeading>
<SectionHeading
as="p"
variant="display"
id="nx-ci"
className="mt-4"
>
Effortless, Fast CI
</SectionHeading>
</header>
<div className="mt-8 flex gap-16 font-normal">
<p className="max-w-xl text-lg text-slate-700 dark:text-slate-400">
Nx comes with the building blocks to not only scale your monorepo
locally and provide great DX while developing, but also to address
one of the major pain points:{' '}
<span className="font-bold">fast, maintainable CI.</span>
</p>
</div>
</div>
<div className="flex items-end">
<div className="w-full">
<div className="flex">
<div className="shrink-0 w-28 border-r border-slate-200 dark:border-slate-500 py-3 text-slate-700 dark:text-slate-400">
CI without Nx
</div>
<div className="flex-grow py-1">
<motion.div
initial={{ display: 'none', width: 0 }}
variants={{ visible: { display: 'flex', width: '90%' } }}
transition={{ type: 'tween', duration: 1 }}
className="flex-grow items-center justify-end text-slate-600 dark:text-slate-400 bg-slate-200 dark:bg-slate-700 py-2 px-4 rounded-r-lg"
>
<Counter from={0} to={90} round={0} progress={progress} />
<span className="ml-1">minutes</span>
</motion.div>
</div>
</div>
<div className="flex">
<div className="shrink-0 font-medium w-28 border-r border-slate-200 dark:border-slate-500 py-3 text-slate-700 dark:text-slate-400">
CI with Nx
</div>
<div className="flex-grow flex gap-4 py-1 font-medium">
<motion.div
initial={{ display: 'none', width: 0 }}
variants={{ visible: { display: 'flex', width: '10%' } }}
transition={{ type: 'tween', duration: 1 }}
className="bg-blue-500 dark:bg-sky-500 rounded-r-lg"
/>
<div className="flex items-center text-slate-700 dark:text-slate-400">
<Counter from={0} to={10} round={0} progress={progress} />
<span className="ml-1">minutes</span>
</div>
</div>
</div>
<div className="flex w-full justify-end">
<a
href="https://www.youtube.com/watch?v=KPCMg_Dn0Eo"
target="_blank"
className="hover:underline text-sm font-medium"
title="Find out how to reduce CI time with Nx"
rel="noreferrer"
>
Find out how
</a>
</div>
</div>
</div>
</motion.div>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-8 lg:pb-16">
<dl className="grid grid-cols-1 gap-x-8 gap-y-16 sm:grid-cols-3">
<div className="relative">
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
<div className="relative group">
<div className="absolute -inset-1 bg-gradient-to-r from-cyan-500 to-blue-500 rounded-lg blur-sm opacity-25 group-hover:opacity-90 transition duration-1000 group-hover:duration-200"></div>
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
<NxCacheIcon className="h-8 w-8" aria-hidden="true" />
<Link
href="/ci/features/remote-cache"
title="Discover Nx Replay"
>
<span className="absolute inset-0"></span>Nx Replay
</Link>
</div>
</div>
</dt>
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
Built-in local and remote caching to speed up your tasks and save
you time and money.
</dd>
</div>
<div className="relative">
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
<div className="relative group">
<div className="absolute -inset-1 bg-gradient-to-r from-emerald-500 to-green-500 rounded-lg blur-sm opacity-25 group-hover:opacity-90 transition duration-1000 group-hover:duration-200"></div>
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
<NxAgentsIcon className="h-8 w-8" aria-hidden="true" />
<a href="/ci/features/nx-agents" title="Discover Nx Agents">
<span className="absolute inset-0"></span>Nx Agents
</a>
</div>
</div>
</dt>
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
A single line to enable distributed computation, across multiple
machines. Fully managed agents, dynamically allocated based on PR
size.
</dd>
</div>
<div className="relative">
<dt className="text-base font-semibold leading-7 text-slate-900 dark:text-slate-100">
<div className="relative group opacity-70">
<div className="absolute -inset-1 bg-slate-500 rounded-lg blur-sm opacity-25"></div>
<div className="relative flex gap-4 items-center rounded-lg border border-slate-200 bg-white p-4 text-lg shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 dark:border-slate-800/40 dark:bg-slate-800">
<SparklesIcon
className="block h-8 w-8 sm:hidden md:block"
aria-hidden="true"
/>
{/*<a href="/ci/features/tusky" title="Discover Tusky">*/}
<span className="absolute inset-0"></span>Tusky
{/*</a>*/}
<div className="flex-grow" />
<span className="dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20 inline-flex 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">
Coming soon
</span>
</div>
</div>
</dt>
<dd className="mt-4 text-base leading-7 text-slate-700 dark:text-slate-400">
A powerful Artificial Intelligence equipped with context of your
workspace, commit history, and historical build timing data.
</dd>
</div>
</dl>
</div>
</article>
);
}
export const NxAgentsIcon = (props: any) => (
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
fill="none"
{...props}
>
<path
strokeLinejoin="round"
d="M21 12.5h-4m4 0a1 1 0 1 0 2 0 1 1 0 0 0-2 0Zm-2 8h-5v-3m5 3a1 1 0 1 0 2 0 1 1 0 0 0-2 0Zm-16-8h4m-4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm2 8h5v-3m-5 3a1 1 0 1 1-2 0 1 1 0 0 1 2 0Zm0-17h5v4m-5-4a1 1 0 1 0-2 0 1 1 0 0 0 2 0Zm14 0h-5v4m5-4a1 1 0 1 1 2 0 1 1 0 0 1-2 0Zm-3 14H8a1 1 0 0 1-1-1v-8a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1Zm-5-8h2l-.667 1.875L14 11.346 11.333 15.5l.334-2.77H10l1-3.23Z"
/>
</svg>
);
export const NxCacheIcon = (props: any) => (
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
fill="none"
{...props}
>
<path
strokeLinecap="round"
d="m8.625 2.621-.014.004c-.317.09-.622.22-.907.387m4.796-.965-.014-.002a3.958 3.958 0 0 0-.986.002m3.875.574.014.004c.317.09.622.22.907.387m-.92 18.367.013-.004c.317-.09.622-.22.907-.387m-4.796.965.014.002c.328.04.659.04.986-.002m-3.875-.574-.014-.004a3.96 3.96 0 0 1-.907-.387M21.38 8.625l-.004-.014a3.96 3.96 0 0 0-.387-.907m.965 4.796.002-.014c.04-.328.04-.659-.002-.986m-.574 3.875-.004.014a3.96 3.96 0 0 1-.387.907m-18.367-.92.004.013c.09.317.22.622.387.907M2.047 11.5l-.002.014c-.04.328-.04.659.002.986m.574-3.875.004-.014c.09-.317.22-.622.387-.907M7.5 10.516h9m-9 0a1.5 1.5 0 0 1 0-3h9a1.5 1.5 0 0 1 0 3m-9 0a1.5 1.5 0 0 0 0 3m9-3a1.5 1.5 0 0 1 0 3m-6.412-4.5h.01m-1.883 0h.01m-.725 4.5h9m-9 0a1.5 1.5 0 0 0 0 3h9a1.5 1.5 0 0 0 0-3m-6.412-1.5h.01m-1.883 0h.01m1.863 3h.01m-1.883 0h.01M3 4.516a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm15 0a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm-15 15a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Zm15 0a1.5 1.5 0 1 0 3 0 1.5 1.5 0 0 0-3 0Z"
/>
</svg>
);
export const NxWorkflowsIcon = (props: any) => (
<svg
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
fill="none"
{...props}
>
<g clipPath="url(#a)">
<path
strokeLinecap="round"
d="M13.315 10h7.37m-7.37-6h7.37m-5.95-1v2m-1.42 2h7.37m-1.159-1v2M17 9v2M4.678 3.5h.75m2.29 0h.75m-3.79 7h.75m2.29 0h.75m1.605-5.395v.75m0 2.29v.75m-7-3.79v.75m0 2.29v.75M3.698 21h6a1 1 0 0 0 1-1v-.763a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1V20a1 1 0 0 0 1 1Zm0-4.605h6a1 1 0 0 0 1-1v-.763a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v.763a1 1 0 0 0 1 1Zm14.723.976a1.42 1.42 0 1 1-2.842 0 1.42 1.42 0 0 1 2.842 0ZM2.499.499H21.5a2 2 0 0 1 2 2V21.5a2 2 0 0 1-2 2h-19a2 2 0 0 1-2-2v-19a2 2 0 0 1 2-2ZM20.11 16.087l.599.126a.6.6 0 0 1 .476.587v1.133a.6.6 0 0 1-.477.588l-.594.123a.6.6 0 0 0-.448.773l.19.583a.6.6 0 0 1-.27.706l-.983.566a.6.6 0 0 1-.747-.12l-.408-.456a.6.6 0 0 0-.896.001l-.403.454a.6.6 0 0 1-.748.12l-.98-.565a.6.6 0 0 1-.27-.704l.19-.586a.6.6 0 0 0-.449-.773l-.594-.124a.6.6 0 0 1-.478-.588l.001-1.13a.6.6 0 0 1 .476-.587l.6-.127a.6.6 0 0 0 .446-.775l-.19-.576a.6.6 0 0 1 .27-.707l.978-.567a.6.6 0 0 1 .747.119l.408.454a.6.6 0 0 0 .894 0l.405-.454a.6.6 0 0 1 .747-.12l.982.567a.6.6 0 0 1 .27.707l-.19.578a.6.6 0 0 0 .446.774Z"
/>
</g>
<defs>
<clipPath id="a">
<path fill="transparent" d="M0 0h24v24H0z" />
</clipPath>
</defs>
</svg>
);