Skip to content

Commit

Permalink
Adds new user onboarding dialog (#124)
Browse files Browse the repository at this point in the history
* basic onboarding dialog complete

* dialog buttons update depending on step

* syntax

* add and animate cursor

* cursor size adjustment

* image update

* fix cursor shadow bug

* update nux images

* add query param in sign up callback url to automatically show NUX

* fix issues with mobile responsive changes

* update text size
  • Loading branch information
eliang-stripe committed May 13, 2024
1 parent 72e708a commit 3ba11b5
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 12 deletions.
2 changes: 1 addition & 1 deletion app/(auth)/onboarding/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function Onboarding() {
<EmbeddedComponentContainer>
<ConnectAccountOnboarding
onExit={() => {
window.location.href = '/home';
window.location.href = '/home?shownux=true';
}}
/>
</EmbeddedComponentContainer>
Expand Down
2 changes: 2 additions & 0 deletions app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import AuthenticatedAndOnboardedRoute from '@/app/components/AuthenticatedAndOnboardedRoute';
import Nav from '@/app/components/Nav';
import {EmbeddedComponentWrapper} from '@/app/hooks/EmbeddedComponentWrapper';
import OnboardingDialog from '../components/OnboardingDialog';
import DataRequest from '../components/DataRequest';

export default function DashboardLayout({
Expand All @@ -16,6 +17,7 @@ export default function DashboardLayout({
<Nav />
<div className="flex flex-1 justify-center p-3 mt-[74px] sm:mt-0 sm:p-8 sm:ml-52 lg:ml-64">
<div className="max-w-[1200px] w-full flex-1 space-y-4 md:space-y-5">
<OnboardingDialog />
{children}
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/(dashboard)/pets/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ export default function Pets() {
key={key}
>
<Image
className="relative h-[200px] w-full rounded-lg border object-cover"
className="relative h-32 md:h-52 w-full rounded-lg border object-cover"
fill
quality={100}
src={`/pet_photos/${pet.profilePhoto}.jpg`}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
alt={`Photo of ${pet.name}`}
/>
<div className="flex w-full items-center gap-4">
<div className="flex flex-col md:flex-row w-full md:items-center gap-4">
<div className="flex-1">
<h3 className="text-lg font-medium">{pet.name}</h3>
<p className="text-sm text-subdued">
Expand Down
2 changes: 1 addition & 1 deletion app/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const Nav = () => {
const [showMobileNavItems, setShowMobileNavItems] = React.useState(false)

return (
<div className="fixed sm:flex w-full sm:w-52 lg:w-64 z-40 sm:fixed sm:h-screen flex-col border-b sm:border-r bg-white sm:p-1 lg:p-3">
<div className="fixed sm:flex w-full sm:w-52 lg:w-64 sm:fixed sm:h-screen flex-col border-b sm:border-r bg-white sm:p-1 lg:p-3 z-40">
<div className="flex justify-between items-center sm:mb-4 p-3">
<Link href="/home">
<div className="flex items-center gap-3 text-xl font-bold text-primary">
Expand Down
162 changes: 162 additions & 0 deletions app/components/OnboardingDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"use client";

import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import Image from "next/image";
import ImageStep1 from '@/public/onboarding-images/step-1.png';
import ImageStep2 from '@/public/onboarding-images/step-2.png';
import ImageStep3 from '@/public/onboarding-images/step-3.png';
import PointingHand from '@/public/onboarding-images/pointinghand.png';
import { ArrowRight } from "lucide-react";
import { useSearchParams } from 'next/navigation'
import * as React from 'react';

const OnboardingDialog = () => {
// Look for showNux query param in the URL.
const searchParams = useSearchParams();
let showNux = searchParams.get('shownux') === 'true';

// Check browser width, and don't show NUX if on mobile device.
window.innerWidth < 640 ? showNux = false : "";

const [openNux, setOpenNux] = React.useState(showNux);
const [currentStep, setCurrentStep] = React.useState(0);

const onboardingSteps = [
{
"title": "Welcome to Furever!",
"description": "Take a quick tour to learn more.",
"imageClassName": "scale-[0.85]",
"cursorClassName": "opacity-0",
"imageURL": ImageStep1,
}, {
"title": "Eplore components on different pages.",
"description": "Components can be found in Payments, Payouts, Finances, and My Account.",
"imageClassName": "scale-[1.6] translate-x-[280px] translate-y-[25px]",
"cursorClassName": "opacity-100 translate-x-[60px] translate-y-[-160px]",
"imageURL": ImageStep2,
}, {
"title": "View component outlines",
"description": "Turn on borders to visualize embedded components in dashboard.",
"imageClassName": "scale-[1.8] translate-x-[450px] translate-y-[-450px]",
"cursorClassName": "opacity-100 translate-x-[208px] translate-y-[-75px]",
"imageURL": ImageStep3,
}
]

const DecrementButon = () => {
// If user is at first step, show a close button
if (currentStep == 0) {
return (
<DialogClose asChild>
<Button type="button" variant="secondary" autoFocus={false}>
Close
</Button>
</DialogClose>
)
}

// If not, show a back button
return (
<Button
variant="secondary"
onClick={() => setCurrentStep(currentStep - 1)}
>
Back
</Button>
)
}

const IncrementButton = () => {
if (currentStep == onboardingSteps.length - 1) {
return (
<DialogClose asChild>
<Button
className="gap-1 items-center bg-gradient-to-r from-[#7F81FA] to-[#5AA5F2] hover:opacity-90"
autoFocus={true}
>
Finish
<ArrowRight size={20} />
</Button>
</DialogClose>
)
}

return (
<Button
onClick={() => setCurrentStep(currentStep + 1)}
className="gap-1 items-center bg-gradient-to-r from-[#7F81FA] to-[#5AA5F2] hover:opacity-90"
>
Continue
<ArrowRight size={20} />
</Button>
)
}

const ProgressIndicator = () => {
const progressDot = (isActive: boolean) => {
const bg = isActive ? "bg-[#675DFF]" : "bg-neutral-200"
return <div className={`w-2.5 h-2.5 rounded-full ${bg}`}></div>
}

return (
<div className="flex gap-3">
{ progressDot(currentStep >= 0) }
{ progressDot(currentStep >= 1) }
{ progressDot(currentStep >= 2) }
</div>
)
}

return (
<>
<Dialog open={openNux} onOpenChange={setOpenNux}>
<DialogContent className="max-w-[none] w-[700px] p-0 gap-0 overflow-hidden border-0">
<div className="w-full h-[300px] overflow-hidden border-b relative bg-gradient-to-tr from-[#CCCCFD] to-[#B0E9F7]">
<Image
src={onboardingSteps[currentStep].imageURL}
alt="Image of the Furever website"
placeholder="blur"
quality={100}
sizes="100vw"
priority
className={`overflow-hidden rounded-xl object-cover shadow-lg transition duration-700 ease-in-out
${onboardingSteps[currentStep].imageClassName}`
}
/>
<Image
src={PointingHand}
alt="Pointer cursor"
priority
className={`w-14 h-14 absolute transition duration-700 right-3/4 bottom-[-20px] ${onboardingSteps[currentStep].cursorClassName}`}
/>
</div>
<DialogHeader className="p-5">
<DialogTitle className="text-2xl">{onboardingSteps[currentStep].title}</DialogTitle>
<DialogDescription className="text-lg">
{onboardingSteps[currentStep].description}
</DialogDescription>
<div className="flex flex-1 pt-4 gap-2 items-center justify-between">
<ProgressIndicator />
<div className="flex gap-2">
<DecrementButon />
<IncrementButton />
</div>
</div>

</DialogHeader>
</DialogContent>
</Dialog>
</>
);
};

export default OnboardingDialog;
4 changes: 2 additions & 2 deletions app/components/QuickstartButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ const QuickstartButton = () => {
email: `${salonName}_${emailNumber}@stripe.com`,
password: `${passwordWords[0]}-${passwordWords[1]}-${passwordNumber}`,
businessName: salonName,
callbackUrl: '/home',
callbackUrl: '/home?shownux=true',
});

router.push('/home'); // Redirect to the dashboard
router.push('/home?shownux=true'); // Redirect to the dashboard
}

const onClick = async () => {
Expand Down
4 changes: 2 additions & 2 deletions app/components/Schedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const renderDayProgressBar = () => {

return (
<div
className="absolute h-[2px] left-[40px] w-[calc(100%-35px)] bg-accent"
className="absolute h-[2px] left-[40px] w-[calc(100%-35px)] bg-accent z-30"
style={{
top: `${(SCHEDULE_HEIGHT * minutesSince9AM) / MINUTES_IN_BUSINESS_DAY + 60}px`,
}}
Expand Down Expand Up @@ -82,7 +82,7 @@ const Schedule = () => {
<h1 className="text-xl font-bold">Today&apos;s schedule</h1>
<div className="font-bold text-accent">{getCurrentDate()}</div>
</div>
<div className="relative z-40 left-0 flex w-full flex-row">
<div className="relative z-30 left-0 flex w-full flex-row">
{renderDayProgressBar()}
</div>
<div className="ml-10 flex flex-row">
Expand Down
2 changes: 1 addition & 1 deletion components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
'placeholder:text-muted-foreground flex w-full rounded-md border border-gray-300 bg-background px-[8px] py-[4px] text-sm outline-accent file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline disabled:cursor-not-allowed disabled:opacity-50',
'placeholder:text-muted-foreground flex w-full rounded-md border border-gray-300 bg-background px-[8px] py-[4px] text-base outline-accent file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline disabled:cursor-not-allowed disabled:opacity-50',
className
)}
ref={ref}
Expand Down
6 changes: 3 additions & 3 deletions components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
'placeholder:text-muted-foreground flex w-full items-center justify-between rounded-md border border-gray-300 bg-background px-2 py-1 text-sm font-medium ring-offset-background focus:outline-accent disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
'placeholder:text-muted-foreground flex w-full items-center justify-between rounded-md border border-gray-300 bg-background px-2 py-1 text-base font-medium ring-offset-background focus:outline-accent disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className
)}
{...props}
Expand Down Expand Up @@ -105,7 +105,7 @@ const SelectLabel = React.forwardRef<
>(({className, ...props}, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
className={cn('py-1.5 pl-8 pr-2 text-base font-semibold', className)}
{...props}
/>
));
Expand All @@ -118,7 +118,7 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
'relative flex w-full cursor-default select-none items-center rounded-sm py-1 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
'relative flex w-full cursor-default select-none items-center rounded-sm py-1 pl-8 pr-2 text-base outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className
)}
{...props}
Expand Down
Binary file added public/onboarding-images/pointinghand.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/onboarding-images/step-1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/onboarding-images/step-2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/onboarding-images/step-3.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3ba11b5

Please sign in to comment.