feat(web): migrate CRM screens to Ant Design Vue with central theme

Adopt ant-design-vue and a shared antdTheme for leads, pipeline, invoices, and shared form controls under strict single-page governance.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-18 19:39:59 +03:30
co-authored by Cursor
parent ab538b8304
commit 4abab55a6b
40 changed files with 7529 additions and 11795 deletions
+85
View File
@@ -0,0 +1,85 @@
---
description: Hard governance for Ant Design Vue v4 migration — isolated page-by-page, no manual CSS, 24-col grid, default controlHeight 36
alwaysApply: true
---
# Ant Design Vue v4 — Strict Governance (locked)
Source of truth for migrated UI: **Ant Design Vue 4** + central theme [`apps/web/src/theme/antdTheme.ts`](apps/web/src/theme/antdTheme.ts).
Legacy CRM UI (`components/ui/*`, `.crm-*`, `.btn` / `.field` in `styles/`) remains for **unmigrated** screens only. Never mix systems inside one page.
## Hard rules
1. **Single-page migration lock**
Migrate **exactly one** route/view (or one clearly named page folder) per change set. Do not “clean up” neighboring pages, shared legacy primitives, or global CSS while migrating a page.
2. **No edits to legacy kit during Ant work**
Do **not** modify `apps/web/src/components/ui/**` or broaden changes across `views/` beyond the single page under migration, unless the user explicitly names that page.
2b. **Child components of a migrated View (mandatory)**
هنگام بازنویسی یک صفحه (View)، ایجنت موظف است علاوه بر خود فایل View، تمام کامپوننت‌های فرزندی را که در آن View ایمپورت شده‌اند و حاوی اینپوت/سلکت/دکمه هستند نیز بازبینی کرده و درون آن‌ها را به کامپوننت‌های رسمی `ant-design-vue` تبدیل کند. هیچ تگ `<input>`, `<select>`, `<textarea>`, `<button>` خامی نباید در صفحه یا کامپوننت‌های فرزندِ مصرفی آن باقی بماند.
When rewriting a View, the agent **must** also review every component imported by that View that contains inputs/selects/buttons, and convert their internals to official `ant-design-vue` controls.
**No raw** `<input>`, `<select>`, `<textarea>`, or `<button>` may remain in the View **or** in those consumed child components. Prefer native Ant equivalents (`a-select` / `mode="multiple|tags"` / `a-time-picker` / …) over custom pickers when logic allows; if domain logic is required (e.g. Jalali), convert that child file to Ant chrome (`a-input` + `a-popover`, etc.) instead of leaving raw HTML. Do **not** leave legacy `UiInput` / `UiSelect` / `UiButton` / `UiField` / `UiTextarea` in the View or those children.
3. **Ant-only UI on migrated pages**
On a migrated page, build controls with Ant Design Vue (`a-button`, `a-form`, `a-table`, `a-card`, …) and `@ant-design/icons-vue`. Do **not** introduce new usage of `UiButton`, `UiInput`, `.btn`, `.field`, or ad-hoc HTML form controls for the same job.
4. **Ban manual CSS for layout/look**
On migrated pages:
- No new `<style scoped>` rules for color, spacing, radius, shadow, typography, or layout that Ant already covers.
- No hardcoded hex / `rgb()` for chrome UI (charts/data palettes may be excepted when not chrome).
- Prefer ConfigProvider theme tokens, component props, and Ant space/grid utilities.
- Inline `style` only for rare one-off values that tokens cannot express; prefer tokens first.
5. **Default Ant control size (`controlHeight: 36`) — no manual `size`**
- Global default comes from theme token `controlHeight: 36` in `antdTheme.ts` (Ant derives SM/LG from this).
- Do **not** set `size="small"`, `size="middle"`, `size="large"`, or `:size` on `a-button`, `a-input`, `a-select`, `a-input-number`, `a-date-picker`, `a-radio-group`, `a-pagination`, or similar controls on migrated pages.
- Do **not** override `controlHeight` / `controlHeightSM` / `controlHeightLG` inside `components.*` of the theme — inherit the token default.
- Exception: only if the user explicitly requests a one-off non-default size for a named control.
6. **24-column grid only**
Page layout on migrated screens uses Ant Grid: `a-row` / `a-col` with the **24-column** system (`:span`, `:xs`, `:sm`, `:md`, `:lg`, `:xl`). Do not invent parallel flex/grid CSS layouts for page chrome.
7. **RTL + locale**
App root is wrapped with `<a-config-provider direction="rtl" :locale="faIR" :theme="antdTheme">`. Do not override direction to LTR on whole pages; use local `direction="ltr"` only for inherently LTR fragments (e.g. phone numbers, codes).
8. **Auto-import**
`<a-*>` components are resolved via `unplugin-vue-components` + `AntDesignVueResolver({ importStyle: false })`. Do not add blanket `app.use(antd)` unless there is a documented exception. Do not per-component CSS imports (`importStyle` stays `false`; CSS-in-JS + theme).
9. **Theme is central**
Visual tokens (primary, radius, `controlHeight`, padding/margin scale, shadows, Form/Card overrides) live only in `antdTheme.ts`. Do not fork theme objects inside views.
10. **Icons**
Use `@ant-design/icons-vue` on migrated pages. Do not expand `UiIcon` for Ant pages.
11. **Isolation guarantee**
Infrastructure PRs (deps, vite plugin, theme, App `ConfigProvider`) must not change behavior of unmigrated pages. No `ant-design-vue/dist/reset.css` (or similar global reset) unless explicitly approved — it can break legacy CSS.
## Allowed on migrated pages
- Ant Design Vue 4 components and their props/slots (except banned manual `size` on controls)
- `a-row` / `a-col` (24 columns), `a-space`, `a-flex` where appropriate
- Theme tokens via ConfigProvider
- Minimal page-local script logic (data fetching, forms) unchanged in spirit
## Forbidden on migrated pages
- New legacy `components/ui` usage
- Parallel button/input CSS classes (`.btn`, `.field`, `.crm-*` for chrome that Ant replaces)
- Custom CSS that re-skins Ant components
- Manual `size` props on form/action controls (use theme default 36px)
- Multi-page refactors “while we’re here”
- Adding Tailwind/Bootstrap/other UI kits
## Agent checklist before finishing a migration PR
- [ ] Only one page/route touched for UI migration
- [ ] Imported child components with controls also converted to Ant (no raw HTML left)
- [ ] Grep verification: `<input|<select|<textarea|<button|UiInput|UiSelect|UiField|UiTextarea|UiButton` count is **0** in the View + those children
- [ ] No changes under `components/ui/`
- [ ] No new scoped CSS for look-and-feel
- [ ] No manual `size` on Ant controls
- [ ] Layout uses 24-col Ant grid
- [ ] Uses `antdTheme` / ConfigProvider / StyleProvider `hash-priority="high"` — no local theme fork
- [ ] `npm run build -w @mento/web` (or `vue-tsc`) passes
@@ -0,0 +1,86 @@
---
name: antd-system-architect
description: >-
Architect and migrate Montact web UI to Ant Design Vue v4 under strict
governance: single-page lock, no manual CSS, no manual size props, default
controlHeight 36, 24-column grid, central antdTheme, isolated from legacy
components/ui. Use when migrating a page to Ant Design, adding Ant components,
editing antdTheme, or reviewing Ant PRs.
---
# Ant Design Vue System Architect (Montact)
## When to use
- Migrating any `apps/web` view/page to Ant Design Vue 4
- Extending [`apps/web/src/theme/antdTheme.ts`](../../../apps/web/src/theme/antdTheme.ts)
- Touching Ant ConfigProvider / vite Ant auto-import
- Reviewing whether a change violates isolation from legacy UI
Also read and obey [`.cursor/rules/antd-strict-governance.mdc`](../../rules/antd-strict-governance.mdc).
## Non-negotiables
1. **One page per migration** — never batch unrelated views.
2. **Do not edit** `apps/web/src/components/ui/**` during Ant migration work.
3. **Child components of the View are in scope (mandatory)** — هنگام بازنویسی یک صفحه (View)، ایجنت موظف است علاوه بر خود فایل View، تمام کامپوننت‌های فرزندی را که در آن View ایمپورت شده‌اند و حاوی اینپوت/سلکت/دکمه هستند نیز بازبینی کرده و درون آن‌ها را به کامپوننت‌های رسمی `ant-design-vue` تبدیل کند. هیچ تگ `<input>`, `<select>`, `<textarea>`, `<button>` خامی نباید در صفحه یا کامپوننت‌های فرزندِ مصرفی آن باقی بماند. When rewriting a View, also open every imported child that renders inputs/selects/buttons and convert them to official `ant-design-vue` controls. No raw `<input>`, `<select>`, `<textarea>`, or `<button>` (and no `UiInput` / `UiSelect` / `UiButton` / `UiField` / `UiTextarea`) may remain in the View or those children. Prefer Ant natives (`a-select` multiple/tags, `a-time-picker`, …); for Jalali/domain pickers, convert the child file to Ant chrome (`a-input` + `a-popover`, etc.).
4. **No manual look CSS** on migrated pages (no new scoped colors/spacing/radius/shadows for chrome).
5. **24-column Ant grid** (`a-row` / `a-col`) for page layout.
6. **Theme only in** `apps/web/src/theme/antdTheme.ts`.
7. **Default control height is 36** via token `controlHeight: 36`. Do **not** pass `size` / `:size` on buttons, inputs, selects, or similar controls; do **not** set per-component `controlHeight*` in the theme `components` map.
8. **Auto-import** `<a-*>` via `AntDesignVueResolver({ importStyle: false })` — no per-file style imports. Root uses `StyleProvider` with `hash-priority="high"` so legacy `main.css` cannot override Ant.
9. **No global Ant reset CSS** that would restyle legacy pages.
## Stack facts (this repo)
| Piece | Location |
|---|---|
| Dependency | `ant-design-vue` ^4, `@ant-design/icons-vue`, `unplugin-vue-components` in `@mento/web` |
| Vite plugin | `apps/web/vite.config.ts` — `Components` + `AntDesignVueResolver` |
| Theme | `apps/web/src/theme/antdTheme.ts` (`controlHeight: 36`) |
| RTL + locale + theme | `apps/web/src/App.vue` — `<a-config-provider direction="rtl" :locale="faIR" :theme="antdTheme">` |
| Legacy kit (do not touch) | `apps/web/src/components/ui/*`, `styles/main.css` utilities |
## Migration workflow
1. Confirm the **single** route/view the user named.
2. Inventory current UI: legacy `Ui*`, `.btn`/`.field`, `.crm-*`, custom tables.
3. List all imported children that contain controls; convert them in the same pass (except `components/ui/**`).
4. Replace with Ant equivalents:
- Button → `a-button` (**no** `size` prop)
- Input/Select/Form field → `a-form` + `a-form-item` + `a-input` / `a-select` / … (**no** `size`)
- Modal/Drawer → `a-modal` / `a-drawer`
- Table → `a-table` (columns via Ant API)
- Page chrome → `a-card`, title + `a-space`, layout via `a-row`/`a-col`
- Icons → `@ant-design/icons-vue`
5. Remove page-local CSS that only existed to skin the old controls.
6. Keep business logic, API calls, and router behavior intact.
7. Grep-verify zero hits for `<input|<select|<textarea|<button|UiInput|UiSelect|UiField|UiTextarea|UiButton` in the View + converted children.
8. Run `npm run build -w @mento/web` (or at least `vue-tsc -b` in `apps/web`).
9. Smoke-check that **other** routes still render with legacy UI unchanged.
## Theme extension rules
- Change tokens in `antdTheme.ts` only when the user asks or when a migrated page proves a missing token.
- Keep `controlHeight: 36` as the single source for control sizing; let Ant derive SM/LG.
- Prefer `token` and limited `components.Card` / `components.Form` overrides — never per-control `controlHeight` forks.
- Do not duplicate `ThemeConfig` objects inside views.
## Anti-patterns (reject)
- Importing both `UiButton` and `a-button` on the same migrated page
- Adding `ant-design-vue/dist/reset.css` “to fix spacing”
- Editing `components/ui` to “look more like Ant”
- `size="small|middle|large"` on Ant controls
- Multi-page visual refactors in one PR
- Custom CSS overriding `.ant-btn` / `.ant-input` selectors
## Done criteria
- Migrated page uses Ant components + 24-col grid
- Imported control-bearing children also Ant-pure (grep = 0)
- Controls use theme default height (no manual `size`)
- No new manual chrome CSS
- Legacy kit untouched
- Build/typecheck clean
- Unmigrated pages untouched in the diff