---
name: react-rtl-arabic
description: >
  بناء مكونات React ثنائية اللغة (عربي/إنجليزي) مع دعم كامل لـ RTL.
  استخدم هذا الـ skill عند: إنشاء واجهة عربية، بناء component بـ React،
  تصميم form عربي، بناء جدول بيانات، إنشاء dashboard، تصميم layout ثنائي اللغة،
  أو أي مهمة frontend تحتاج دعم العربية و RTL. يشمل i18n و accessibility.
---

# React RTL Arabic Components
# مكونات React العربية

## Project Setup

```
client/src/
├── modules/                    # Feature modules
├── shared/
│   ├── components/
│   │   ├── layout/
│   │   │   ├── AppLayout.tsx
│   │   │   ├── Sidebar.tsx
│   │   │   ├── Header.tsx
│   │   │   └── PageContainer.tsx
│   │   ├── ui/
│   │   │   ├── DataTable.tsx
│   │   │   ├── FormField.tsx
│   │   │   ├── Modal.tsx
│   │   │   ├── StatusBadge.tsx
│   │   │   ├── SearchInput.tsx
│   │   │   ├── DatePicker.tsx       # Hijri + Gregorian
│   │   │   ├── FileUpload.tsx
│   │   │   └── ConfirmDialog.tsx
│   │   └── feedback/
│   │       ├── LoadingSpinner.tsx
│   │       ├── EmptyState.tsx
│   │       └── ErrorBoundary.tsx
│   ├── hooks/
│   │   ├── useAuth.ts
│   │   ├── usePagination.ts
│   │   ├── useDebounce.ts
│   │   ├── usePermission.ts
│   │   └── useDirection.ts
│   ├── api/
│   │   └── apiClient.ts
│   ├── i18n/
│   │   ├── config.ts
│   │   ├── ar.json
│   │   └── en.json
│   └── styles/
│       ├── rtl.css
│       └── theme.ts
```

## RTL Foundation

```tsx
// App.tsx - RTL Provider
import { DirectionProvider } from './shared/contexts/DirectionContext';
import { I18nextProvider } from 'react-i18next';

const App: React.FC = () => {
  const { i18n } = useTranslation();
  const direction = i18n.language === 'ar' ? 'rtl' : 'ltr';

  return (
    <DirectionProvider direction={direction}>
      <div dir={direction} className={direction === 'rtl' ? 'font-cairo' : 'font-inter'}>
        <AppLayout>
          <AppRoutes />
        </AppLayout>
      </div>
    </DirectionProvider>
  );
};
```

## Core Components

### DataTable - جدول البيانات
```tsx
interface DataTableProps<T> {
  columns: ColumnDef<T>[];
  data: T[];
  pagination?: PaginationInfo;
  isLoading?: boolean;
  onPageChange?: (page: number) => void;
  onSearch?: (term: string) => void;
  onAdd?: () => void;
  addPermission?: string;
  exportable?: boolean;
  searchPlaceholder?: string;
}

const DataTable = <T extends Record<string, any>>({
  columns, data, pagination, isLoading, onPageChange,
  onSearch, onAdd, addPermission, exportable = true,
  searchPlaceholder = 'بحث...'
}: DataTableProps<T>) => {
  const { t } = useTranslation();
  const { hasPermission } = useAuth();

  return (
    <div className="space-y-4">
      {/* Toolbar */}
      <div className="flex justify-between items-center">
        <SearchInput
          placeholder={searchPlaceholder}
          onSearch={onSearch}
        />
        <div className="flex gap-2">
          {exportable && (
            <Button variant="outline" onClick={handleExport}>
              <DownloadIcon className="ml-2 h-4 w-4" />
              {t('common.export')}
            </Button>
          )}
          {onAdd && hasPermission(addPermission!) && (
            <Button onClick={onAdd}>
              <PlusIcon className="ml-2 h-4 w-4" />
              {t('common.add')}
            </Button>
          )}
        </div>
      </div>

      {/* Table */}
      <div className="rounded-md border overflow-x-auto">
        <table className="w-full text-right">
          <thead className="bg-gray-50">
            <tr>
              {columns.map(col => (
                <th key={col.accessorKey} className="px-4 py-3 text-sm font-medium text-gray-700">
                  {col.header}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {isLoading ? (
              <tr><td colSpan={columns.length} className="text-center py-8">
                <LoadingSpinner />
              </td></tr>
            ) : data.length === 0 ? (
              <tr><td colSpan={columns.length}>
                <EmptyState message={t('common.noData')} />
              </td></tr>
            ) : (
              data.map((row, i) => (
                <tr key={i} className="border-b hover:bg-gray-50">
                  {columns.map(col => (
                    <td key={col.accessorKey} className="px-4 py-3 text-sm">
                      {col.cell ? col.cell({ value: row[col.accessorKey], row }) 
                        : row[col.accessorKey]}
                    </td>
                  ))}
                </tr>
              ))
            )}
          </tbody>
        </table>
      </div>

      {/* Pagination */}
      {pagination && (
        <Pagination
          current={pagination.pageNumber}
          total={pagination.totalPages}
          onChange={onPageChange!}
        />
      )}
    </div>
  );
};
```

### FormField - حقل النموذج
```tsx
interface FormFieldProps {
  label: string;
  name: string;
  type?: 'text' | 'number' | 'email' | 'textarea' | 'select' | 'date' | 'file';
  required?: boolean;
  error?: string;
  register: UseFormRegister<any>;
  options?: { value: string | number; label: string }[];
  placeholder?: string;
}

const FormField: React.FC<FormFieldProps> = ({
  label, name, type = 'text', required, error, register, options, placeholder
}) => (
  <div className="space-y-1">
    <label htmlFor={name} className="block text-sm font-medium text-gray-700">
      {label}
      {required && <span className="text-red-500 mr-1">*</span>}
    </label>
    
    {type === 'select' ? (
      <select id={name} {...register(name)} className="form-select w-full rounded-md">
        <option value="">-- اختر --</option>
        {options?.map(opt => (
          <option key={opt.value} value={opt.value}>{opt.label}</option>
        ))}
      </select>
    ) : type === 'textarea' ? (
      <textarea id={name} {...register(name)} rows={4}
        className="form-textarea w-full rounded-md" placeholder={placeholder} />
    ) : (
      <input id={name} type={type} {...register(name)}
        className="form-input w-full rounded-md" placeholder={placeholder} />
    )}
    
    {error && <p className="text-sm text-red-600">{error}</p>}
  </div>
);
```

### PageContainer - حاوية الصفحة
```tsx
interface PageContainerProps {
  title: string;
  subtitle?: string;
  breadcrumbs?: { label: string; href?: string }[];
  actions?: React.ReactNode;
  children: React.ReactNode;
}

const PageContainer: React.FC<PageContainerProps> = ({
  title, subtitle, breadcrumbs, actions, children
}) => (
  <div className="space-y-6">
    {/* Breadcrumbs */}
    {breadcrumbs && (
      <nav className="flex text-sm text-gray-500">
        {breadcrumbs.map((crumb, i) => (
          <span key={i} className="flex items-center">
            {i > 0 && <ChevronLeftIcon className="mx-2 h-4 w-4" />}
            {crumb.href ? (
              <Link to={crumb.href} className="hover:text-primary">{crumb.label}</Link>
            ) : (
              <span className="text-gray-900">{crumb.label}</span>
            )}
          </span>
        ))}
      </nav>
    )}

    {/* Header */}
    <div className="flex justify-between items-center">
      <div>
        <h1 className="text-2xl font-bold text-gray-900">{title}</h1>
        {subtitle && <p className="mt-1 text-sm text-gray-500">{subtitle}</p>}
      </div>
      {actions && <div className="flex gap-2">{actions}</div>}
    </div>

    {/* Content */}
    <div className="bg-white rounded-lg shadow p-6">
      {children}
    </div>
  </div>
);
```

## i18n Configuration

```typescript
// i18n/config.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import ar from './ar.json';
import en from './en.json';

i18n.use(initReactI18next).init({
  resources: { ar: { translation: ar }, en: { translation: en } },
  lng: 'ar', // Arabic by default
  fallbackLng: 'ar',
  interpolation: { escapeValue: false },
});
```

```json
// i18n/ar.json
{
  "common": {
    "add": "إضافة",
    "edit": "تعديل",
    "delete": "حذف",
    "save": "حفظ",
    "cancel": "إلغاء",
    "search": "بحث",
    "export": "تصدير",
    "import": "استيراد",
    "print": "طباعة",
    "confirm": "تأكيد",
    "back": "رجوع",
    "noData": "لا توجد بيانات",
    "loading": "جاري التحميل...",
    "active": "فعال",
    "inactive": "غير فعال",
    "yes": "نعم",
    "no": "لا",
    "actions": "الإجراءات",
    "status": "الحالة",
    "createdAt": "تاريخ الإنشاء",
    "details": "التفاصيل",
    "required": "حقل مطلوب",
    "success": "تمت العملية بنجاح",
    "error": "حدث خطأ"
  },
  "auth": {
    "login": "تسجيل الدخول",
    "logout": "تسجيل الخروج",
    "nafathLogin": "الدخول عبر نفاذ",
    "unauthorized": "غير مصرح لك بالوصول"
  }
}
```

## RTL CSS Utilities

```css
/* rtl.css - Critical RTL overrides */
[dir="rtl"] {
  text-align: right;
}

[dir="rtl"] .flex-row { flex-direction: row-reverse; }
[dir="rtl"] .ml-2 { margin-left: 0; margin-right: 0.5rem; }
[dir="rtl"] .mr-2 { margin-right: 0; margin-left: 0.5rem; }
[dir="rtl"] .pl-4 { padding-left: 0; padding-right: 1rem; }
[dir="rtl"] .pr-4 { padding-right: 0; padding-left: 1rem; }
[dir="rtl"] .text-left { text-align: right; }
[dir="rtl"] .text-right { text-align: left; }

/* Arabic Typography */
.font-cairo { font-family: 'Cairo', sans-serif; }

/* Form inputs RTL */
[dir="rtl"] input, [dir="rtl"] textarea, [dir="rtl"] select {
  text-align: right;
}

/* Table alignment */
[dir="rtl"] th, [dir="rtl"] td {
  text-align: right;
}

/* Sidebar */
[dir="rtl"] .sidebar {
  right: 0;
  left: auto;
  border-left: 1px solid #e5e7eb;
  border-right: none;
}
```

## API Client with Arabic Error Handling

```typescript
// api/apiClient.ts
import axios from 'axios';
import { toast } from 'react-hot-toast';

const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_URL,
  headers: { 'Accept-Language': 'ar' }
});

apiClient.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

apiClient.interceptors.response.use(
  response => response,
  error => {
    const message = error.response?.data?.messageAr || 'حدث خطأ غير متوقع';
    
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    } else if (error.response?.status === 403) {
      toast.error('ليس لديك صلاحية للقيام بهذا الإجراء');
    } else {
      toast.error(message);
    }
    
    return Promise.reject(error);
  }
);

export { apiClient };
```

## Key Conventions

- Arabic is always the primary language, English is secondary
- All labels, messages, and placeholders in Arabic first
- Use `text-right` as default alignment
- Font: Cairo for Arabic, Inter for English
- Date display: Hijri primary, Gregorian secondary
- Numbers: Arabic numerals (not Hindi numerals) unless user preference
- Form validation messages always in Arabic
- Toast/notification messages in Arabic
- Use `useTranslation()` hook for all static text
- RTL-aware icons (arrows, chevrons should flip)
