Files
openhuman/docs/src/06-components.md
T
Steven EnamakelandGitHub 58969667d9 fix: add ESLint and Prettier configuration (#15)
* ran prettier

* Refactor ESLint configuration to use ES module syntax and enhance TypeScript support

- Converted CommonJS `require` statements to ES module `import` syntax for better compatibility with modern JavaScript.
- Added new paths to ignore in ESLint configuration to exclude additional directories.
- Updated TypeScript file patterns to be more specific, improving linting accuracy.
- Adjusted React hooks rules to allow certain patterns, enhancing flexibility in component design.

These changes improve the maintainability and clarity of the ESLint configuration, aligning it with current best practices.

* Refactor ESLint configuration to use ES module syntax and enhance TypeScript support

- Converted CommonJS `require` statements to ES module `import` syntax for better compatibility with modern JavaScript.
- Added new paths to ignore in ESLint configuration to exclude additional directories.
- Updated TypeScript file patterns to be more specific, improving linting accuracy.
- Introduced new React hooks rules and adjusted existing rules for better adherence to best practices.
- Made minor adjustments to import statements across various files for consistency and clarity.

These changes improve the overall linting setup and ensure better code quality across the project.

* Refactor import statements across multiple files for consistency

- Updated import statements to use TypeScript's `type` syntax for type imports, enhancing clarity and consistency across the codebase.
- Consolidated imports from the same module into single statements, improving readability and maintainability.

These changes streamline the code structure and align with best practices for TypeScript imports.

* Refactor import statements in memory manager for improved clarity

- Updated import statements to consolidate type imports and enhance readability.
- Removed redundant imports, streamlining the code structure in the memory manager file.

These changes align with best practices for TypeScript imports and improve maintainability.

* Add Husky for pre-commit and pre-push hooks

- Introduced Husky to manage Git hooks, enhancing the development workflow.
- Added pre-commit and pre-push scripts to enforce code formatting and linting checks before commits and pushes.
- Updated package.json to include Husky as a dependency and added a prepare script for setup.

These changes improve code quality and ensure adherence to formatting and linting standards during the development process.

* Refactor import statements for improved clarity and consistency

- Updated import statements across multiple files to consolidate type imports and enhance readability.
- Adjusted the order of imports for better organization and alignment with best practices in TypeScript.

These changes streamline the code structure and improve maintainability throughout the project.

* ran formatter

* Refactor import statements and improve code formatting across multiple files

- Consolidated and reordered import statements for better clarity and consistency in `SkillsGrid.tsx`, `SkillProvider.tsx`, and `index.ts`.
- Enhanced readability by adjusting formatting and removing redundant lines.
- These changes align with best practices for TypeScript imports and improve overall maintainability of the codebase.

* Refactor and optimize code in multiple components

- Removed redundant properties from the `STATUS_DISPLAY` object in `SkillsGrid.tsx` to streamline status handling.
- Consolidated import statements in `SettingsModal.tsx` for improved organization.
- Simplified state management and error handling in `BillingPanel.tsx`, enhancing performance and readability.
- Added `REHYDRATE` import to `index.ts` for better state persistence management.

These changes improve code clarity, maintainability, and align with best practices in TypeScript development.

* Consolidate import statements in SettingsModal.tsx for improved organization

* Add Prettier and ESLint checks to typecheck workflow

- Integrated a Prettier formatting check to ensure code style consistency.
- Added an ESLint step to enforce code quality and catch potential issues.
- These enhancements improve the development workflow by automating formatting and linting checks during the typecheck process.

* Add activeSkillDescription state to ConnectionsPanel and ConnectStep

- Introduced activeSkillDescription state in both ConnectionsPanel and ConnectStep components to store and manage skill descriptions.
- Updated the SkillSetupModal to accept skillDescription as a prop, enhancing the modal's functionality and data handling.

These changes improve the user experience by providing more detailed information about skills during the connection setup process.

* Enhance pre-push hook to include TypeScript compile check

- Added a TypeScript compile check to the pre-push script, ensuring that code compiles successfully before pushing.
- Updated error handling to include compile errors alongside formatting and linting issues, providing clearer feedback to developers.

These changes improve the reliability of the codebase by preventing non-compiling code from being pushed.

* Update GitHub workflows for pull request handling and publishing logic

- Modified the package-and-publish workflow to support pull request events, ensuring proper handling of branches.
- Adjusted the SHOULD_PUBLISH environment variable to differentiate between pull requests and main branch events.
- Updated the pr-protection workflow to focus solely on the main branch, removing references to the master branch.

These changes enhance the CI/CD process by refining branch handling and improving clarity in workflow conditions.
2026-02-02 06:24:50 +05:30

10 KiB

Components

Reusable React components organized by feature.

Component Structure

components/
├── Route Guards
│   ├── ProtectedRoute.tsx
│   ├── PublicRoute.tsx
│   └── DefaultRedirect.tsx
│
├── Authentication
│   └── TelegramLoginButton.tsx
│
├── Connection Status
│   ├── ConnectionIndicator.tsx
│   ├── TelegramConnectionIndicator.tsx
│   ├── TelegramConnectionModal.tsx
│   └── GmailConnectionIndicator.tsx
│
├── Onboarding
│   ├── ProgressIndicator.tsx
│   └── LottieAnimation.tsx
│
├── Settings Modal (16 files)
│   ├── SettingsModal.tsx
│   ├── SettingsLayout.tsx
│   ├── SettingsHome.tsx
│   ├── panels/
│   ├── components/
│   └── hooks/
│
└── Development
    └── DesignSystemShowcase.tsx

Route Guard Components

ProtectedRoute

Requires authentication and optionally onboarding.

interface ProtectedRouteProps {
  requireOnboarded?: boolean;
}

// Usage in AppRoutes.tsx
<Route element={<ProtectedRoute />}>
  <Route path="/onboarding/*" element={<Onboarding />} />
</Route>

<Route element={<ProtectedRoute requireOnboarded />}>
  <Route path="/home" element={<Home />} />
</Route>

PublicRoute

Redirects authenticated users away.

// Usage in AppRoutes.tsx
<Route element={<PublicRoute />}>
  <Route path="/" element={<Welcome />} />
  <Route path="/login" element={<Login />} />
</Route>

DefaultRedirect

Fallback that routes based on auth state.

// Redirects to:
// - "/" if not authenticated
// - "/onboarding" if authenticated but not onboarded
// - "/home" if authenticated and onboarded

Authentication Components

TelegramLoginButton

OAuth login button for Telegram.

interface TelegramLoginButtonProps {
  onClick: () => void;
  disabled?: boolean;
}

// Usage
<TelegramLoginButton
  onClick={() => openUrl(`${BACKEND_URL}/auth/telegram?platform=desktop`)}
/>

Connection Status Components

ConnectionIndicator

Generic connection status badge.

interface ConnectionIndicatorProps {
  status: 'connected' | 'connecting' | 'disconnected' | 'error';
  label?: string;
}

<ConnectionIndicator status="connected" label="Socket" />

TelegramConnectionIndicator

Telegram-specific status display.

interface TelegramConnectionIndicatorProps {
  status: 'connected' | 'connecting' | 'disconnected' | 'error';
}

// Usage with Redux state
const telegramStatus = useAppSelector((state) =>
  selectTelegramConnectionStatus(state, userId)
);

<TelegramConnectionIndicator status={telegramStatus} />

TelegramConnectionModal

Modal for setting up Telegram connection.

interface TelegramConnectionModalProps {
  isOpen: boolean;
  onClose: () => void;
}

// Usage in onboarding/settings
const [showModal, setShowModal] = useState(false);

<TelegramConnectionModal
  isOpen={showModal}
  onClose={() => setShowModal(false)}
/>

Features:

  • QR code login flow
  • Phone number login flow
  • Connection status display
  • Error handling

GmailConnectionIndicator

Gmail status badge (future integration).

<GmailConnectionIndicator status="coming-soon" />

Onboarding Components

ProgressIndicator

Visual progress through onboarding steps.

interface ProgressIndicatorProps {
  current: number;
  total: number;
}

<ProgressIndicator current={2} total={5} />

LottieAnimation

Lottie animation player for onboarding.

interface LottieAnimationProps {
  animationData: object;
  loop?: boolean;
  autoplay?: boolean;
  className?: string;
}

import welcomeAnimation from '../assets/animations/welcome.json';

<LottieAnimation
  animationData={welcomeAnimation}
  loop={true}
  autoplay={true}
/>

Settings Modal System

Complete modal system with URL-based routing.

File Structure

components/settings/
├── SettingsModal.tsx          # Route-based container
├── SettingsLayout.tsx         # Portal + backdrop wrapper
├── SettingsHome.tsx           # Main menu with profile
├── panels/
│   ├── ConnectionsPanel.tsx   # Connection management
│   ├── MessagingPanel.tsx     # (Future)
│   ├── PrivacyPanel.tsx       # (Future)
│   ├── ProfilePanel.tsx       # (Future)
│   ├── AdvancedPanel.tsx      # (Future)
│   └── BillingPanel.tsx       # (Future)
├── components/
│   ├── SettingsHeader.tsx     # User profile section
│   ├── SettingsMenuItem.tsx   # Menu item component
│   ├── SettingsBackButton.tsx # Back navigation
│   └── SettingsPanelLayout.tsx# Panel wrapper
└── hooks/
    ├── useSettingsNavigation.ts # URL routing
    └── useSettingsAnimation.ts  # Animation state

SettingsModal

Main container that renders based on URL.

export function SettingsModal() {
  const location = useLocation();
  const isOpen = location.pathname.startsWith('/settings');

  if (!isOpen) return null;

  return (
    <SettingsLayout>
      {/* Route to appropriate panel */}
      {location.pathname === '/settings' && <SettingsHome />}
      {location.pathname === '/settings/connections' && <ConnectionsPanel />}
      {/* ... more panels */}
    </SettingsLayout>
  );
}

SettingsLayout

Portal-based modal wrapper.

export function SettingsLayout({ children }) {
  const { closeModal } = useSettingsNavigation();

  return createPortal(
    <div className="fixed inset-0 z-50">
      {/* Backdrop */}
      <div
        className="absolute inset-0 bg-black/50 backdrop-blur-sm"
        onClick={closeModal}
      />

      {/* Modal */}
      <div className="absolute inset-4 flex items-center justify-center">
        <div className="bg-white rounded-2xl w-full max-w-[520px] shadow-xl">
          {children}
        </div>
      </div>
    </div>,
    document.body
  );
}

SettingsHome

Main menu with user profile.

export function SettingsHome() {
  const { navigateTo, closeModal } = useSettingsNavigation();
  const user = useAppSelector((state) => state.user.profile);

  const menuItems = [
    { id: 'connections', label: 'Connections', icon: LinkIcon },
    { id: 'messaging', label: 'Messaging', icon: MessageIcon },
    { id: 'privacy', label: 'Privacy', icon: ShieldIcon },
    // ... more items
  ];

  return (
    <div>
      <SettingsHeader user={user} onClose={closeModal} />

      {menuItems.map((item) => (
        <SettingsMenuItem
          key={item.id}
          {...item}
          onClick={() => navigateTo(item.id)}
        />
      ))}
    </div>
  );
}

ConnectionsPanel

Connection management interface.

export function ConnectionsPanel() {
  const { navigateBack } = useSettingsNavigation();
  const [telegramModalOpen, setTelegramModalOpen] = useState(false);

  const telegramStatus = useAppSelector((state) =>
    selectTelegramConnectionStatus(state, userId)
  );

  // Reuses connectOptions from onboarding
  const connections = connectOptions.map((opt) => ({
    ...opt,
    status: opt.id === 'telegram' ? telegramStatus : 'coming-soon'
  }));

  return (
    <SettingsPanelLayout title="Connections" onBack={navigateBack}>
      {connections.map((conn) => (
        <ConnectionItem
          key={conn.id}
          {...conn}
          onConnect={() => conn.id === 'telegram' && setTelegramModalOpen(true)}
        />
      ))}

      <TelegramConnectionModal
        isOpen={telegramModalOpen}
        onClose={() => setTelegramModalOpen(false)}
      />
    </SettingsPanelLayout>
  );
}

Settings Hooks

useSettingsNavigation

URL-based navigation for settings modal.

interface UseSettingsNavigationReturn {
  currentRoute: string;
  navigateTo: (panel: string) => void;
  navigateBack: () => void;
  closeModal: () => void;
}

const { navigateTo, navigateBack, closeModal } = useSettingsNavigation();

// Navigate to panel
navigateTo('connections'); // → /settings/connections

// Go back
navigateBack(); // → /settings

// Close modal
closeModal(); // → previous non-settings route

useSettingsAnimation

Animation state management.

interface UseSettingsAnimationReturn {
  isEntering: boolean;
  isExiting: boolean;
  animationClass: string;
}

const { animationClass } = useSettingsAnimation();

<div className={`modal ${animationClass}`}>
  {/* Content */}
</div>

Settings Components

SettingsHeader

User profile section at top of settings.

interface SettingsHeaderProps {
  user: User | null;
  onClose: () => void;
}

<SettingsHeader user={user} onClose={handleClose} />

SettingsMenuItem

Individual menu item with icon and chevron.

interface SettingsMenuItemProps {
  label: string;
  icon: React.ComponentType;
  onClick: () => void;
  badge?: string;
  disabled?: boolean;
}

<SettingsMenuItem
  label="Connections"
  icon={LinkIcon}
  onClick={() => navigateTo('connections')}
  badge="2"
/>

SettingsBackButton

Back navigation button.

interface SettingsBackButtonProps {
  onClick: () => void;
}

<SettingsBackButton onClick={navigateBack} />

SettingsPanelLayout

Wrapper for settings panels.

interface SettingsPanelLayoutProps {
  title: string;
  onBack: () => void;
  children: React.ReactNode;
}

<SettingsPanelLayout title="Connections" onBack={navigateBack}>
  {/* Panel content */}
</SettingsPanelLayout>

Component Patterns

Reusing Connection Options

The connectOptions array is shared between onboarding and settings:

// Defined in ConnectStep.tsx, imported elsewhere
export const connectOptions = [
  {
    id: 'telegram',
    label: 'Telegram',
    icon: TelegramIcon,
    description: 'Connect your Telegram account',
  },
  {
    id: 'gmail',
    label: 'Gmail',
    icon: GmailIcon,
    description: 'Connect your Gmail account',
    comingSoon: true,
  },
];

Modal via Portal

Settings modal uses createPortal to render outside the component tree:

return createPortal(
  <div className="modal-container">
    {/* Modal content */}
  </div>,
  document.body
);

Controlled vs Uncontrolled

Connection modals are controlled components:

// Parent controls open state
const [isOpen, setIsOpen] = useState(false);

<TelegramConnectionModal
  isOpen={isOpen}
  onClose={() => setIsOpen(false)}
/>

Previous: Pages & Routing | Next: Providers