Create rule flow core infrastructure and routing

This commit is contained in:
adilallo
2026-02-07 21:13:46 -07:00
parent 12b1f59886
commit 343b96a9bb
8 changed files with 265 additions and 12 deletions
@@ -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: () => (
<footer className="bg-[var(--color-surface-default-primary)] w-full min-h-[200px]" />
),
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 <Footer />;
});
ConditionalFooter.displayName = "ConditionalFooter";
export default ConditionalFooter;
@@ -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 <TopNavWithPathname />;
});
ConditionalNavigation.displayName = "ConditionalNavigation";
export default ConditionalNavigation;
@@ -205,6 +205,7 @@ const TopNavContainer = memo<TopNavProps>(
size={buttonSize}
buttonType={buttonType}
palette={palette}
href="/create/informational"
ariaLabel={t("ariaLabels.createNewRule")}
>
{renderAvatarGroup(containerSize, avatarSize)}
+54
View File
@@ -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 (
<div className="flex-1 flex items-center justify-center">
<div className="text-center">
<h1 className="text-white text-2xl font-bold mb-4">
Create Flow Step: {step}
</h1>
<p className="text-gray-400">
Template implementation coming in CR-51 through CR-55
</p>
</div>
</div>
);
}
+64
View File
@@ -0,0 +1,64 @@
"use client";
import { createContext, useContext, useState, type ReactNode } from "react";
import type {
CreateFlowState,
CreateFlowContextValue,
CreateFlowStep,
} from "../types";
const CreateFlowContext = createContext<CreateFlowContextValue | null>(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<CreateFlowState>({});
const [currentStep] = useState<CreateFlowStep | null>(
initialStep,
);
const updateState = (updates: Partial<CreateFlowState>) => {
setState((prevState) => ({
...prevState,
...updates,
}));
};
const contextValue: CreateFlowContextValue = {
state,
currentStep,
updateState,
};
return (
<CreateFlowContext.Provider value={contextValue}>
{children}
</CreateFlowContext.Provider>
);
}
/**
* 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;
}
+24
View File
@@ -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 (
<CreateFlowProvider>
<div className="min-h-screen bg-black flex flex-col">
{children}
</div>
</CreateFlowProvider>
);
}
+62
View File
@@ -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<CreateFlowState>) => 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;
}
+4 -12
View File
@@ -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: () => (
<footer className="bg-[var(--color-surface-default-primary)] w-full min-h-[200px]" />
),
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 }) {
>
<MessagesProvider messages={messages}>
<div className="min-h-screen flex flex-col">
<TopNavWithPathname />
<ConditionalNavigation />
<main className="flex-1">{children}</main>
<Footer />
<ConditionalFooter />
</div>
</MessagesProvider>
</body>