Table of Contents
Component Architecture Overview
Striae's frontend is built using React components organized in a modular structure. This guide covers the major components, their purposes, and how they interact within the application.
Component Directory Structure
app/components/
├── actions/ # Data handling components
├── auth/ # Authentication components
├── button/ # Reusable button components
├── canvas/ # Main canvas for image annotation
├── colors/ # Color picker components
├── footer/ # Footer and modal components
├── icon/ # Icon system
├── mobile/ # Mobile-specific components
├── notice/ # Notification and modal components
├── sidebar/ # Sidebar navigation and controls
├── theme-provider/ # Theme management
├── toast/ # Toast notification system
├── toolbar/ # Main toolbar components
├── turnstile/ # CAPTCHA components
└── user/ # User management components
Core Components
1. Authentication Components
MFAEnrollment (app/components/auth/mfa-enrollment.tsx
)
Purpose: Multi-factor authentication setup
Features:
-
Phone number verification
-
SMS code validation
-
Firebase MFA integration
-
reCAPTCHA verification
Key Props:
-
user: User
- Firebase user object -
onSuccess: () => void
- Success callback -
onError: (message: string) => void
- Error callback -
mandatory: boolean
- Whether MFA is required
MFAVerification (app/components/auth/mfa-verification.tsx
)
Purpose: MFA challenge during login
Features:
-
Multi-factor resolver handling
-
SMS code input and validation
-
Error handling and retry logic
2. Canvas System
Canvas (app/components/canvas/canvas.tsx
)
Purpose: Main image display and annotation interface
Features:
-
High-resolution image rendering
-
Annotation overlay display
-
Loading states and error handling
-
Flash effects for user feedback (subclass characteristics)
Key Props:
interface CanvasProps {
imageUrl?: string;
filename?: string;
company?: string;
firstName?: string;
error?: string;
activeAnnotations?: Set<string>;
annotationData?: AnnotationData | null;
}
State Management:
-
Image loading states
-
Error handling for network issues
-
Flash effects for user feedback (subclass characteristics)
Key Methods:
-
Image load error detection
-
Annotation overlay rendering
-
User interaction handling
3. Sidebar System
Sidebar Container (app/components/sidebar/sidebar-container.tsx
)
Purpose: Main sidebar wrapper with footer integration
Features:
-
Sidebar component orchestration
-
Footer modal management
-
Keyboard event handling (Escape key)
-
Patreon widget integration
Key Props:
interface SidebarContainerProps {
user: User;
onImageSelect: (file: FileData) => void;
imageId?: string;
onCaseChange: (caseNumber: string) => void;
currentCase: string;
files: FileData[];
// ... additional props for state management
}
Sidebar (app/components/sidebar/sidebar.tsx
)
Purpose: Core sidebar functionality
Features:
-
Case management interface
-
File upload and selection
-
Image management controls
Case Sidebar (app/components/sidebar/case-sidebar.tsx
)
Purpose: Case-specific sidebar functionality
Features:
-
Case creation and management
-
File upload interface
-
Image selection and deletion
-
Case validation and error handling
Key Props:
interface CaseSidebarProps {
user: User;
onImageSelect: (file: FileData) => void;
onCaseChange: (caseNumber: string) => void;
imageLoaded: boolean;
setImageLoaded: (loaded: boolean) => void;
onNotesClick: () => void;
files: FileData[];
setFiles: React.Dispatch<React.SetStateAction<FileData[]>>;
caseNumber: string;
setCaseNumber: (caseNumber: string) => void;
currentCase: string | null;
setCurrentCase: (caseNumber: string) => void;
}
Notes Sidebar (app/components/sidebar/notes-sidebar.tsx
)
Purpose: Annotation and notes management interface
Features:
-
Comprehensive annotation forms
-
Color selection integration
-
Classification options (Bullet, Cartridge Case, Other)
-
Support level selection (ID, Exclusion, Inconclusive)
-
Index type management (number/color)
-
Subclass characteristics
-
Additional notes handling
Key Props:
interface NotesSidebarProps {
currentCase: string;
onReturn: () => void;
user: User;
imageId: string;
onAnnotationRefresh?: () => void;
}
Data Types:
type ClassType = 'Bullet' | 'Cartridge Case' | 'Other';
type IndexType = 'number' | 'color';
type SupportLevel = 'ID' | 'Exclusion' | 'Inconclusive';
Cases Modal (app/components/sidebar/cases-modal.tsx
)
Purpose: Case selection and management modal
Features:
-
Paginated case listing
-
Case selection interface
-
Loading states and error handling
-
Keyboard navigation (Escape key)
Props:
interface CasesModalProps {
isOpen: boolean;
onClose: () => void;
onSelectCase: (caseNum: string) => void;
currentCase: string;
user: User;
}
Notes Modal (app/components/sidebar/notes-modal.tsx
)
Purpose: Additional notes editing modal
Features:
-
Text area for detailed notes
-
Save/cancel functionality
-
Keyboard event handling
-
Temporary state management
Props:
interface NotesModalProps {
isOpen: boolean;
onClose: () => void;
notes: string;
onSave: (notes: string) => void;
}
4. Action Components
Case Management (app/components/actions/case-manage.tsx
)
Purpose: Complete case lifecycle management
Key Functions:
export const validateCaseNumber = (caseNumber: string): boolean
export const checkExistingCase = async (
caseNumber: string,
user: User
): Promise<boolean>
export const createNewCase = async (
caseNumber: string,
user: User
): Promise<{ success: boolean; message: string }>
export const renameCase = async (
oldCaseNumber: string,
newCaseNumber: string,
user: User
): Promise<{ success: boolean; message: string }>
export const deleteCase = async (
caseNumber: string,
user: User
): Promise<{ success: boolean; message: string }>
export const listCases = async (user: User): Promise<string[]>
Features:
-
Case number validation
-
Duplicate case detection
-
Case creation and deletion
-
Case renaming functionality
-
User case list management
Image Management (app/components/actions/image-manage.tsx
)
Purpose: Image upload and retrieval operations
Key Functions:
export const uploadImage = async (
file: File,
caseNumber: string,
apiKey: string
): Promise<UploadResult>
export const getImageUrl = async (
imageId: string,
apiKey: string
): Promise<string>
export const deleteImage = async (
imageId: string,
apiKey: string
): Promise<void>
export const fetchFiles = async (
caseNumber: string,
apiKey: string
): Promise<FileData[]>
export const uploadFile = async (
file: File,
caseNumber: string,
apiKey: string
): Promise<{ success: boolean; message: string; fileData?: FileData }>
export const deleteFile = async (
fileId: string,
caseNumber: string,
apiKey: string
): Promise<{ success: boolean; message: string }>
PDF Generation (app/components/actions/generate-pdf.tsx
)
Purpose: PDF report generation
Features:
-
Dynamic PDF creation
-
Annotation integration
-
Custom formatting
-
Error handling and progress feedback
Key Function:
export const generatePDF = async (
imageUrl: string,
caseNumber: string,
annotationData: AnnotationData | null,
activeAnnotations: Set<string>,
firstName?: string
): Promise<{ success: boolean; message: string }>
Notes Management (app/components/actions/notes-manage.tsx
)
Purpose: Annotation and notes data management
Features:
-
CRUD operations for annotation data
-
Data validation and sanitization
-
Error handling for API operations
Key Functions:
export const getNotes = async (
caseNumber: string,
imageId: string,
apiKey: string
): Promise<AnnotationData | null>
export const saveNotes = async (
caseNumber: string,
imageId: string,
notesData: AnnotationData,
apiKey: string
): Promise<{ success: boolean; message: string }>
Sign Out (app/components/actions/signout.tsx
)
Purpose: User authentication logout
Features:
-
Firebase sign out
-
Local storage cleanup
-
Redirect handling
-
Error handling
Props:
interface SignOutProps {
redirectTo?: string;
}
5. UI Components
Button System (app/components/button/
)
Purpose: Reusable button components
Components:
Button
- Standard button with variants
Props:
interface ButtonProps {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
type?: 'button' | 'submit' | 'reset';
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
className?: string;
}
Color System (app/components/colors/colors.tsx
)
Purpose: Color selection interface
Features:
-
Predefined color palette
-
Custom color wheel
-
Color validation
-
Real-time preview
Props:
interface ColorSelectorProps {
selectedColor: string;
onColorSelect: (color: string) => void;
}
Footer Component (app/components/footer/footer.tsx
)
Purpose: Application footer with navigation and social links
Features:
-
External link navigation
-
Patreon integration
-
Dynamic year display
-
Terms and privacy links
Icon System (app/components/icon/icon.tsx
)
Purpose: Centralized icon management
Features:
-
SVG icon system
-
Consistent sizing and styling
-
Type-safe icon names
-
Available icons: eye, eye-off, class, ID, index, notes, number, print, other unused/misc icons
Usage:
<Icon icon="eye" />
<Icon icon="eye-off" />
Mobile Warning (app/components/mobile/mobile-warning.tsx
)
Purpose: Mobile device usage warning
Features:
-
Responsive design detection
-
Route-specific display
-
User experience guidance
-
Desktop-only enforcement
Notice System (app/components/notice/notice.tsx
)
Purpose: Modal notification display
Features:
-
Dynamic content rendering
-
Keyboard event handling (Escape key)
-
Customizable button text
-
Overlay backdrop
Props:
interface NoticeProps {
isOpen: boolean;
onClose: () => void;
notice: {
title: string;
content: React.ReactNode;
buttonText?: string;
};
}
Toast System (app/components/toast/toast.tsx
)
Purpose: User feedback and notifications
Features:
-
Success and error message display
-
Auto-dismiss functionality
-
Customizable styling
Props:
interface ToastProps {
message: string;
type: 'success' | 'error' | 'warning';
isVisible: boolean;
onClose: () => void;
}
Toolbar (app/components/toolbar/toolbar.tsx
)
Purpose: Main application toolbar
Features:
-
Tool selection management
-
PDF generation controls
-
Visibility toggle
-
Active tool state tracking
Props:
interface ToolbarProps {
onToolSelect?: (toolId: ToolId, active: boolean) => void;
onGeneratePDF?: () => void;
canGeneratePDF?: boolean;
onVisibilityChange?: (visible: boolean) => void;
isGeneratingPDF?: boolean;
}
type ToolId = 'number' | 'class' | 'index' | 'id' | 'notes' | 'print' | 'visibility';
Turnstile CAPTCHA (app/components/turnstile/turnstile.tsx
)
Purpose: Cloudflare Turnstile CAPTCHA integration
Features:
-
Security verification
-
Theme customization
-
Widget lifecycle management
-
Callback handling
Props:
interface TurnstileProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
onWidgetId?: (id: string) => void;
success?: boolean;
theme?: 'light' | 'dark' | 'auto';
}
Theme Provider (app/components/theme-provider/theme-provider.tsx
)
Purpose: Application theme management
Features:
-
Theme context provision
-
Theme persistence
-
System theme detection
-
Theme switching functionality
Theme Types (app/components/theme-provider/theme.ts
):
type Theme = 'light' | 'dark' | 'system';
6. User Management Components
User Profile Management (app/components/user/manage-profile.tsx
)
Purpose: Comprehensive user profile management
Features:
-
Profile information editing (display name)
-
Email address viewing (read-only)
-
Company information viewing (read-only)
-
Password change functionality
-
User reauthentication
-
Firebase integration
-
Error handling with detailed messages
Props:
interface ManageProfileProps {
isOpen: boolean;
onClose: () => void;
}
Key Features:
-
Display name modification
-
Company information management (read-only)
-
Email address display (read-only)
-
Firebase error handling integration
InactivityWarning (app/components/user/inactivity-warning.tsx
)
Purpose: Session timeout management
Features:
-
Inactivity detection
-
Warning countdown display
-
Session extension handling
Component State Management
Local State Patterns
Most components use React's built-in state management:
// Typical state structure
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | undefined>();
const [data, setData] = useState<DataType | null>(null);
Context Usage
AuthContext (app/contexts/auth.context.tsx
)
Purpose: Global authentication state
Provided Values:
-
Current user information
-
Authentication status
-
Login/logout functions
Custom Hooks
useEmailSyncToKV (app/hooks/useEmailSyncToKV.ts
)
Purpose: Synchronize user email with KV storage
Features:
-
Automatic email sync on auth state change
-
Error handling for sync failures
-
Retry logic for failed operations
useInactivityTimeout (app/hooks/useInactivityTimeout.ts
)
Purpose: Session inactivity management
Features:
-
Configurable timeout periods
-
Activity detection
-
Automatic logout on timeout
Component Communication Patterns
Props Down, Events Up
Components follow React's unidirectional data flow:
// Parent component
const [selectedImage, setSelectedImage] = useState<string>();
// Child component receives data and callbacks
<ImageSelector
images={images}
onImageSelect={setSelectedImage}
/>
Event Handling
Components use callback props for communication:
interface ComponentProps {
onSuccess: () => void;
onError: (message: string) => void;
onDataChange: (data: DataType) => void;
}
Modal and Dialog Patterns
Many components follow consistent modal patterns:
// Common modal interface
interface ModalProps {
isOpen: boolean;
onClose: () => void;
}
// Keyboard event handling for modals
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}
}, [isOpen, onClose]);
Styling Approach
CSS Modules
Components use CSS Modules for scoped styling:
// Component file
import styles from './component.module.css';
// Usage
<div className={styles.container}>
<button className={styles.primaryButton}>
Click me
</button>
</div>
Style Conventions
-
BEM-like naming:
styles.componentName__elementName--modifier
-
CSS Custom Properties: For theming and consistency
-
Intuitive Design: Clean, simple, user-friendly interfaces
-
Accessibility: ARIA labels and semantic HTML
Performance Considerations
Component Lifecycle
Components are designed for efficient mounting and unmounting:
useEffect(() => {
// Setup
const cleanup = setupComponent();
// Cleanup
return cleanup;
}, [dependencies]);
Accessibility Features
Built-in Accessibility
-
Semantic HTML: Proper element usage
-
ARIA Labels: Screen reader support
-
Keyboard Navigation: Full keyboard accessibility
-
Focus Management: Proper focus handling
-
Color Contrast: WCAG compliance
Development Guidelines
Component Creation Checklist
-
✅ Create component directory
-
✅ Implement TypeScript interfaces
-
✅ Add CSS Module styling
-
✅ Include error handling (follow Error Handling Guide)
-
✅ Add loading states
-
✅ Implement accessibility features
-
✅ Add documentation
Best Practices
-
Single Responsibility: Each component has one clear purpose
-
Type Safety: Full TypeScript coverage
-
Error Boundaries: Graceful error handling (see Error Handling Guide)
-
Performance: Optimized for large datasets
-
Maintainability: Clear, documented code