App reorganization

This commit is contained in:
adilallo
2026-04-18 14:12:49 -06:00
parent f866d11ff8
commit e9dab04b34
288 changed files with 2698 additions and 5029 deletions
@@ -1,30 +1,16 @@
import { memo } from "react";
import { normalizeSize } from "../../../lib/propNormalization";
export type AvatarContainerSizeValue =
| "small"
| "medium"
| "large"
| "xlarge"
| "Small"
| "Medium"
| "Large"
| "XLarge";
export type AvatarContainerSizeValue = "small" | "medium" | "large" | "xlarge";
interface AvatarContainerProps extends React.HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
/**
* Avatar container size. Accepts both lowercase and PascalCase (case-insensitive).
* Figma uses PascalCase, codebase uses lowercase - both are supported.
*/
size?: AvatarContainerSizeValue;
className?: string;
}
const AvatarContainer = memo<AvatarContainerProps>(
({ children, size: sizeProp = "small", className = "", ...props }) => {
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
const size = normalizeSize(sizeProp, "small");
const size = sizeProp;
const sizeStyles: Record<string, string> = {
small: "flex -space-x-[var(--spacing-scale-008)]",
medium: "flex -space-x-[9px]",
@@ -0,0 +1,2 @@
export { default } from "./AvatarContainer";
export type { AvatarContainerSizeValue } from "./AvatarContainer";
@@ -7,6 +7,10 @@ import type { CardStackProps } from "./CardStack.types";
const DEFAULT_TOGGLE_LABEL = "See all communication approaches";
const DEFAULT_SHOW_LESS_LABEL = "Show less";
/**
* Figma: "Utility / CardStack" (TODO(figma)). Selectable stack of cards with
* an optional "see all"/"show less" expand toggle.
*/
const CardStackContainer = memo<CardStackProps>(
({
cards,
@@ -4,6 +4,10 @@ import { memo } from "react";
import { CreateFlowFooterView } from "./CreateFlowFooter.view";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
/**
* Figma: "Utility / CreateFlowFooter" (TODO(figma)). Sticky footer for the
* create flow with a back action, optional secondary button, and progress bar.
*/
const CreateFlowFooterContainer = memo<CreateFlowFooterProps>(
({
secondButton,
@@ -1,4 +1,3 @@
import { normalizeProportionBarVariant } from "../../../../lib/propNormalization";
import ProportionBar from "../../progress/ProportionBar";
import Button from "../../buttons/Button";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
@@ -11,9 +10,7 @@ export function CreateFlowFooterView({
onBackClick,
className = "",
}: CreateFlowFooterProps) {
const proportionBarVariant = normalizeProportionBarVariant(
proportionBarVariantProp,
);
const proportionBarVariant = proportionBarVariantProp ?? "default";
return (
<footer
className={`bg-black w-full ${className}`}
@@ -5,6 +5,10 @@ import { useRouter } from "next/navigation";
import { CreateFlowTopNavView } from "./CreateFlowTopNav.view";
import type { CreateFlowTopNavProps } from "./CreateFlowTopNav.types";
/**
* Figma: "Utility / CreateFlowTopNav" (TODO(figma)). Top navigation bar for
* the create flow with exit, share, export, and edit actions.
*/
const CreateFlowTopNavContainer = memo<CreateFlowTopNavProps>(
({
hasShare = false,
@@ -3,11 +3,11 @@
import { memo } from "react";
import DecisionMakingSidebarView from "./DecisionMakingSidebar.view";
import type { DecisionMakingSidebarProps } from "./DecisionMakingSidebar.types";
import {
normalizeHeaderLockupJustification,
normalizeHeaderLockupSize,
} from "../../../../lib/propNormalization";
/**
* Figma: "Utility / DecisionMakingSidebar" (TODO(figma)). Sidebar pairing a
* header lockup with an `InfoMessageBox` checklist for decision-making screens.
*/
const DecisionMakingSidebarContainer = memo<DecisionMakingSidebarProps>(
({
title,
@@ -20,8 +20,8 @@ const DecisionMakingSidebarContainer = memo<DecisionMakingSidebarProps>(
justification: justificationProp = "left",
className = "",
}) => {
const size = normalizeHeaderLockupSize(sizeProp);
const justification = normalizeHeaderLockupJustification(justificationProp);
const size = sizeProp;
const justification = justificationProp;
return (
<DecisionMakingSidebarView
-58
View File
@@ -1,58 +0,0 @@
"use client";
import React, { Component, type ReactNode } from "react";
import { logger } from "../../../lib/logger";
interface ErrorBoundaryProps {
children: ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
error: Error | null;
}
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
// Update state so the next render will show the fallback UI
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Log the error to an error reporting service
logger.error("ErrorBoundary caught an error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
// Fallback UI using design tokens
return (
<div className="min-h-[200px] flex items-center justify-center p-[var(--spacing-scale-016)]">
<div className="text-center">
<h2 className="text-xl font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-008)]">
Something went wrong
</h2>
<p className="text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-016)]">
We&apos;re sorry, but something unexpected happened.
</p>
<button
onClick={() => this.setState({ hasError: false, error: null })}
className="px-[var(--spacing-scale-016)] py-[var(--spacing-scale-008)] bg-[var(--color-surface-default-brand-royal)] text-[var(--color-content-inverse-primary)] rounded-[var(--radius-measures-radius-small)] hover:bg-[var(--color-surface-hover-brand-royal)] transition-colors"
>
Try again
</button>
</div>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
@@ -1,70 +0,0 @@
"use client";
import { memo } from "react";
import { normalizeImagePlaceholderColor } from "../../../lib/propNormalization";
export type ImagePlaceholderColorValue =
| "blue"
| "green"
| "purple"
| "red"
| "orange"
| "teal"
| "Blue"
| "Green"
| "Purple"
| "Red"
| "Orange"
| "Teal";
interface ImagePlaceholderProps {
width?: number;
height?: number;
text?: string;
/**
* Image placeholder color. Accepts both lowercase and PascalCase (case-insensitive).
* Figma uses PascalCase, codebase uses lowercase - both are supported.
*/
color?: ImagePlaceholderColorValue;
className?: string;
}
/**
* Simple image placeholder component for testing
* Generates colored backgrounds with text overlays
*/
const ImagePlaceholder = memo<ImagePlaceholderProps>(
({
width = 260,
height = 390,
text = "Blog Image",
color: colorProp = "blue",
className = "",
}) => {
// Normalize props to handle both PascalCase (Figma) and lowercase (codebase)
const color = normalizeImagePlaceholderColor(colorProp);
const colors: Record<string, string> = {
blue: "bg-blue-500",
green: "bg-green-500",
purple: "bg-purple-500",
red: "bg-red-500",
orange: "bg-orange-500",
teal: "bg-teal-500",
};
const bgColor = colors[color] || colors.blue;
return (
<div
className={`${bgColor} flex items-center justify-center text-white font-bold text-lg ${className}`}
style={{ width: `${width}px`, height: `${height}px` }}
>
{text}
</div>
);
},
);
ImagePlaceholder.displayName = "ImagePlaceholder";
export default ImagePlaceholder;
@@ -4,6 +4,10 @@ import { memo, useCallback, useState } from "react";
import InfoMessageBoxView from "./InfoMessageBox.view";
import type { InfoMessageBoxProps } from "./InfoMessageBox.types";
/**
* Figma: "Utility / InfoMessageBox" (TODO(figma)). Bordered message box that
* lists checkbox items under a title with an optional leading icon.
*/
const InfoMessageBoxContainer = memo<InfoMessageBoxProps>(
({
title,
@@ -3,23 +3,23 @@
import { memo } from "react";
import InputLabelView from "./InputLabel.view";
import type { InputLabelProps } from "./InputLabel.types";
import {
normalizeInputLabelSize,
normalizeInputLabelPalette,
} from "../../../../lib/propNormalization";
/**
* Figma: "Utility / InputLabel" (TODO(figma)). Reusable form-input label with
* optional asterisk, help icon, and helper text.
*/
const InputLabelContainer = memo<InputLabelProps>(
({
label,
helpIcon = false,
asterisk = false,
helperText = false,
size: sizeProp = "S",
palette: paletteProp = "Default",
size: sizeProp = "s",
palette: paletteProp = "default",
className = "",
}) => {
const size = normalizeInputLabelSize(sizeProp);
const palette = normalizeInputLabelPalette(paletteProp);
const size = sizeProp;
const palette = paletteProp;
return (
<InputLabelView
@@ -1,9 +1,5 @@
export type InputLabelSizeValue = "S" | "M" | "s" | "m";
export type InputLabelPaletteValue =
| "Default"
| "Inverse"
| "default"
| "inverse";
export type InputLabelSizeValue = "s" | "m";
export type InputLabelPaletteValue = "default" | "inverse";
export interface InputLabelProps {
/**
@@ -25,13 +21,11 @@ export interface InputLabelProps {
*/
helperText?: boolean | string;
/**
* Size variant: "S" (small) or "M" (medium)
* Accepts both uppercase (Figma) and lowercase values.
* Size variant: "s" (small) or "m" (medium)
*/
size?: InputLabelSizeValue;
/**
* Palette variant: "Default" or "Inverse"
* Accepts both PascalCase (Figma) and lowercase values.
* Palette variant: "default" or "inverse"
*/
palette?: InputLabelPaletteValue;
className?: string;
@@ -0,0 +1,18 @@
"use client";
import { memo } from "react";
import { ModalFooterView } from "./ModalFooter.view";
import type { ModalFooterProps } from "./ModalFooter.types";
/**
* Figma: "Utility / ModalFooter" (TODO(figma)).
* Sticky modal footer slot used by the create-flow + login modals to host
* primary/secondary actions.
*/
const ModalFooterContainer = memo<ModalFooterProps>((props) => {
return <ModalFooterView {...props} />;
});
ModalFooterContainer.displayName = "ModalFooter";
export default ModalFooterContainer;
+1 -1
View File
@@ -1,2 +1,2 @@
export { ModalFooterView as default } from "./ModalFooter.view";
export { default } from "./ModalFooter.container";
export type { ModalFooterProps } from "./ModalFooter.types";
@@ -0,0 +1,18 @@
"use client";
import { memo } from "react";
import { ModalHeaderView } from "./ModalHeader.view";
import type { ModalHeaderProps } from "./ModalHeader.types";
/**
* Figma: "Utility / ModalHeader" (TODO(figma)).
* Sticky 48px modal header with optional close (left) and more-options
* (right) icon buttons.
*/
const ModalHeaderContainer = memo<ModalHeaderProps>((props) => {
return <ModalHeaderView {...props} />;
});
ModalHeaderContainer.displayName = "ModalHeader";
export default ModalHeaderContainer;
+1 -1
View File
@@ -1,2 +1,2 @@
export { ModalHeaderView as default } from "./ModalHeader.view";
export { default } from "./ModalHeader.container";
export type { ModalHeaderProps } from "./ModalHeader.types";
@@ -0,0 +1,18 @@
"use client";
import { memo } from "react";
import { ScrollbarView } from "./Scrollbar.view";
import type { ScrollbarProps } from "./Scrollbar.types";
/**
* Figma: "Utility / Scrollbar" (TODO(figma)).
* Custom-styled scrollable wrapper. Most surfaces should attach
* `SCROLLBAR_DESIGN_CLASS` directly instead of nesting through this view.
*/
const ScrollbarContainer = memo<ScrollbarProps>((props) => {
return <ScrollbarView {...props} />;
});
ScrollbarContainer.displayName = "Scrollbar";
export default ScrollbarContainer;
+1 -1
View File
@@ -1,4 +1,4 @@
export { ScrollbarView as default } from "./Scrollbar.view";
export { default } from "./Scrollbar.container";
export type { ScrollbarProps } from "./Scrollbar.types";
/** Class name to apply the design system scrollbar to any scrollable element (e.g. textarea, div). */
@@ -0,0 +1 @@
export { default } from "./Separator";
@@ -9,6 +9,10 @@ const DEFAULT_LABELS: Record<TagProps["variant"], string> = {
selected: "SELECTED",
};
/**
* Figma: "Utility / Tag" (TODO(figma)). Small status pill (e.g. "RECOMMENDED"
* or "SELECTED") used to annotate cards and options.
*/
const TagContainer = memo<TagProps>(({ variant, children, className = "" }) => {
const content = children ?? DEFAULT_LABELS[variant];
return (