From e3c103966a9ef99382f56a9c9209b0d0c34063ef Mon Sep 17 00:00:00 2001 From: Nicholas Cunningham <ndcunningham@gmail.com> Date: Tue, 7 Jan 2025 14:19:33 -0700 Subject: [PATCH] feat(nx-dev): Enhance customer testimonial carousel and icon grid layout --- .../src/lib/customer-icon-grid.tsx | 2 +- .../src/lib/customer-testimonial-carousel.tsx | 57 +++++++++++++++---- .../src/lib/enterprise-customers.tsx | 2 +- .../src/lib/download-case-study.tsx | 41 +++++-------- 4 files changed, 64 insertions(+), 38 deletions(-) diff --git a/nx-dev/ui-customers/src/lib/customer-icon-grid.tsx b/nx-dev/ui-customers/src/lib/customer-icon-grid.tsx index bda32105364384..8bcece655ba5ab 100644 --- a/nx-dev/ui-customers/src/lib/customer-icon-grid.tsx +++ b/nx-dev/ui-customers/src/lib/customer-icon-grid.tsx @@ -38,7 +38,7 @@ function getBorderClass(index: number, totalIcons: number, columns = 4) { const CustomerIconGrid: FC<CustomerIconGridProps> = ({ icons }) => { return ( - <div className="grid grid-cols-2 justify-between px-4 md:grid-cols-4"> + <div className="grid grid-cols-2 justify-between md:grid-cols-4"> {icons.map((customerIcon, index) => { const borderClass = getBorderClass(index, icons.length); diff --git a/nx-dev/ui-customers/src/lib/customer-testimonial-carousel.tsx b/nx-dev/ui-customers/src/lib/customer-testimonial-carousel.tsx index 3d8656b5b8aecc..a31a4d3900261f 100644 --- a/nx-dev/ui-customers/src/lib/customer-testimonial-carousel.tsx +++ b/nx-dev/ui-customers/src/lib/customer-testimonial-carousel.tsx @@ -1,7 +1,11 @@ 'use client'; import { Fragment, useState, useEffect } from 'react'; import { Dialog, Transition } from '@headlessui/react'; -import { PlayIcon } from '@heroicons/react/24/outline'; +import { + ChevronLeftIcon, + ChevronRightIcon, + PlayIcon, +} from '@heroicons/react/24/outline'; import Image from 'next/image'; import { CasewareIcon, @@ -56,12 +60,21 @@ const testimonials: Testimonial[] = [ { title: 'Customer story', subtitle: - 'Scaling 700+ projects: How Nx Enterprise became a no-brainer for Caseware', + 'Scaling 700+ projects: How Nx Enterprise became a no-brainer for Caseware.', metrics: [ - { value: '700+', label: 'Monorepo projects scaled effortlessly' }, { - value: 'Efficiency', - label: 'Unified workflows: frontend to backend', + value: 'Massive scale', + label: + '600–700 projects, unifying frontends and backends company wide.', + }, + { + value: 'Instant impact', + label: 'Trialing Nx Enterprise cut build times immediately.', + }, + { + value: 'Actionable insights', + label: + 'Nx Cloud’s metrics uncovered inefficiencies across 10+ year old codebase.', }, ], company: 'Caseware', @@ -167,7 +180,6 @@ export function CustomerTestimonialCarousel(): JSX.Element { useEffect(() => { let timer: NodeJS.Timeout; - // Clear the current timer and start a new one if (!isOpen) { timer = setInterval(() => { setCurrentIndex((prevIndex) => { @@ -177,7 +189,6 @@ export function CustomerTestimonialCarousel(): JSX.Element { }, slideLogoTimeOut); } - // Cleanup on unmount or when dependencies change return () => { clearInterval(timer); }; @@ -230,7 +241,7 @@ export function CustomerTestimonialCarousel(): JSX.Element { <div className="flex h-full flex-col justify-center space-y-8"> {currentTestimonial.metrics?.map((metric, index) => ( <div key={index} className="space-y-2"> - <div className="text-4xl font-bold text-blue-500 lg:text-5xl"> + <div className="text-xl font-bold text-sky-600 lg:text-2xl"> {metric.value} </div> <div className="text-base text-slate-500 lg:text-lg dark:text-slate-400"> @@ -245,6 +256,19 @@ export function CustomerTestimonialCarousel(): JSX.Element { {/* Right side - Video Card */} <div className="col-span-2 md:col-span-3"> <div className="flex items-center gap-4"> + {/* Prev Button Mobile only */} + <button + disabled={currentIndex === 0} + title={`See ${testimonials[currentIndex - 1]?.company} again!`} + className="flex h-12 w-12 items-center justify-center rounded-full p-2 transition hover:text-slate-950 disabled:pointer-events-none disabled:opacity-0 md:hidden dark:hover:text-white" + onClick={() => { + setCurrentIndex( + (currentIndex - 1 + testimonials.length) % testimonials.length + ); + }} + > + <ChevronLeftIcon className="h-8 w-8" /> + </button> <div className="group relative h-[450px] w-full cursor-pointer self-stretch overflow-hidden rounded-lg xl:shadow-2xl" onClick={() => setIsOpen(true)} @@ -272,6 +296,19 @@ export function CustomerTestimonialCarousel(): JSX.Element { </button> </div> </div> + {/* Next Button - Mobile only */} + <button + className="flex h-12 w-12 items-center justify-center rounded-full p-2 transition hover:text-slate-950 disabled:pointer-events-none disabled:opacity-0 md:hidden dark:hover:text-white" + disabled={currentIndex === testimonials.length - 1} + title={`Next ${testimonials[currentIndex + 1]?.company}!`} + onClick={() => { + setCurrentIndex( + (currentIndex + 1 + testimonials.length) % testimonials.length + ); + }} + > + <ChevronRightIcon className="h-8 w-8" /> + </button> </div> {/* Mobile Navigation display dots */} @@ -290,7 +327,7 @@ export function CustomerTestimonialCarousel(): JSX.Element { </div> </div> - {/* Carosel Navigation */} + {/* Carosel Navigation - Larger screens */} <div className="relative mx-auto hidden max-w-7xl grid-cols-6 items-center justify-center px-4 pt-16 md:grid"> {testimonials.map(({ company, logo }, i) => ( <button @@ -308,7 +345,7 @@ export function CustomerTestimonialCarousel(): JSX.Element { className={`${logo.height} ${logo.width} transition-transform duration-300`} /> - {/* Progress Bar at the Top */} + {/* Progress Bar */} {i === currentIndex && !isOpen && ( <div className="absolute left-0 top-0 h-[2px] w-full overflow-hidden bg-gray-300/80 transition-all"> <div diff --git a/nx-dev/ui-customers/src/lib/enterprise-customers.tsx b/nx-dev/ui-customers/src/lib/enterprise-customers.tsx index 9c657c947ec610..15953a67490aed 100644 --- a/nx-dev/ui-customers/src/lib/enterprise-customers.tsx +++ b/nx-dev/ui-customers/src/lib/enterprise-customers.tsx @@ -274,7 +274,7 @@ export function EnterpriseCustomers(): JSX.Element { <div className="mx-auto max-w-7xl"> <CustomerIconGrid icons={secondCustomerIcons} /> - <div className="grid-cols grid justify-center gap-4 px-4 py-6 md:grid-cols-3"> + <div className="grid-cols grid justify-center gap-4 px-2 py-6 md:grid-cols-3"> <DownloadCaseStudy title="Financial Institution Case Study" description="$28B Fortune 500 financial institution reduces CI times by 79% with Nx Cloud." diff --git a/nx-dev/ui-enterprise/src/lib/download-case-study.tsx b/nx-dev/ui-enterprise/src/lib/download-case-study.tsx index 4ac80d2cfa0af1..9cfdec13625781 100644 --- a/nx-dev/ui-enterprise/src/lib/download-case-study.tsx +++ b/nx-dev/ui-enterprise/src/lib/download-case-study.tsx @@ -1,9 +1,5 @@ import { ButtonLink } from '@nx/nx-dev/ui-common'; import { ReactElement } from 'react'; -import { - ArrowDownTrayIcon, - ChevronRightIcon, -} from '@heroicons/react/24/outline'; export interface DownloadCaseStudyProps { title: string; @@ -23,31 +19,24 @@ export function DownloadCaseStudy({ variant = 'primary', }: DownloadCaseStudyProps): ReactElement { return ( - <div className="border border-slate-100 bg-white shadow-lg sm:rounded-lg dark:border-slate-800/60 dark:bg-slate-950"> - <div className="px-4 py-5 sm:p-6"> + <div className="flex h-full flex-col border border-slate-100 bg-white shadow-lg sm:rounded-lg dark:border-slate-800/60 dark:bg-slate-950"> + <div className="flex flex-1 flex-col px-4 py-5 sm:p-6"> <h3 className="text-base font-semibold leading-6 text-slate-900 dark:text-slate-100"> {title} </h3> - <div className="mt-2 sm:flex sm:items-start sm:justify-between"> - <div className="max-w-xl text-sm"> - <p>{description}</p> - </div> - <div className="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center"> - <ButtonLink - href={buttonHref} - title={`${buttonCTA} ${title}`} - variant={variant} - target="_blank" - size="small" - > - {buttonText}{' '} - {buttonCTA === 'Read more' ? ( - <ChevronRightIcon className="h-4 w-4" /> - ) : ( - <ArrowDownTrayIcon className="h-4 w-4 translate-x-1" /> - )} - </ButtonLink> - </div> + <div className="mt-2 max-w-xl flex-1 text-sm"> + <p>{description}</p> + </div> + <div className="mt-auto pt-5"> + <ButtonLink + href={buttonHref} + title={`${buttonCTA} ${title}`} + variant={variant} + target={buttonCTA === 'Download' ? '_blank' : undefined} + size="small" + > + {buttonText} + </ButtonLink> </div> </div> </div>