From 343b96a9bbd8de29517b211e25398f332c853b0d Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:13:46 -0700 Subject: [PATCH] Create rule flow core infrastructure and routing --- .../navigation/ConditionalFooter.tsx | 32 ++++++++++ .../navigation/ConditionalNavigation.tsx | 24 +++++++ .../navigation/TopNav/TopNav.container.tsx | 1 + app/create/[step]/page.tsx | 54 ++++++++++++++++ app/create/context/CreateFlowContext.tsx | 64 +++++++++++++++++++ app/create/layout.tsx | 24 +++++++ app/create/types.ts | 62 ++++++++++++++++++ app/layout.tsx | 16 ++--- 8 files changed, 265 insertions(+), 12 deletions(-) create mode 100644 app/components/navigation/ConditionalFooter.tsx create mode 100644 app/components/navigation/ConditionalNavigation.tsx create mode 100644 app/create/[step]/page.tsx create mode 100644 app/create/context/CreateFlowContext.tsx create mode 100644 app/create/layout.tsx create mode 100644 app/create/types.ts diff --git a/app/components/navigation/ConditionalFooter.tsx b/app/components/navigation/ConditionalFooter.tsx new file mode 100644 index 0000000..48461d5 --- /dev/null +++ b/app/components/navigation/ConditionalFooter.tsx @@ -0,0 +1,32 @@ +"use client"; + +import { memo } from "react"; +import { usePathname } from "next/navigation"; +import dynamic from "next/dynamic"; + +// Code split Footer - below the fold, can be lazy loaded +const Footer = dynamic(() => import("./Footer"), { + loading: () => ( + + ), + ssr: true, // Keep SSR for SEO +}); + +/** + * Conditionally renders Footer based on pathname. + * Hides footer for /create/* routes (full-screen create flow). + */ +const ConditionalFooter = memo(() => { + const pathname = usePathname(); + const isCreateFlow = pathname?.startsWith("/create"); + + if (isCreateFlow) { + return null; + } + + return ; +}); + +ConditionalFooter.displayName = "ConditionalFooter"; + +export default ConditionalFooter; diff --git a/app/components/navigation/ConditionalNavigation.tsx b/app/components/navigation/ConditionalNavigation.tsx new file mode 100644 index 0000000..7222a60 --- /dev/null +++ b/app/components/navigation/ConditionalNavigation.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { memo } from "react"; +import { usePathname } from "next/navigation"; +import TopNavWithPathname from "./TopNav/TopNavWithPathname"; + +/** + * Conditionally renders TopNav based on pathname. + * Hides navigation for /create/* routes (full-screen create flow). + */ +const ConditionalNavigation = memo(() => { + const pathname = usePathname(); + const isCreateFlow = pathname?.startsWith("/create"); + + if (isCreateFlow) { + return null; + } + + return ; +}); + +ConditionalNavigation.displayName = "ConditionalNavigation"; + +export default ConditionalNavigation; diff --git a/app/components/navigation/TopNav/TopNav.container.tsx b/app/components/navigation/TopNav/TopNav.container.tsx index c6a0506..c6e9731 100644 --- a/app/components/navigation/TopNav/TopNav.container.tsx +++ b/app/components/navigation/TopNav/TopNav.container.tsx @@ -205,6 +205,7 @@ const TopNavContainer = memo( size={buttonSize} buttonType={buttonType} palette={palette} + href="/create/informational" ariaLabel={t("ariaLabels.createNewRule")} > {renderAvatarGroup(containerSize, avatarSize)} diff --git a/app/create/[step]/page.tsx b/app/create/[step]/page.tsx new file mode 100644 index 0000000..22b4786 --- /dev/null +++ b/app/create/[step]/page.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { notFound } from "next/navigation"; +import { use } from "react"; +import type { CreateFlowStep } from "../types"; + +interface PageProps { + params: Promise<{ step: string }>; +} + +/** + * Valid step IDs for the create rule flow + */ +const VALID_STEPS: CreateFlowStep[] = [ + "informational", + "text", + "select", + "upload", + "review", + "compact-cards", + "expanded-cards", + "right-rail", + "final-review", + "completed", +]; + +/** + * Dynamic route handler for create flow steps + * + * Handles all flow steps via dynamic routing: /create/[step] + * Validates step exists and renders appropriate template (placeholder for now) + */ +export default function CreateFlowStepPage({ params }: PageProps) { + const { step } = use(params); + + // Validate step exists + if (!VALID_STEPS.includes(step as CreateFlowStep)) { + notFound(); + } + + // Placeholder content - templates will be implemented in CR-51-55 + return ( + + + + Create Flow Step: {step} + + + Template implementation coming in CR-51 through CR-55 + + + + ); +} diff --git a/app/create/context/CreateFlowContext.tsx b/app/create/context/CreateFlowContext.tsx new file mode 100644 index 0000000..3c98858 --- /dev/null +++ b/app/create/context/CreateFlowContext.tsx @@ -0,0 +1,64 @@ +"use client"; + +import { createContext, useContext, useState, type ReactNode } from "react"; +import type { + CreateFlowState, + CreateFlowContextValue, + CreateFlowStep, +} from "../types"; + +const CreateFlowContext = createContext(null); + +interface CreateFlowProviderProps { + children: ReactNode; + initialStep?: CreateFlowStep | null; +} + +/** + * Provider component for Create Flow state management + * + * This is a basic implementation that will be expanded in CR-56 + * with full navigation logic, state persistence, and validation. + */ +export function CreateFlowProvider({ + children, + initialStep = null, +}: CreateFlowProviderProps) { + const [state, setState] = useState({}); + const [currentStep] = useState( + initialStep, + ); + + const updateState = (updates: Partial) => { + setState((prevState) => ({ + ...prevState, + ...updates, + })); + }; + + const contextValue: CreateFlowContextValue = { + state, + currentStep, + updateState, + }; + + return ( + + {children} + + ); +} + +/** + * Hook to access Create Flow context + * + * @throws Error if used outside CreateFlowProvider + * @returns CreateFlowContextValue + */ +export function useCreateFlow(): CreateFlowContextValue { + const context = useContext(CreateFlowContext); + if (!context) { + throw new Error("useCreateFlow must be used within CreateFlowProvider"); + } + return context; +} diff --git a/app/create/layout.tsx b/app/create/layout.tsx new file mode 100644 index 0000000..0ac1448 --- /dev/null +++ b/app/create/layout.tsx @@ -0,0 +1,24 @@ +"use client"; + +import type { ReactNode } from "react"; +import { CreateFlowProvider } from "./context/CreateFlowContext"; + +/** + * 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. + */ +export default function CreateFlowLayout({ + children, +}: { + children: ReactNode; +}) { + return ( + + + {children} + + + ); +} diff --git a/app/create/types.ts b/app/create/types.ts new file mode 100644 index 0000000..3f1e705 --- /dev/null +++ b/app/create/types.ts @@ -0,0 +1,62 @@ +/** + * Type definitions for the Create Rule Flow + * + * These types define the structure for the full-screen create rule flow, + * including step types, state management, and context interfaces. + */ + +/** + * Valid step IDs for the create rule flow + */ +export type CreateFlowStep = + | "informational" + | "text" + | "select" + | "upload" + | "review" + | "compact-cards" + | "expanded-cards" + | "right-rail" + | "final-review" + | "completed"; + +/** + * Flow state interface for storing user inputs across all steps + * Will be expanded in CR-56 with specific field definitions + */ +export interface CreateFlowState { + // Placeholder structure - will be expanded in CR-56 + [key: string]: unknown; +} + +/** + * Context value interface for CreateFlowContext + * Provides state management and navigation capabilities + */ +export interface CreateFlowContextValue { + state: CreateFlowState; + currentStep: CreateFlowStep | null; + updateState: (updates: Partial) => void; + // Navigation handlers will be added in CR-56 +} + +/** + * Base props interface for page templates + * Will be expanded in template implementation tickets (CR-51-55) + */ +export interface PageTemplateProps { + // Base props for all page templates + // Will be expanded in template tickets +} + +/** + * Navigation handlers interface + * Will be implemented in CR-56 + */ +export interface NavigationHandlers { + goToNextStep: () => void; + goToPreviousStep: () => void; + goToStep: (step: CreateFlowStep) => void; + canGoNext: () => boolean; + canGoBack: () => boolean; +} diff --git a/app/layout.tsx b/app/layout.tsx index c7d531c..40a5ff9 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,19 +1,11 @@ import { Inter, Bricolage_Grotesque, Space_Grotesk } from "next/font/google"; import type { Metadata } from "next"; import type { ReactNode } from "react"; -import dynamic from "next/dynamic"; import { MessagesProvider } from "./contexts/MessagesContext"; import messages from "../messages/en/index"; import "./globals.css"; -import TopNavWithPathname from "./components/navigation/TopNav/TopNavWithPathname"; - -// Code split Footer - below the fold, can be lazy loaded -const Footer = dynamic(() => import("./components/navigation/Footer"), { - loading: () => ( - - ), - ssr: true, // Keep SSR for SEO -}); +import ConditionalNavigation from "./components/navigation/ConditionalNavigation"; +import ConditionalFooter from "./components/navigation/ConditionalFooter"; const inter = Inter({ subsets: ["latin"], @@ -107,9 +99,9 @@ export default function RootLayout({ children }: { children: ReactNode }) { > - + {children} - +
+ Template implementation coming in CR-51 through CR-55 +