# Claude handoff: ربط autocomplete بمتجر محدد في تطبيق Kapitano

## الهدف

عند فتح البحث من صفحة متجر، يجب أن تكون اقتراحات autocomplete محصورة بمنتجات وتصنيفات وماركات وكلمات ذلك المتجر. الخادم أصبح يدعم ذلك عبر `shop_id`، والمطلوب في تطبيق الهاتف هو تمرير سياق المتجر إلى طلب الاقتراحات والمحافظة عليه حتى فتح النتائج.

## عقد الـAPI

المسار:

```http
GET /api/v2/search/suggestions
```

الطلب داخل متجر:

```http
GET /api/v2/search/suggestions?q=anker&shop_id=18
```

الطلب في البحث العام:

```http
GET /api/v2/search/suggestions?q=anker
```

### معنى `shop_id`

- القيمة هي `shop.id` القادمة من موديل المتجر.
- لا تستخدم `shop.user_id`.
- لا تستخدم `seller_id` أو `vendor_id`.
- لا ترسل `shop_id=0` أو `shop_id=`. احذف البرامتر بالكامل في البحث العام.

مثال:

```json
{
  "id": 18,
  "user_id": 742,
  "name": "اسم المتجر"
}
```

القيمة التي يجب إرسالها هي `18`.

## المطلوب تنفيذه في التطبيق

### 1. حالة شاشة البحث

أضف قيمة اختيارية إلى شاشة/Controller/Bloc/Provider البحث:

```dart
final int? shopId;
```

عند فتح البحث من صفحة متجر، مرر `shop.id`. عند فتحه من البحث العام، مرر `null`.

### 2. دالة autocomplete

أضف `shopId` إلى دالة جلب الاقتراحات:

```dart
Future<SearchSuggestions> getSearchSuggestions({
  required String query,
  int? shopId,
}) async {
  final queryParameters = <String, dynamic>{
    'q': query.trim(),
    if (shopId != null) 'shop_id': shopId,
  };

  return api.get(
    '/api/v2/search/suggestions',
    queryParameters: queryParameters,
  );
}
```

كيّف المثال مع HTTP client وبنية المشروع الحالية، ولا تنشئ طبقة جديدة إذا كانت دالة الاقتراحات موجودة أصلًا.

### 3. كل طلب أثناء الكتابة

مع كل طلب autocomplete بعد الـdebounce، مرر `shopId` المخزن في حالة الشاشة:

```dart
getSearchSuggestions(
  query: searchText,
  shopId: state.shopId,
);
```

استمر باستخدام آلية debounce وإلغاء الطلب السابق الموجودة حاليًا.

### 4. عند الضغط على اقتراح

عند اختيار كلمة أو منتج أو تصنيف أو ماركة وفتح صفحة النتائج، مرر `shopId` نفسه. يجب ألا يتحول البحث إلى بحث عام بعد اختيار الاقتراح.

صفحة النتائج يجب أن تستمر بإرسال `shop_id` إلى:

```text
GET /api/v2/products/search
GET /api/v2/filter/options
```

### 5. شكل الاستجابة

شكل الاستجابة لم يتغير:

```json
{
  "keywords": [],
  "products": [],
  "categories": [],
  "brands": [],
  "success": true,
  "status": 200
}
```

لا تعدل موديلات `Product`, `Category`, `Brand` أو موديل استجابة الاقتراحات بسبب هذا التغيير.

عند إرسال `shop_id` يقوم الخادم بحصر:

- `products` بمنتجات المتجر.
- `categories` بالتصنيفات الموجودة في منتجات المتجر.
- `brands` بالماركات الموجودة في منتجات المتجر.
- `keywords` مصفوفة فارغة داخل المتجر لتجنب الكلمات العامة وتقليل كلفة الطلب.

## معالجة الأخطاء

إذا كان `shop_id` غير صالح أو المتجر غير موجود، يرجع الخادم HTTP `422`:

```json
{
  "result": false,
  "message": "...",
  "errors": {
    "shop_id": ["..."]
  }
}
```

لا تعِد الطلب تلقائيًا من دون `shop_id`، لأن ذلك سيعرض اقتراحات جميع المتاجر. استخدم معالجة الخطأ الحالية في شاشة البحث.

## قيود يجب الحفاظ عليها

- لا تغيّر endpoint أو أسماء حقول الاستجابة.
- لا تفلتر القوائم محليًا داخل التطبيق.
- لا تستخرج رقم المتجر من المنتج المقترح.
- لا تفقد `shopId` عند إعادة بناء الشاشة، اختيار اقتراح، الفرز، الفلترة أو pagination.
- كل الطلبات تستخدم هيدرات التطبيق الحالية، ومنها `System-Key` و`App-Language`.

## اختبارات مطلوبة في مشروع التطبيق

أضف أو حدّث اختبارات Repository/API client وطبقة الحالة لتثبت التالي:

1. داخل متجر رقمه `18`، طلب autocomplete يحتوي `q` و`shop_id=18`.
2. في البحث العام، الطلب يحتوي `q` ولا يحتوي مفتاح `shop_id`.
3. اختيار اقتراح داخل متجر يحافظ على `shopId=18` عند فتح النتائج.
4. طلب `/products/search` بعد اختيار الاقتراح يحتوي `shop_id=18`.
5. طلب `/filter/options` في شاشة النتائج يحتوي `shop_id=18`.
6. تغيير النص بسرعة لا يعرض استجابة طلب قديم، مع بقاء `shop_id` في الطلب الأحدث.
7. استجابة `422` لا تسبب إعادة طلب عام من دون `shop_id`.
8. parsing للاستجابة الحالية يبقى ناجحًا دون تعديل الموديلات.
9. داخل المتجر، `keywords` الفارغة لا تعرض عنوان قسم أو مساحة فارغة في الواجهة.

## فحص يدوي نهائي

استخدم متجرين مختلفين A وB:

1. افتح البحث من متجر A واكتب كلمة موجودة في متجر A ومتجر B.
2. افحص Network logs وتأكد أن الطلب يحتوي `shop_id` الخاص بمتجر A.
3. تأكد أن اقتراحات المنتجات لا تحتوي منتجًا من متجر B.
4. اضغط على اقتراح وتأكد أن النتائج والفلاتر ما زالت محصورة بمتجر A.
5. افتح البحث العام واكتب الكلمة نفسها، وتأكد أن `shop_id` غير موجود وأن الاقتراحات العامة ما زالت تعمل.
6. اختبر متجرًا لا يملك نتائج للكلمة، ويجب أن تظهر قوائم فارغة بدل نتائج عامة.

ترتيب العرض داخل المتجر: المنتجات، ثم التصنيفات، ثم الماركات. أخفِ قسم الكلمات عندما تكون `keywords` فارغة. خارج المتجر يبقى ترتيب وتجميع الاقتراحات الحالي كما هو.

## شرط التسليم

قبل اعتبار تعديل التطبيق مكتملًا، أرسل:

- أسماء الملفات التي تغيرت.
- الاختبارات التي شُغلت ونتيجتها.
- مثال Network request فعلي يحتوي `q` و`shop_id` داخل متجر.
- مثال Network request فعلي للبحث العام من دون `shop_id`.
- تأكيد أن الضغط على الاقتراح حافظ على المتجر في `/products/search` و`/filter/options`.
