# DataTable Pattern

## COMPONENT: DataTable

```tsx
import { DataTable } from '@/components/ui/DataTable';

<DataTable
  data={users}
  columns={[
    { key: 'name', label: 'Name', sortable: true },
    { key: 'role', label: 'Role', render: (user) => <Badge>{user.role}</Badge> }
  ]}
  pagination={{ pageSize: 10 }}
  searchable
  onRowClick={(user) => navigate(`/users/${user.id}`)}
/>
```

## Full List Page with DataTable

> **Complete page showing DataTable integration with loading, error, empty states, CSS variables, and i18n.**

```tsx
import { useState, useCallback, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { Loader2, AlertCircle } from 'lucide-react';
import { DocToggleButton } from '@/components/docs/DocToggleButton';
import { DataTable } from '@/components/ui/DataTable';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
  isActive: boolean;
}

export function UsersPage() {
  const { t } = useTranslation(['users']);
  const navigate = useNavigate();
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [users, setUsers] = useState<User[]>([]);

  const loadData = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      const result = await usersApi.getAll();
      setUsers(result?.items ?? []);
    } catch (err: any) {
      setError(err.message || t('users:errors.loadFailed', 'Failed to load users'));
    } finally {
      setLoading(false);
    }
  }, [t]);

  useEffect(() => { loadData(); }, [loadData]);

  // Loading state
  if (loading) {
    return (
      <div className="flex items-center justify-center min-h-[400px]">
        <Loader2 className="w-8 h-8 animate-spin text-[var(--color-accent-500)]" />
      </div>
    );
  }

  // Error state with retry
  if (error) {
    return (
      <div className="flex items-center justify-center min-h-[400px]">
        <div className="text-center">
          <AlertCircle className="h-8 w-8 text-[var(--error-text)] mx-auto mb-2" />
          <p className="text-[var(--text-secondary)]">{error}</p>
          <button
            onClick={loadData}
            className="mt-4 px-4 py-2 bg-[var(--color-accent-500)] text-white rounded-[var(--radius-button)]"
          >
            {t('common:actions.retry', 'Retry')}
          </button>
        </div>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex items-center justify-between">
        <h1 className="text-2xl font-bold text-[var(--text-primary)]">
          {t('users:title', 'Users')}
        </h1>
        <div className="flex items-center gap-2">
          <DocToggleButton />
          <button
            onClick={() => navigate('create')}
            className="px-4 py-2 bg-[var(--color-accent-500)] text-white rounded-[var(--radius-button)]"
          >
            {t('users:actions.create', 'Create User')}
          </button>
        </div>
      </div>

      {/* Empty state */}
      {users.length === 0 ? (
        <div className="text-center py-12 text-[var(--text-secondary)]">
          {t('users:empty', 'No users found.')}
        </div>
      ) : (
        /* DataTable with sorting, search, pagination, row click */
        <DataTable
          data={users}
          columns={[
            { key: 'name', label: t('users:columns.name', 'Name'), sortable: true },
            { key: 'email', label: t('users:columns.email', 'Email'), sortable: true },
            { key: 'role', label: t('users:columns.role', 'Role'), sortable: true },
            {
              key: 'isActive',
              label: t('users:columns.status', 'Status'),
              render: (user) => (
                <span className={`px-2 py-0.5 rounded text-xs ${
                  user.isActive
                    ? 'bg-[var(--success-bg)] text-[var(--success-text)]'
                    : 'bg-[var(--error-bg)] text-[var(--error-text)]'
                }`}>
                  {user.isActive
                    ? t('common:status.active', 'Active')
                    : t('common:status.inactive', 'Inactive')}
                </span>
              ),
            },
          ]}
          searchable
          pagination={{ pageSize: 10 }}
          onRowClick={(user) => navigate(`${user.id}`)}
        />
      )}
    </div>
  );
}
```

**Key points:**
- **NEVER** use raw `<table>` — always use `DataTable`
- **ALL** text uses `t('namespace:key', 'Fallback')` for i18n
- **ALL** colors use CSS variables (`var(--...)`) — zero hardcoded Tailwind colors
- **Loading/Error/Empty** states are always handled
- **Row click** navigates to detail page via relative path
- **Status badges** use semantic CSS variables (`--success-bg`, `--error-text`)

---

## COMPONENT: Tooltip

```tsx
import { Tooltip } from '@/components/ui/Tooltip';

// Variants: default, error, warning, success, info
// Positions: top, bottom, left, right

<Tooltip content="Permission required" variant="error">
  <button disabled>Protected action</button>
</Tooltip>
```

### Disabled Button with Explanation Pattern
```tsx
const canExecute = hasPermission('module.action.execute');
<Tooltip content={!canExecute ? t('errors.noPermission') : undefined} variant="error" disabled={canExecute}>
  <button disabled={!canExecute}>Action</button>
</Tooltip>
```
