Create rule flow core infrastructure and routing
This commit is contained in:
@@ -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}
|
size={buttonSize}
|
||||||
buttonType={buttonType}
|
buttonType={buttonType}
|
||||||
palette={palette}
|
palette={palette}
|
||||||
|
href="/create/informational"
|
||||||
ariaLabel={t("ariaLabels.createNewRule")}
|
ariaLabel={t("ariaLabels.createNewRule")}
|
||||||
>
|
>
|
||||||
{renderAvatarGroup(containerSize, avatarSize)}
|
{renderAvatarGroup(containerSize, avatarSize)}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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
@@ -1,19 +1,11 @@
|
|||||||
import { Inter, Bricolage_Grotesque, Space_Grotesk } from "next/font/google";
|
import { Inter, Bricolage_Grotesque, Space_Grotesk } from "next/font/google";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { MessagesProvider } from "./contexts/MessagesContext";
|
import { MessagesProvider } from "./contexts/MessagesContext";
|
||||||
import messages from "../messages/en/index";
|
import messages from "../messages/en/index";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import TopNavWithPathname from "./components/navigation/TopNav/TopNavWithPathname";
|
import ConditionalNavigation from "./components/navigation/ConditionalNavigation";
|
||||||
|
import ConditionalFooter from "./components/navigation/ConditionalFooter";
|
||||||
// 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
|
|
||||||
});
|
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
@@ -107,9 +99,9 @@ export default function RootLayout({ children }: { children: ReactNode }) {
|
|||||||
>
|
>
|
||||||
<MessagesProvider messages={messages}>
|
<MessagesProvider messages={messages}>
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
<TopNavWithPathname />
|
<ConditionalNavigation />
|
||||||
<main className="flex-1">{children}</main>
|
<main className="flex-1">{children}</main>
|
||||||
<Footer />
|
<ConditionalFooter />
|
||||||
</div>
|
</div>
|
||||||
</MessagesProvider>
|
</MessagesProvider>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user