# Vue-CLI Branch Port Plan (Part 2)

Port code changes from `vue-cli` branch (commits `66b305c4..28f1a6f6`, 9 commits) into the current `dev` branch.
Continues from [vue-cli-port.md](./vue-cli-port.md) which covered commits `16c4065..66b305c`.

---

## 1. Auto-Run Limits System

### New constant: `SHOPEE_AUTO_RUN_LIMITS`

**File:** `src/app/constant/shopee.js`

Add after `SHOPEE_USAGE_LIMITS`:

```js
/** @type {import('@type/Constant/Usage').ShopeeAutoRunLimits} */
export const SHOPEE_AUTO_RUN_LIMITS = {
  ratingOrder: {
    Vip: { runLimit: 10 },
    VipPlus: { runLimit: Infinity },
  },
  replyRating: {
    Vip: { runLimit: 10 },
    VipPlus: { runLimit: Infinity },
  },
  sendMessage: {
    Vip: { runLimit: 50 },
    VipPlus: { runLimit: Infinity },
  },
};
```

### New types

**File:** `src/app/types/Constant/Usage.js`

Append:

```js
/**
 * @typedef {object} AutoRunConfig
 * @property {number} runLimit
 */

/**
 * @typedef {object} AutoRunPlanConfig
 * @property {AutoRunConfig} Vip
 * @property {AutoRunConfig} VipPlus
 */

/**
 * @typedef {object} ShopeeAutoRunLimits
 * @property {AutoRunPlanConfig} ratingOrder
 * @property {AutoRunPlanConfig} replyRating
 * @property {AutoRunPlanConfig} sendMessage
 */
```

### i18n changes

**File:** `src/app/plugins/i18n/lang/vi.json`

**Remove:**
- `"ALERT_REQUIRED_PLAN_PLUS"`
- `"FEAUTRE_REQUIRE_VIP_PLUS"`
- `"FEAUTRE_SPY_VIP_PLUS"`
- `"FEAUTRE_STOP_VIP_PLUS"`
- `"USER_REQUIRE_VIP_PLUS"`

**Add:**
- `"FEATURE_RUN_LIMIT_INFO": "Gói Vip giới hạn {limit} lượt mỗi lần chạy. Nâng cấp VipPlus để không giới hạn."`
- `"FEATURE_RUN_LIMIT_REACHED": "Đã đạt giới hạn {sent}/{limit} lượt. Nâng cấp gói VipPlus để chạy không giới hạn."`
- `"UPGRADE_PLAN_CONTACT_ZALO": "Khi gia hạn xong, liên hệ Zalo để được cập nhật gói"`

### View changes — Same pattern applied to 3 views

**Affected views:**
- `views/Shopee/Seller/Auto/RatingOrder.vue` — uses `SHOPEE_AUTO_RUN_LIMITS.ratingOrder`
- `views/Shopee/Seller/Auto/ReplyRating.vue` — uses `SHOPEE_AUTO_RUN_LIMITS.replyRating`
- `views/Shopee/Seller/Auto/SendMessage/Send.vue` — uses `SHOPEE_AUTO_RUN_LIMITS.sendMessage`

#### Pattern for each view

**Import:**
```js
import { SHOPEE_AUTO_RUN_LIMITS } from '@/constant/shopee';
```

**Template — Add Alert before form:**
```html
<Alert
  v-if="isRunLimitVisible"
  type="warning"
  show-icon
  class="mb-3"
>
  {{ $t('FEATURE_RUN_LIMIT_INFO', { limit: autoRunLimit }) }}
</Alert>
```

**Template — Disable button when limit reached:**
```html
:disabled="... || isRunLimitReached"
```

**Computed — Add 3 computed properties:**
```js
autoRunLimit() {
  const config = SHOPEE_AUTO_RUN_LIMITS.{featureKey}[this.getUserPlan];
  if (config) {
    return config.runLimit;
  }
  return 0;
},
isRunLimitVisible() {
  return Number.isFinite(this.autoRunLimit) && this.autoRunLimit > 0;
},
isRunLimitReached() {
  return this.isRunLimitVisible && this.{handler}.sent >= this.autoRunLimit;
},
```

Where `{featureKey}` = `ratingOrder` / `replyRating` / `sendMessage` and `{handler}` = `ratingHandler` / `replyHandler` / `collectData`.

**Script — Modify run loop:**

Before (RatingOrder/ReplyRating):
```js
if (!this.getIsVipPlus) {
  this.$Message.warning(this.$t('FEAUTRE_STOP_VIP_PLUS'));
  this.ratingHandler.log.push(this.$t('FEAUTRE_STOP_VIP_PLUS'));
  isAllow = false;
}
this.ratingHandler.sent += 1;
```

After:
```js
this.ratingHandler.sent += 1;
if (this.ratingHandler.sent >= this.autoRunLimit) {
  const msg = this.$t('FEATURE_RUN_LIMIT_REACHED', {
    sent: this.ratingHandler.sent,
    limit: this.autoRunLimit,
  });
  this.$Message.warning(msg);
  this.ratingHandler.log.push(msg);
  isAllow = false;
}
```

Also:
- Add `this.ratingHandler.sent = 0;` at start of handler
- Remove `if (isAllow)` wrapper around final `this.ratingHandler.loading = false;` — always set loading false

For SendMessage/Send.vue — add limit check in send loop:
```js
if (totalSent >= this.autoRunLimit) {
  reachedMax = true;
  const msg = this.$t('FEATURE_RUN_LIMIT_REACHED', {
    sent: totalSent,
    limit: this.autoRunLimit,
  });
  this.$Message.warning(msg);
  this.addLog('warning', msg);
  break;
}
```
Also add `|| this.isRunLimitReached` to `canStartSending` computed.

---

## 2. Usage Limits Adjustment

**File:** `src/app/constant/shopee.js` — `SHOPEE_USAGE_LIMITS`

| Feature | Plan | Field | Old | New |
|---------|------|-------|-----|-----|
| productClone | Free | dailyLimit | 50 | 20 |
| productClone | Free | waitFor | 30 | 60 |
| productClone | Free | totalMax | 500 | 200 |
| productClone | Vip | dailyLimit | 200 | Infinity |
| productClone | VipPlus | dailyLimit | 500 | Infinity |
| downloadImage | Free | waitFor | 10 | 20 |
| downloadImage | Vip | dailyLimit | 9999 | Infinity |
| downloadImage | VipPlus | dailyLimit | 9999 | Infinity |
| downloadVideo | Free | waitFor | 20 | 30 |
| downloadVideo | Vip | dailyLimit | 9999 | Infinity |
| downloadVideo | VipPlus | dailyLimit | 9999 | Infinity |

---

## 3. Content Script Refactor — Centralized Constants

### New file: `src/content/shopee/client/constant.js` (89 lines)

Extracts all hardcoded element IDs, CSS classes, selectors, and button styles into one file:

```js
export const ELEMENT_IDS = {
  SPY_SHOP_BTN: 'autoshopee_spy_shop_btn',
  SPY_SEARCH_BTN: 'autoshopee_spy_search_btn',
  STATS_SEARCH: 'autoshopee_stats_search',
  PRODUCT_SEARCH: 'autoshopee_product_search',
  PRODUCT_SPY_DETAIL: 'autoshopee-product-spy-detail',
  PRODUCT_SPY_BUTTON: 'autoshopee-product-spy-button',
  PRODUCT_OPEN_BUTTON: 'autoshopee-product-open-button',
  PRODUCT_REVIEW_BUTTON: 'autoshopee-product-review-button',
  BTN_DOWNLOAD_IMAGE: 'autoshopee-btn-download-image',
};

export const CSS_CLASSES = {
  COPY_BTN: 'autoshopee-copy-btn',
  SPY_ADS_BTN: 'autoshopee-spyads-btn',
  BID_ADS_BTN: 'autoshopee-bidads-btn',
};

export const SELECTORS = {
  SEARCH_HEADER: 'span.shopee-search-result-header__text',
  SEARCH_PRODUCT_LIST: 'section > ul > li > div > div > div > a.contents',
  SEARCH_ITEM_RESULT: 'ul.row.shopee-search-item-result__items',
  IMAGE_WRAPPER: 'div.relative.z-0.w-full.pt-full',
  PRODUCT_ROOT: 'div.page-product__content > ...',
  PRODUCT_REVIEW: 'div.product-rating-overview',
  PRODUCT_DETAIL_HEADING: 'div.page-product__content--left > ...',
  SHOP_SELECTION: '#sll2-pdp-product-shop > section',
  IMAGE_ROOT_IN_PRODUCT: 'div.DfREf5 > ...',
  IMAGE_ROOT_IN_DESCRIPTION: 'div.page-product__content > ...',
};

export const SPY_BTN_BASE_STYLE = { /* fixed button style */ };
export const SPY_BTN_HOVER_STYLE = { /* scale + shadow on hover */ };
export const SPY_BTN_NORMAL_STYLE = { /* reset hover */ };

export const CONTENT_OPTIONS = {
  spyShopBtn: true,
  spySearchBtn: true,
  spyProductDetail: true,
  spyAds: true,
  cloneBtn: true,
  imageDownloader: true,
  floatSpySearchBtn: false,   // disabled
  floatSpyShopBtn: false,     // disabled
};

export const SHOP_RESERVED_SEGMENTS = [
  'search', 'buyer', 'cart', 'account', 'chat', 'order',
  'product', 'mall', 'voucher', 'blog', 'help',
];
```

### Modified files

**`src/content/shopee/client/loader.js`**
- Import `CONTENT_OPTIONS`
- Wrap each loader init in feature toggle check:
  - `productSearchLoader.initialize()` → `if (CONTENT_OPTIONS.spySearchBtn)`
  - `productDetailLoader.initialize()` → `if (CONTENT_OPTIONS.spyProductDetail)`
  - `shopSpyBtnLoader.initialize()` → `if (CONTENT_OPTIONS.spyShopBtn)`
  - `btnImageDownloader()` interval → `if (CONTENT_OPTIONS.imageDownloader)`
  - `window.onscroll` handler → `if (CONTENT_OPTIONS.spySearchBtn)`
  - `onUrlChange` spy btn → `if (CONTENT_OPTIONS.spyShopBtn)`

**`src/content/shopee/client/product/detail.js`**
- Import `ELEMENT_IDS, SELECTORS`
- Replace 8 hardcoded strings in `this.elementProduct` constructor with constants

**`src/content/shopee/client/product/search.js`**
- Import `ELEMENT_IDS, SELECTORS, CSS_CLASSES, SPY_BTN_BASE_STYLE, SPY_BTN_HOVER_STYLE, SPY_BTN_NORMAL_STYLE, CONTENT_OPTIONS`
- Import `getShopeeSellerId` from `@content/shopee/client/shop`
- Replace all hardcoded selectors/IDs/classes with constants
- Float spy search button gated by `CONTENT_OPTIONS.floatSpySearchBtn`
- Replace inline button style objects with shared constants

**`src/content/shopee/client/service.js`**
- Import `ELEMENT_IDS, SELECTORS`
- Replace 3 hardcoded strings in `btnImageDownloader()`

**`src/content/shopee/client/shop/spyBtn.js`**
- Import `ELEMENT_IDS, SPY_BTN_BASE_STYLE, SPY_BTN_HOVER_STYLE, SPY_BTN_NORMAL_STYLE, SHOP_RESERVED_SEGMENTS, CONTENT_OPTIONS`
- Remove local `BTN_ID`, `RESERVED_SEGMENTS`, `getButtonStyle()` → use imports
- Add `if (!CONTENT_OPTIONS.floatSpyShopBtn) return;` guard in `appendBtn()`
- Use `Object.assign(btn.style, SPY_BTN_BASE_STYLE, { fontSize: '13px', fontWeight: '600' })`

---

## 4. Seller Popup Revert

**File:** `src/app/components/Shopee/Seller/Popup.vue`

Major simplification — revert from fancy profile card back to iView Form layout.

### Template

**Before:** Custom profile card with cover gradient, avatar ring, identity section, info pills, Collapse accordion for SPCF/cookies
**After:** Simple `<Form>` with `<FormItem>` for username (readonly), SPCF (editable), cookies (editable), phone/email/shopId (readonly if authed), sync button

### Script

- Component name: `ShopPopup` → `ShopInfo`
- Added `msg: ''` data field for status message
- `init()`: changed `return` → `return true`
- `changeSpcf()`: extract `'SPC_F'` to `const name = 'SPC_F'`
- `changeCookie()`: restructured to `if (raw) { try {...} catch {} }` pattern, removed error logging
- `createShop()`: renamed `result` → `createShop` variable, improved error object formatting
- Moved `computed` and `created` after `methods`
- Removed `isShopAuthed` from being inline computed to being a normal computed that references `this.id`

### Style

- Removed entire `<style>` block (~175 lines of SCSS)

---

## 5. UpgradePlan Contact Info

**File:** `src/app/components/Single/UpgradePlan.vue`

### Template

Add contact section after promotion button:
```html
<div :class="['upgrade-plan__contact', isUsePopup ? 'mt-10' : 'mt-20']">
  <p class="mb-5">{{ $t('UPGRADE_PLAN_CONTACT_ZALO') }}</p>
  <div class="upgrade-plan__contact-links">
    <a v-for="link in contactLinks" :key="link.label" :href="link.url" target="_blank">
      {{ link.label }}
    </a>
    <span v-for="phone in hotlines" :key="phone">{{ phone }}</span>
  </div>
</div>
```

### Data

```js
data() {
  return {
    contactLinks: [
      { label: 'Facebook', url: 'https://www.facebook.com/autoshopee.com.vn' },
      { label: 'Zalo', url: 'https://zalo.me/2141564390855067259' },
    ],
    hotlines: ['0904.628.850', '0907.298.768'],
  };
},
```

### Style

```scss
.upgrade-plan__contact-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  align-items: center;
}

.upgrade-plan__contact-links a {
  color: #2d8cf0;
  text-decoration: none;
  &:hover { text-decoration: underline; }
}
```

---

## 6. Router Fix — URL Match Priority

**File:** `src/app/store/modules/router.js` — `RouterUrlMatch` mutation

Move `shopeeUrlMatchProductId` check **before** `tiktokUrlMatchProductId` check. Previously Shopee product URL matching happened after TikTok checks, causing incorrect routing.

**Before order:**
1. shopeetabUrl → tiktokUrlMatchProductId → tiktokShopUrl → tiktokTabUrl → shopeeUrlMatchProductId

**After order:**
1. shopeetabUrl → **shopeeUrlMatchProductId** → tiktokUrlMatchProductId → tiktokShopUrl → tiktokTabUrl

---

## 7. Lint Cleanup

**Files:** Minor formatting in:
- `components/Shopee/Spy/Search/Keyword.vue`
- `components/Shopee/Spy/Search/Shop.vue`
- `components/Single/Register.vue`
- `components/Single/UpgradePlan.vue`

These are auto-fixable lint changes (spacing, line breaks). Will be applied naturally when the other changes are ported.

---

## Port Order (recommended)

1. **Constants & types** — `constant/shopee.js` (SHOPEE_AUTO_RUN_LIMITS + USAGE_LIMITS values), `types/Constant/Usage.js`
2. **i18n** — add/remove keys in `vi.json`
3. **Auto-run limit views** — RatingOrder, ReplyRating, SendMessage/Send
4. **Content script constants** — create `src/content/shopee/client/constant.js`
5. **Content script refactor** — loader.js, product/detail.js, product/search.js, service.js, shop/spyBtn.js
6. **Seller Popup revert** — Popup.vue
7. **UpgradePlan contact** — UpgradePlan.vue
8. **Router fix** — store/modules/router.js
