Implement create flow topnav and footer

This commit is contained in:
adilallo
2026-02-07 22:42:30 -07:00
parent 343b96a9bb
commit e6c1002dbb
10 changed files with 319 additions and 4 deletions
+13 -3
View File
@@ -15,7 +15,8 @@ interface LogoProps {
| "headerLg"
| "headerXl"
| "footer"
| "footerLg";
| "footerLg"
| "createFlow";
showText?: boolean;
}
@@ -114,6 +115,13 @@ const Logo = memo<LogoProps>(({ size = "default", showText = true }) => {
lineHeight: "leading-[calc(27.05px*2.05)]",
iconSize: "w-[calc(27.05px*2.05)] h-[calc(27.05px*2.05)]",
},
createFlow: {
containerHeight: "h-[30px] md:h-[41px]",
gap: "gap-[6px] md:gap-[8.28px]",
textSize: "text-[16.48px] md:text-[21.97px]",
lineHeight: "leading-[20.28px] md:leading-[27.05px]",
iconSize: "w-[20.28px] h-[20.28px] md:w-[27.05px] md:h-[27.05px]",
},
};
const config =
@@ -137,8 +145,10 @@ const Logo = memo<LogoProps>(({ size = "default", showText = true }) => {
? sizes.headerXl
: size === "footer"
? sizes.footer
: size === "footerLg"
? sizes.footerLg
: size === "footerLg"
? sizes.footerLg
: size === "createFlow"
? sizes.createFlow
: sizes.default;
return (
@@ -0,0 +1,21 @@
"use client";
import { memo } from "react";
import { CreateFlowFooterView } from "./CreateFlowFooter.view";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
const CreateFlowFooterContainer = memo<CreateFlowFooterProps>(
({ secondButton, progressBar = true, className = "" }) => {
return (
<CreateFlowFooterView
secondButton={secondButton}
progressBar={progressBar}
className={className}
/>
);
},
);
CreateFlowFooterContainer.displayName = "CreateFlowFooter";
export default CreateFlowFooterContainer;
@@ -0,0 +1,20 @@
/**
* Type definitions for CreateFlowFooter component
*
* Footer component for the create rule flow with progress bar and buttons.
*/
export interface CreateFlowFooterProps {
/**
* The second button (typically "Next" button) to display on the right side
*/
secondButton?: React.ReactNode;
/**
* Whether to show the progress bar
* @default true
*/
progressBar?: boolean;
/**
* Additional CSS classes
*/
className?: string;
}
@@ -0,0 +1,42 @@
import ProportionBar from "../../progress/ProportionBar";
import Button from "../../buttons/Button";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
export function CreateFlowFooterView({
secondButton,
progressBar = true,
className = "",
}: CreateFlowFooterProps) {
return (
<footer
className={`sticky bottom-0 z-50 bg-black w-full ${className}`}
role="contentinfo"
aria-label="Create Flow Footer"
>
{/* Progress Bar - Top */}
{progressBar && (
<div className="px-[var(--spacing-measures-spacing-500,20px)] md:px-[var(--spacing-measures-spacing-1200,48px)] pt-[var(--spacing-measures-spacing-300,12px)]">
<ProportionBar progress="1-0" />
</div>
)}
{/* Buttons Container */}
<div className="flex items-center justify-between mx-auto max-w-[639px] md:max-w-[1920px] px-[var(--spacing-measures-spacing-500,20px)] md:px-[var(--spacing-measures-spacing-1200,48px)] py-[var(--spacing-measures-spacing-300,12px)] gap-[var(--spacing-measures-spacing-300,12px)]">
{/* Back Button - Left */}
<Button
buttonType="ghost"
palette="default"
size="xsmall"
className="md:!text-[14px] md:!leading-[16px] !text-[12px] !leading-[14px] !px-[var(--spacing-measures-spacing-200,8px)] md:!px-[var(--spacing-measures-spacing-250,10px)] !py-[var(--spacing-measures-spacing-200,8px)] md:!py-[var(--spacing-measures-spacing-250,10px)]"
>
Back
</Button>
{/* Second Button - Right */}
{secondButton && (
<div className="flex-shrink-0">{secondButton}</div>
)}
</div>
</footer>
);
}
@@ -0,0 +1,2 @@
export { default } from "./CreateFlowFooter.container";
export type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
@@ -0,0 +1,49 @@
"use client";
import { memo } from "react";
import { useRouter } from "next/navigation";
import { CreateFlowTopNavView } from "./CreateFlowTopNav.view";
import type { CreateFlowTopNavProps } from "./CreateFlowTopNav.types";
const CreateFlowTopNavContainer = memo<CreateFlowTopNavProps>(
({
hasShare = false,
hasExport = false,
hasEdit = false,
loggedIn = false,
onShare,
onExport,
onEdit,
onExit,
className = "",
}) => {
const router = useRouter();
const handleExit = () => {
if (onExit) {
onExit();
} else {
// Default behavior: navigate to home
router.push("/");
}
};
return (
<CreateFlowTopNavView
hasShare={hasShare}
hasExport={hasExport}
hasEdit={hasEdit}
loggedIn={loggedIn}
onShare={onShare}
onExport={onExport}
onEdit={onEdit}
onExit={handleExit}
className={className}
/>
);
},
);
CreateFlowTopNavContainer.displayName = "CreateFlowTopNav";
export default CreateFlowTopNavContainer;
@@ -0,0 +1,49 @@
/**
* Type definitions for CreateFlowTopNav component
*
* Top navigation bar for the create rule flow.
* Includes logo and action buttons (Share, Export, Edit, Exit).
*/
export interface CreateFlowTopNavProps {
/**
* Whether to show the Share button
* @default false
*/
hasShare?: boolean;
/**
* Whether to show the Export button
* @default false
*/
hasExport?: boolean;
/**
* Whether to show the Edit button
* @default false
*/
hasEdit?: boolean;
/**
* Whether the user is logged in
* @default false
*/
loggedIn?: boolean;
/**
* Callback when Share button is clicked
*/
onShare?: () => void;
/**
* Callback when Export button is clicked
*/
onExport?: () => void;
/**
* Callback when Edit button is clicked
*/
onEdit?: () => void;
/**
* Callback when Exit/Save & Exit button is clicked
*/
onExit?: () => void;
/**
* Additional CSS classes
*/
className?: string;
}
@@ -0,0 +1,101 @@
import Logo from "../../icons/Logo";
import Button from "../../buttons/Button";
import type { CreateFlowTopNavProps } from "./CreateFlowTopNav.types";
export function CreateFlowTopNavView({
hasShare = false,
hasExport = false,
hasEdit = false,
loggedIn = false,
onShare,
onExport,
onEdit,
onExit,
className = "",
}: CreateFlowTopNavProps) {
const exitButtonText = loggedIn ? "Save & Exit" : "Exit";
return (
<header
className={`bg-black w-full border-b border-[var(--color-border-default-tertiary)] ${className}`}
role="banner"
aria-label="Create Rule Flow Navigation"
>
<nav
className="flex items-center justify-between mx-auto max-w-[639px] md:max-w-[1920px] px-[var(--spacing-measures-spacing-500,20px)] md:px-[48px] py-[var(--spacing-measures-spacing-300,12px)] md:py-[var(--spacing-measures-spacing-016,16px)]"
role="navigation"
aria-label="Create Flow Navigation"
>
{/* Logo - Left */}
<Logo size="createFlow" showText={true} />
{/* Button Group - Right */}
<div className="flex items-center gap-[var(--spacing-scale-012,12px)]">
{hasShare && (
<Button
buttonType="outline"
palette="default"
size="xsmall"
onClick={onShare}
ariaLabel="Share"
className="md:!text-[12px] md:!leading-[14px] !text-[10px] !leading-[12px] !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"
>
Share
</Button>
)}
{hasExport && (
<Button
buttonType="outline"
palette="default"
size="xsmall"
onClick={onExport}
ariaLabel="Export"
className="justify-center gap-[var(--spacing-scale-002,2px)] !pl-[var(--spacing-scale-012,12px)] !pr-[var(--spacing-scale-006,6px)] md:!pr-[var(--spacing-scale-006,6px)] !text-[10px] md:!text-[12px] !leading-[12px] md:!leading-[14px] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"
>
<span>Export</span>
<svg
width="12"
height="12"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="shrink-0 md:w-[14px] md:h-[14px]"
aria-hidden="true"
>
<path d="M19 9l-7 7-7-7" />
</svg>
</Button>
)}
{hasEdit && (
<Button
buttonType="outline"
palette="default"
size="xsmall"
onClick={onEdit}
ariaLabel="Edit"
className="md:!text-[12px] md:!leading-[14px] !text-[10px] !leading-[12px] !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"
>
Edit
</Button>
)}
<Button
buttonType="outline"
palette="default"
size="xsmall"
onClick={onExit}
ariaLabel={exitButtonText}
className="md:!text-[12px] md:!leading-[14px] !text-[10px] !leading-[12px] !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"
>
{exitButtonText}
</Button>
</div>
</nav>
</header>
);
}
@@ -0,0 +1,2 @@
export { default } from "./CreateFlowTopNav.container";
export type { CreateFlowTopNavProps } from "./CreateFlowTopNav.types";
+20 -1
View File
@@ -2,12 +2,16 @@
import type { ReactNode } from "react";
import { CreateFlowProvider } from "./context/CreateFlowContext";
import CreateFlowTopNav from "../components/utility/CreateFlowTopNav";
import CreateFlowFooter from "../components/utility/CreateFlowFooter";
import Button from "../components/buttons/Button";
/**
* Layout for the Create Rule Flow
*
* Provides a full-screen layout without the root layout's TopNav/Footer.
* This layout wraps all create flow pages and provides the CreateFlowContext.
* Includes the create flow-specific TopNav and Footer components.
*/
export default function CreateFlowLayout({
children,
@@ -17,7 +21,22 @@ export default function CreateFlowLayout({
return (
<CreateFlowProvider>
<div className="min-h-screen bg-black flex flex-col">
{children}
<CreateFlowTopNav />
<main className="flex-1 overflow-auto">
{children}
</main>
<CreateFlowFooter
secondButton={
<Button
buttonType="filled"
palette="default"
size="xsmall"
className="md:!text-[14px] md:!leading-[16px] !text-[12px] !leading-[14px] !px-[var(--spacing-measures-spacing-200,8px)] md:!px-[var(--spacing-measures-spacing-250,10px)] !py-[var(--spacing-measures-spacing-200,8px)] md:!py-[var(--spacing-measures-spacing-250,10px)]"
>
Next
</Button>
}
/>
</div>
</CreateFlowProvider>
);