elcrm 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.js +2477 -922
- package/dist/vite/discover-lib.js +4 -4
- package/dist/vite/plugin-css-scoped.js +2 -2
- package/package.json +2 -2
- package/templates/elcrm-cursor/AGENTS.md +13 -0
- package/templates/elcrm-cursor/rules/elcrm-lib.mdc +101 -0
- package/templates/elcrm-cursor/rules/elcrm-module.mdc +25 -0
- package/templates/elcrm-cursor/rules/elcrm-packages.mdc +43 -0
- package/templates/elcrm-cursor/rules/elcrm-rpc.mdc +23 -0
- package/templates/elcrm-cursor/rules/elcrm-server.mdc +30 -0
- package/templates/elcrm-cursor/rules/elcrm-theme.mdc +13 -0
- package/templates/elcrm-cursor/rules/elcrm-ui.mdc +41 -0
- package/templates/elcrm-cursor/rules/elcrm.mdc +43 -0
- package/templates/elcrm-docs/ALERT.elCRM.md +26 -0
- package/templates/elcrm-docs/API.elCRM.md +38 -0
- package/templates/elcrm-docs/BUTTON.elCRM.md +36 -0
- package/templates/elcrm-docs/CLI.elCRM.md +27 -0
- package/templates/elcrm-docs/COMPONENTS.elCRM.md +68 -0
- package/templates/elcrm-docs/FORM.elCRM.md +39 -0
- package/templates/elcrm-docs/ICONS.elCRM.md +40 -0
- package/templates/elcrm-docs/MODAL.elCRM.md +41 -0
- package/templates/elcrm-docs/NOTICE.elCRM.md +31 -0
- package/templates/elcrm-docs/NOTIFICATIONS.elCRM.md +39 -0
- package/templates/elcrm-docs/PASSKEY.elCRM.md +26 -0
- package/templates/elcrm-docs/ROUTER.elCRM.md +34 -0
- package/templates/elcrm-docs/SERVER.elCRM.md +53 -0
- package/templates/elcrm-docs/SOCKET.elCRM.md +34 -0
- package/templates/elcrm-docs/TOOLTIP.elCRM.md +25 -0
- package/templates/panel/web/src/style/theme-dark.css +39 -3
- package/templates/panel/web/src/style/theme-light.css +39 -3
- package/templates/panel/web/src/style/theme.css +51 -3
|
@@ -11,8 +11,8 @@ function assignLetters(names, overrides) {
|
|
|
11
11
|
const used = new Set;
|
|
12
12
|
const letters = {};
|
|
13
13
|
for (const [name, letter] of Object.entries(overrides)) {
|
|
14
|
-
if (!/^[a-
|
|
15
|
-
throw new Error(`[discover-lib] override "${name}" → "${letter}" (нужна a-
|
|
14
|
+
if (!/^[a-z0-9]$/.test(letter)) {
|
|
15
|
+
throw new Error(`[discover-lib] override "${name}" → "${letter}" (нужна a-z0-9)`);
|
|
16
16
|
}
|
|
17
17
|
if (used.has(letter)) {
|
|
18
18
|
throw new Error(`[discover-lib] дубль буквы "${letter}" в overrides`);
|
|
@@ -25,9 +25,9 @@ function assignLetters(names, overrides) {
|
|
|
25
25
|
if (letters[name])
|
|
26
26
|
continue;
|
|
27
27
|
const base = name.replace(/[^a-zA-Z]/g, "").toLowerCase();
|
|
28
|
-
let letter = [...base].find((c) => /[a-z]/.test(c) && !used.has(c)) ?? [..."
|
|
28
|
+
let letter = [...base].find((c) => /[a-z]/.test(c) && !used.has(c)) ?? [..."abcdefghijklmnopqrstuvwxyz0123456789"].find((c) => !used.has(c));
|
|
29
29
|
if (!letter) {
|
|
30
|
-
throw new Error(`[discover-lib] нет свободной
|
|
30
|
+
throw new Error(`[discover-lib] нет свободной буквы/цифры для "${name}"`);
|
|
31
31
|
}
|
|
32
32
|
used.add(letter);
|
|
33
33
|
letters[name] = letter;
|
|
@@ -46,8 +46,8 @@ function createCssScoped(options) {
|
|
|
46
46
|
if (!letter) {
|
|
47
47
|
throw new Error(`[css-scoped] нет буквы для компонента "${component}". Добавьте в components.`);
|
|
48
48
|
}
|
|
49
|
-
if (!/^[a-
|
|
50
|
-
throw new Error(`[css-scoped]
|
|
49
|
+
if (!/^[a-z0-9]$/.test(letter)) {
|
|
50
|
+
throw new Error(`[css-scoped] ключ компонента "${component}" должен быть a-z0-9: "${letter}"`);
|
|
51
51
|
}
|
|
52
52
|
const scoped = `${prefix}${letter}${name}`;
|
|
53
53
|
const owner = `${component}.${name}`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elcrm",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "CLI @elcrm/*: update, create, migrate front, audit, doctor, test, build, css",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "MaSkal <dev@elcrm.online>",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# elCRM
|
|
2
|
+
|
|
3
|
+
Стек: React + Vite + Bun, `@elcrm/*`. API: `@elcrm/server` path-RPC, клиент `@elcrm/api`.
|
|
4
|
+
|
|
5
|
+
Шпаргалки: `docs/*.elCRM.md` и `.cursor/docs/*.elCRM.md`. Не выдумывать API пакетов.
|
|
6
|
+
|
|
7
|
+
Корень UI: `Alert.Init` / `Modal.Init` / `Notice.Init` / `Form.Init` + `LoadRoute`. Кнопки `@elcrm/button`, поля `@elcrm/form`, модалки `Modal.Create`.
|
|
8
|
+
|
|
9
|
+
Нельзя: native input/button, react-router, axios, MUI/antd/tailwind, `themes.css`, `size="sm"|"md"`.
|
|
10
|
+
|
|
11
|
+
Перед сдачей: `elcrm test` (в panel — из корня). После смены пакетов: `elcrm update --fix --test`.
|
|
12
|
+
|
|
13
|
+
Обновить эти файлы: `elcrm docs && elcrm cursor`.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Автор @elcrm/* — после src сразу обновить шпаргалки в elCRM.cli
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Репозиторий библиотеки (`elcrm cursor --lib`)
|
|
7
|
+
|
|
8
|
+
Ты в пакете `@elcrm/*`. После изменений в `src/` **сам** доведи этот репо **и** документацию CLI. Не ждать просьбы «обнови docs».
|
|
9
|
+
|
|
10
|
+
Комментарии в коде — на русском. Минимум зависимостей.
|
|
11
|
+
|
|
12
|
+
## Источник `elcrm docs` / `elcrm cursor` (обязательно)
|
|
13
|
+
|
|
14
|
+
Команды берут тексты **не из этого пакета**, а из репозитория CLI. Если его не обновить — приложения получат устаревшие правила.
|
|
15
|
+
|
|
16
|
+
Каталог CLI (этот компьютер):
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
/Users/maskal/Documents/elCRM/@elcrm/elCRM.cli
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Рядом с текущим пакетом то же самое: `../elCRM.cli`
|
|
23
|
+
(например `elCRM.button` → `elCRM.cli`).
|
|
24
|
+
|
|
25
|
+
Править **сразу в том репо**, в тех же файлах, которые копируются в проекты:
|
|
26
|
+
|
|
27
|
+
| Что изменилось в пакете | Файл в CLI |
|
|
28
|
+
| --------------------------------- | ------------------------------------------------------------- |
|
|
29
|
+
| Как пользоваться / можно / нельзя | `templates/elcrm-docs/<ПАКЕТ>.elCRM.md` |
|
|
30
|
+
| Новый пакет в каталоге | `src/lib/elcrmDocsCatalog.ts` + строка в `elcrm-packages.mdc` |
|
|
31
|
+
| Общие запреты UI | `templates/elcrm-cursor/rules/elcrm-ui.mdc`, `elcrm.mdc` |
|
|
32
|
+
| Сервер / легаси | `elcrm-server.mdc`, `src/lib/test/serverRules.ts` |
|
|
33
|
+
| Breaking rename | `src/lib/migrate/*` + audit / `elcrm test` |
|
|
34
|
+
| Новые токены CSS | шпаргалка + чтобы `elcrm css` их подхватывал |
|
|
35
|
+
|
|
36
|
+
Карта пакета → файл:
|
|
37
|
+
|
|
38
|
+
| `@elcrm/…` | `templates/elcrm-docs/` |
|
|
39
|
+
| ------------- | ------------------------ |
|
|
40
|
+
| api | `API.elCRM.md` |
|
|
41
|
+
| alert | `ALERT.elCRM.md` |
|
|
42
|
+
| button | `BUTTON.elCRM.md` |
|
|
43
|
+
| modal | `MODAL.elCRM.md` |
|
|
44
|
+
| notice | `NOTICE.elCRM.md` |
|
|
45
|
+
| notifications | `NOTIFICATIONS.elCRM.md` |
|
|
46
|
+
| passkey | `PASSKEY.elCRM.md` |
|
|
47
|
+
| router | `ROUTER.elCRM.md` |
|
|
48
|
+
| socket | `SOCKET.elCRM.md` |
|
|
49
|
+
| tooltip | `TOOLTIP.elCRM.md` |
|
|
50
|
+
| icons | `ICONS.elCRM.md` |
|
|
51
|
+
| form | `FORM.elCRM.md` |
|
|
52
|
+
| components | `COMPONENTS.elCRM.md` |
|
|
53
|
+
| server | `SERVER.elCRM.md` |
|
|
54
|
+
| CLI (`elcrm`) | `CLI.elCRM.md` |
|
|
55
|
+
|
|
56
|
+
После правок CLI достаточно, чтобы в приложениях снова вызвали `elcrm docs` и `elcrm cursor` — они перезапишут `docs/` и `.cursor/`.
|
|
57
|
+
|
|
58
|
+
## Чеклист по типу правки в `src/`
|
|
59
|
+
|
|
60
|
+
### 1. Публичный API (компонент, хук, функция, тип, props)
|
|
61
|
+
|
|
62
|
+
- [ ] Экспорт в `index` и `package.json` `exports`
|
|
63
|
+
- [ ] `README.md` этого пакета
|
|
64
|
+
- [ ] **Шпаргалка в elCRM.cli** (таблица выше) — те же можно/нельзя, что в README
|
|
65
|
+
- [ ] Тест на контракт
|
|
66
|
+
- [ ] Breaking → migrate + test в CLI
|
|
67
|
+
|
|
68
|
+
### 2. CSS / токены `--*`
|
|
69
|
+
|
|
70
|
+
- [ ] Rename токена = migrate в CLI
|
|
71
|
+
- [ ] Нет `themes.css`
|
|
72
|
+
- [ ] `check:css` / `check:tokens` если есть
|
|
73
|
+
- [ ] README + шпаргалка в CLI, если токен для приложений
|
|
74
|
+
|
|
75
|
+
### 3. Баг без смены API
|
|
76
|
+
|
|
77
|
+
- [ ] Тест, patch-версия; CLI не трогать, если шпаргалка ещё верна
|
|
78
|
+
|
|
79
|
+
### 4. Init / синглтоны
|
|
80
|
+
|
|
81
|
+
- [ ] «Один раз в App» в README и в шпаргалке CLI
|
|
82
|
+
|
|
83
|
+
### 5. `@elcrm/server`
|
|
84
|
+
|
|
85
|
+
- [ ] README/MPC + `serverRules.ts` + `SERVER.elCRM.md`
|
|
86
|
+
|
|
87
|
+
## Команды в этом репо (до коммита)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bun run test
|
|
91
|
+
bun run check:css # если есть
|
|
92
|
+
bun run check:tokens
|
|
93
|
+
bun run build
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Версия: patch / minor / major. Публикация — push `main` (elGIT Actions).
|
|
97
|
+
|
|
98
|
+
## Нельзя забыть
|
|
99
|
+
|
|
100
|
+
- Не писать шпаргалки только в `docs/` **приложения** — они сотрутся следующим `elcrm docs`
|
|
101
|
+
- `sideEffects` CSS, `files`: dist, peer react ≥ 18, size `s`/`m`/`l`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Новый модуль panel — копировать Settings/List, свой ключ роутера
|
|
3
|
+
globs: "**/modules/**/*.{tsx,ts}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Новый модуль
|
|
8
|
+
|
|
9
|
+
Не изобретать страницу с нуля. Скопировать соседний модуль (`Settings` или `List`).
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
src/modules/<Name>/
|
|
13
|
+
index.tsx export default
|
|
14
|
+
router.sections.ts ключи + default
|
|
15
|
+
sections/<Key>.tsx export default
|
|
16
|
+
modal/<Name>.tsx опционально, Modal.Create
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- Вкладки/сайдбар: `createLazyResolver((key) => import(\`./sections/${key}.tsx\`))`.
|
|
20
|
+
- Свой ключ в `Router`: `section` / `listTab` / `accountTab` — **не один `tab` на все модули**.
|
|
21
|
+
- Активный ключ: `pickRouteKey(Router.useRouter("…"), KEYS, DEFAULT)`.
|
|
22
|
+
- Оболочка: `Section` / `NavSections` / `TabSections`. Поля — `@elcrm/form`, кнопки — `@elcrm/button`.
|
|
23
|
+
- Подключить модуль в меню/`LoadRoute` и в `RouterDataMap`, если он есть.
|
|
24
|
+
|
|
25
|
+
Модалка: `modules/<Name>/modal/<File>.tsx`, не `Modal.Add` в кнопке.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Каталог пакетов @elcrm/* — всегда смотри docs/*.elCRM.md перед кодом
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Пакеты elCRM — возможности
|
|
7
|
+
|
|
8
|
+
**Сначала открой шпаргалку**, не выдумывай API:
|
|
9
|
+
|
|
10
|
+
- в проекте: `docs/*.elCRM.md`
|
|
11
|
+
- копия для агента: `.cursor/docs/*.elCRM.md`
|
|
12
|
+
- обновить оба: `elcrm docs && elcrm cursor`
|
|
13
|
+
|
|
14
|
+
| Файл | Пакет | Делать так |
|
|
15
|
+
| ------------------------ | ---------------------- | ------------------------------------------------------- |
|
|
16
|
+
| `CLI.elCRM.md` | `elcrm` | `update --fix --test`, migrate, test, css, docs, cursor |
|
|
17
|
+
| `API.elCRM.md` | `@elcrm/api` | `Api.create` + `Api.query("router/method", body)` |
|
|
18
|
+
| `ALERT.elCRM.md` | `@elcrm/alert` | `<Alert.Init />`, `Alert.Send` / `Confirm` |
|
|
19
|
+
| `BUTTON.elCRM.md` | `@elcrm/button` | `<Button onSend>`, size s/m/l, не `<button>` |
|
|
20
|
+
| `MODAL.elCRM.md` | `@elcrm/modal` | `<Modal.Init />`, **`Modal.Create` + Open**, не Add |
|
|
21
|
+
| `NOTICE.elCRM.md` | `@elcrm/notice` | `<Notice.Init />`, Success/Error/Remark/Undo |
|
|
22
|
+
| `NOTIFICATIONS.elCRM.md` | `@elcrm/notifications` | Init + Bell + worker?url; сервер `/notifications` |
|
|
23
|
+
| `PASSKEY.elCRM.md` | `@elcrm/passkey` | **только** `createPasskeyClient` + `/react` хуки |
|
|
24
|
+
| `ROUTER.elCRM.md` | `@elcrm/router` | `Router.data` + LoadRoute, не react-router |
|
|
25
|
+
| `SOCKET.elCRM.md` | `@elcrm/socket` | клиент SharedWorker; сервер `createSocket` |
|
|
26
|
+
| `TOOLTIP.elCRM.md` | `@elcrm/tooltip` | `<Tooltip content>`, не `title` |
|
|
27
|
+
| `ICONS.elCRM.md` | `@elcrm/icons` | `Icons.Line`, vite `icons()`, `icons update` |
|
|
28
|
+
| `FORM.elCRM.md` | `@elcrm/form` | поля + useForm, не native input |
|
|
29
|
+
| `COMPONENTS.elCRM.md` | `@elcrm/components` | Layout/Header/Section/NavSections |
|
|
30
|
+
| `SERVER.elCRM.md` | `@elcrm/server` | createServer + createRouter, не REST, не node:fs |
|
|
31
|
+
|
|
32
|
+
## Корень приложения (panel)
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<Alert.Init />
|
|
36
|
+
<Modal.Init />
|
|
37
|
+
<Notice.Init position="top-right" />
|
|
38
|
+
<Notifications.Init … /> // если push
|
|
39
|
+
<Form.Init onNotice={(m) => Notice.Remark(String(m))} />
|
|
40
|
+
<LoadRoute variant="page" … />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
HTTP: `@elcrm/api`. WS: `@elcrm/socket` после логина (тот же ssid). Кнопки: `@elcrm/button`. Подсказки: `@elcrm/tooltip`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Новый path-RPC — createRouter + Api.query, без REST и jwt.init
|
|
3
|
+
globs: "**/api/**/*.{ts,js}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Новый метод API
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
createRouter("orders").use("list", async ({ user, body }) => {
|
|
11
|
+
if (!user?.id) return rpcLogout();
|
|
12
|
+
return rpcOk({ items: [] });
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Клиент (web): `Api.query("orders/list", { … })` — тот же prefix, что `createServer({ prefix })`.
|
|
17
|
+
|
|
18
|
+
- Регистрация в `routes/index.ts` (или рядом с остальными `createRouter`).
|
|
19
|
+
- `user` уже из JWT сервера — не вызывать `jwt.init` в handler.
|
|
20
|
+
- SQLite: `@elcrm/server/db`, без `node:fs` / `node:path`.
|
|
21
|
+
- Ошибка клиенту: `rpcErr` / throw → непрозрачный internal; детали в `console.error`.
|
|
22
|
+
|
|
23
|
+
Нельзя: `fetch`, `path === "/api/"`, `handle*(ctx): Promise<Response>`, `nodemailer`, `web-push`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Сервер elCRM — createServer, роутеры, запреты
|
|
3
|
+
globs: "**/api/**/*.{ts,js}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# `@elcrm/server`
|
|
8
|
+
|
|
9
|
+
Подробности: `docs/SERVER.elCRM.md`.
|
|
10
|
+
|
|
11
|
+
## Как писать
|
|
12
|
+
|
|
13
|
+
- Точка входа: `createServer({ port, prefix, jwt, cron, cors })`.
|
|
14
|
+
- Методы: `createRouter("name").use("method", handler)` + `rpcOk` / `rpcErr` / `rpcLogout`.
|
|
15
|
+
- `user` уже из JWT createServer — не вызывать `jwt.init` в каждом handler.
|
|
16
|
+
- БД: `import { db, initDB } from "@elcrm/server/db"` — каталог SQLite создаётся сам.
|
|
17
|
+
- Cron: `import { cron } from "@elcrm/server/cron"; cron.use(fn, interval?, name?)`.
|
|
18
|
+
- WS: `createSocket("ws").use("path", handler)` **до** `createServer`; слать `sendSocket`.
|
|
19
|
+
|
|
20
|
+
## Запрещено
|
|
21
|
+
|
|
22
|
+
- `from "@elcrm/db"` / `jwt` / `cron` / `tb` — только `@elcrm/server/…`
|
|
23
|
+
- `from "node:fs"` / `from "node:path"` для типовых задач (mkdir под sqlite, join путей)
|
|
24
|
+
- `createServerFromEnv`, `createWsHub`, `serverConnect`, `Cron.useJob`
|
|
25
|
+
- REST `if (path === "/api/…")`, `export async function handle*(ctx): Promise<Response>`
|
|
26
|
+
- `nodemailer`, `web-push`
|
|
27
|
+
- `CORS_ORIGIN` (singular) — только `CORS_ORIGINS` без path в значении
|
|
28
|
+
- Отдавать клиенту stack / `err.message`
|
|
29
|
+
|
|
30
|
+
Автопроверка: `elcrm test --server`. Правки легаси: `bunx elcrm-server update`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CSS-токены — геометрия в theme.css, цвет в light и dark
|
|
3
|
+
globs: "**/theme*.css,**/elcrm.css"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Тема
|
|
8
|
+
|
|
9
|
+
- Размеры/отступы/радиусы → `theme.css` (`:root`).
|
|
10
|
+
- Цвет → **оба** файла: `theme-light.css` и `theme-dark.css`.
|
|
11
|
+
- Не класть `--*` в `modules/**/style.css`, если это цвет/размер оболочки.
|
|
12
|
+
- Не `import "@elcrm/*/themes.css"`. Не shorthand `1px solid` в `--field-border-hover`.
|
|
13
|
+
- Недостающие дефолты: `elcrm css` (уже заданные не затирает).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: UI elCRM — компоненты, форма, структура модулей
|
|
3
|
+
globs: "**/*.{tsx,jsx,css}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# UI `@elcrm/*`
|
|
8
|
+
|
|
9
|
+
Шпаргалки: `docs/*.elCRM.md`, `.cursor/docs/*.elCRM.md`.
|
|
10
|
+
|
|
11
|
+
## Корень App
|
|
12
|
+
|
|
13
|
+
`Alert.Init`, `Modal.Init`, `Notice.Init`, `Form.Init`, при необходимости `Notifications.Init`, затем `LoadRoute`.
|
|
14
|
+
|
|
15
|
+
## Структура (panel / app)
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
src/App.tsx — *.Init + LoadRoute
|
|
19
|
+
src/pages/ — экраны (Auth, Main)
|
|
20
|
+
src/modules/Name/
|
|
21
|
+
index.tsx
|
|
22
|
+
router.sections.ts
|
|
23
|
+
sections/Key.tsx — export default
|
|
24
|
+
modal/Name.tsx — Modal.Create
|
|
25
|
+
src/style/theme*.css + elcrm.css
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- Кнопки: `@elcrm/button` (`onSend`). Иконки: `Icons.Line`. Подсказки: `@elcrm/tooltip`.
|
|
29
|
+
- HTTP: `Api.query("auth/pass", body)`. Роутер: `Router.setRouter` / `mergeRouter`, разные ключи вкладок.
|
|
30
|
+
- `NavSections` / `TabSections`: `createLazyResolver((key) => import(\`./sections/${key}.tsx\`))`.
|
|
31
|
+
|
|
32
|
+
## Формы и модалки
|
|
33
|
+
|
|
34
|
+
- `useForm` + `name`/`form`. Канон полей — `docs/FORM.elCRM.md`.
|
|
35
|
+
- `Modal.Create` + Open, не `Modal.Add`.
|
|
36
|
+
- Passkey: `createPasskeyClient` + `usePasskeySupport`.
|
|
37
|
+
|
|
38
|
+
## CSS / запреты
|
|
39
|
+
|
|
40
|
+
- Токены в theme, не `themes.css`, не `size="sm"|"md"`, не native input/select/textarea.
|
|
41
|
+
- Border hover/focus полей — только цвет.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Базовые правила elCRM для агента Cursor
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# elCRM — как работать в этом репозитории
|
|
7
|
+
|
|
8
|
+
**Всегда** сверяйся со шпаргалками: `docs/*.elCRM.md` и `.cursor/docs/*.elCRM.md` (каталог пакетов — правило `elcrm-packages`). Не выдумывай методы. Обновить: `elcrm docs && elcrm cursor`.
|
|
9
|
+
|
|
10
|
+
Комментарии и документация — **на русском**. Минимум зависимостей. Компоненты разделяй по обязанностям.
|
|
11
|
+
|
|
12
|
+
## Стек
|
|
13
|
+
|
|
14
|
+
- Фронт: React + Vite + Bun, пакеты `@elcrm/*`.
|
|
15
|
+
- API: Bun + `@elcrm/server` (path-RPC, не REST `/api/...`).
|
|
16
|
+
- Клиент HTTP `@elcrm/api`, WS `@elcrm/socket` (ssid общий).
|
|
17
|
+
- Стили: `theme.css` + `theme-light` / `theme-dark`. Не импортировать `@elcrm/*/themes.css`.
|
|
18
|
+
- Размеры UI: `"s"` | `"m"` | `"l"`.
|
|
19
|
+
|
|
20
|
+
## После смены `@elcrm/*` в приложении
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
elcrm update --fix --test
|
|
24
|
+
elcrm css
|
|
25
|
+
elcrm docs
|
|
26
|
+
elcrm cursor
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Автор `@elcrm/*`: после правок API **сразу** обновить шпаргалки в репо CLI
|
|
30
|
+
(`/Users/maskal/Documents/elCRM/@elcrm/elCRM.cli` или соседний `../elCRM.cli`),
|
|
31
|
+
иначе `elcrm docs` / `elcrm cursor` устареют. Чеклист: `elcrm cursor --lib`.
|
|
32
|
+
|
|
33
|
+
## Нельзя
|
|
34
|
+
|
|
35
|
+
- Нативные `<input>`, `<textarea>`, `<select>` — `@elcrm/form`.
|
|
36
|
+
- Нативный `<button>` в UI — `@elcrm/button`.
|
|
37
|
+
- `Modal.Add` — `Modal.Create`.
|
|
38
|
+
- `createPasskey` — `createPasskeyClient`.
|
|
39
|
+
- Серверные `@elcrm/db|jwt|cron|tb` на фронте.
|
|
40
|
+
- Секреты / `.env` в чат.
|
|
41
|
+
- Не ставить MUI, antd, tailwind, react-router, axios — только `@elcrm/*` из `package.json`.
|
|
42
|
+
|
|
43
|
+
**Сдача:** `elcrm test` (panel — из корня). Нет — задача не закончена.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Пакет: @elcrm/alert. -->
|
|
2
|
+
|
|
3
|
+
# `@elcrm/alert`
|
|
4
|
+
|
|
5
|
+
iOS-алерты, Promise API. Обязательно `<Alert.Init />` в корне (`App.tsx`).
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Alert } from "@elcrm/alert";
|
|
9
|
+
|
|
10
|
+
await Alert.Send({ title: "Заголовок", text: "Текст", icon: "success" });
|
|
11
|
+
const ok = await Alert.Confirm({ title: "Удалить?", icon: "warning" });
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Можно
|
|
15
|
+
|
|
16
|
+
- `Alert.Send(content)` → `Promise<{ confirmed, action? }>`. С callback — void (легаси).
|
|
17
|
+
- `icon`: `success` \| `error` \| `warning` \| `info`.
|
|
18
|
+
- `button` или `buttons[]`: `{ label, color?: "blue"|"red"|"default", action, onClick }`.
|
|
19
|
+
- `closeOnOverlay` / `closeOnEscape` (по умолчанию true).
|
|
20
|
+
- Токены `--alert-*` в теме приложения (`elcrm css`).
|
|
21
|
+
|
|
22
|
+
## Нельзя
|
|
23
|
+
|
|
24
|
+
- `window.alert` / `window.confirm`.
|
|
25
|
+
- Несколько `Alert.Init`.
|
|
26
|
+
- Нативные `<button>` в UI приложения, если это действие формы — `@elcrm/button`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Пакет: @elcrm/api. -->
|
|
2
|
+
|
|
3
|
+
# `@elcrm/api`
|
|
4
|
+
|
|
5
|
+
HTTP-клиент path-RPC к `@elcrm/server`. JSON `{ status, data }` → `query` возвращает `data` или throw.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Api } from "@elcrm/api";
|
|
9
|
+
|
|
10
|
+
Api.create({
|
|
11
|
+
url: "https://api.example.com/v1/",
|
|
12
|
+
method: "POST",
|
|
13
|
+
timeout: 15000,
|
|
14
|
+
storageKey: "elcrm-token", // ssid
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const users = await Api.query<User[]>("users/list", { page: 1 });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Можно
|
|
21
|
+
|
|
22
|
+
- `Api.create` — клиент по умолчанию; `Api.client({ url })` — второй origin (тот же `storageKey` = общий ssid).
|
|
23
|
+
- `resolveUrl` / `urlKey` + `urlPath` — tenant URL из storage.
|
|
24
|
+
- Тело: `json` (default) · `formdata` · `crypto` (`encryptQuery`) · `binary`.
|
|
25
|
+
- `responseAs`: `json` \| `text` \| `blob` \| `arraybuffer`.
|
|
26
|
+
- React: `const [status, data, update] = Api.useQuery(link, body)` — мемоизируйте `body`.
|
|
27
|
+
- `Api.getSSID()`, `Api.url()`, `Api.image("/x")`, `Api.Logout()`.
|
|
28
|
+
|
|
29
|
+
Контракт `status`: `success` → data; `error` → throw; `logout` → `Api.Logout()`; `refresh` → новый ssid и **один** повтор.
|
|
30
|
+
|
|
31
|
+
Side-fields рядом с `status`/`data`: `ssid` → storageKey, `url` → urlKey, `port` → portKey (снимаются до unwrap).
|
|
32
|
+
|
|
33
|
+
## Нельзя
|
|
34
|
+
|
|
35
|
+
- REST `fetch("/api/users")` вместо `Api.query("router/method", body)`.
|
|
36
|
+
- Путать URL: base уже с prefix (`/v1/` или `/api/`), путь RPC без лишнего `/api`.
|
|
37
|
+
- Игнорировать logout/refresh — сессия сломается.
|
|
38
|
+
- `createPasskey` из `@elcrm/passkey` — это другой пакет; HTTP passkey тоже через клиент/сервер passkey.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Пакет: @elcrm/button. -->
|
|
2
|
+
|
|
3
|
+
# `@elcrm/button`
|
|
4
|
+
|
|
5
|
+
Кнопка с вариантами, размерами и `onSend` (pending). Токены задаёт приложение, fallback в пакете нет.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button } from "@elcrm/button";
|
|
9
|
+
|
|
10
|
+
<Button variant="primary" size="m" width="m" label="Отправить" onClick={() => {}} />
|
|
11
|
+
|
|
12
|
+
<Button
|
|
13
|
+
label="Сохранить"
|
|
14
|
+
onSend={(done) => {
|
|
15
|
+
void save().finally(() => done(false));
|
|
16
|
+
}}
|
|
17
|
+
pendingContent="Сохранение…"
|
|
18
|
+
/>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Можно
|
|
22
|
+
|
|
23
|
+
- `variant`: `primary` · `secondary` · `positive` · `negative` · `ghost`
|
|
24
|
+
- `size`: `s` · `m` · `l` | `width`: `s` (контент) · `m` (min-width) · `l` (на всю ширину)
|
|
25
|
+
- `label` / `before` / `after`; `children` перекрывает слоты
|
|
26
|
+
- `onSend(setDisabled, event)`: вернуть `false` — не входить в pending; иначе снять через `setDisabled(false)`
|
|
27
|
+
- Если задан `onClick` — `onSend` **не** вызывается
|
|
28
|
+
- `buttonType`: `button` \| `submit` \| `reset`
|
|
29
|
+
- Точечно: `@elcrm/button/Button`
|
|
30
|
+
|
|
31
|
+
## Нельзя
|
|
32
|
+
|
|
33
|
+
- Нативный `<button>` в UI приложения (кроме редких слотов, где пакет сам просит children).
|
|
34
|
+
- `size="sm"|"md"`.
|
|
35
|
+
- `import "@elcrm/button/themes.css"` — нет такого файла; токены в theme приложения.
|
|
36
|
+
- Хардкод цветов вместо `--button-*`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Пакет: elcrm (CLI). -->
|
|
2
|
+
|
|
3
|
+
# `elcrm` CLI
|
|
4
|
+
|
|
5
|
+
Глобально: `bun i -g elcrm`. В приложении — после смены `@elcrm/*`:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
elcrm update --fix --test
|
|
9
|
+
elcrm css
|
|
10
|
+
elcrm docs
|
|
11
|
+
elcrm cursor
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Команда | Зачем |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `update [--dry] [--core] [--fix] [--test]` | latest `@elcrm/*`; `--fix` = migrate front + css; `--test` = elcrm test |
|
|
17
|
+
| `create` / `init` | шаблоны `app` \| `lib` \| `panel` |
|
|
18
|
+
| `migrate <name> [--dry]` | `front` (пакет), `form-aliases`, `form-tokens`, `size-sml`, `field-border`, `modal-create`, `legacy-hacks`, `socket-server` |
|
|
19
|
+
| `audit [--fix]` | старые API; `--fix` = migrate front |
|
|
20
|
+
| `doctor [--fix]` | стандарты; сервер --fix → `bunx elcrm-server update` |
|
|
21
|
+
| `test [--front\|--server] [--no-scripts]` | статический анализ + scripts |
|
|
22
|
+
| `css [--dry]` | недостающие токены → theme*.css |
|
|
23
|
+
| `docs` / `cursor` | шпаргалки и правила агента |
|
|
24
|
+
| `cursor --lib` / `-lib` | правила автора `@elcrm/*` lib (чеклист после правок src) |
|
|
25
|
+
| `build` / `postbuild` / `uuid` | деплой клиента/api; склейка CSS lib |
|
|
26
|
+
|
|
27
|
+
Не выдумывать флаги — `elcrm --help` и `COMMANDS.md`.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Источник: @elcrm/components + шаблон panel. Обновление: снова elcrm docs. -->
|
|
2
|
+
|
|
3
|
+
# `@elcrm/components`
|
|
4
|
+
|
|
5
|
+
Shell UI: оболочка страницы, шапка, секции, списки. **Цвета и размеры задаёт приложение** (`theme.css` + `theme-light` / `theme-dark`). Fallback в пакете нет. `elcrm css` дописывает недостающие токены.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
Layout, Header, Footer, Brand, Avatar, Badge,
|
|
10
|
+
Menu, Section, NavSections, TabSections,
|
|
11
|
+
Card, List, Item, Block, Row, Column,
|
|
12
|
+
Loading, EmptyState, createLazyResolver,
|
|
13
|
+
} from "@elcrm/components";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Точечно: `import { Header } from "@elcrm/components/Header"`.
|
|
17
|
+
|
|
18
|
+
## Компоненты
|
|
19
|
+
|
|
20
|
+
| | Назначение | Ключевые props |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| Layout | страница | `header`, `footer`, `children` |
|
|
23
|
+
| Header | слоты шапки | `brand`, `nav`, `search`, `actions` |
|
|
24
|
+
| Footer | подвал | `children` |
|
|
25
|
+
| Brand | логотип | `as`, `onClick` |
|
|
26
|
+
| Avatar | фото / инициалы | `src`, `name`, `size` (`s`/`m`/`l`) |
|
|
27
|
+
| Badge | статус | `tone` |
|
|
28
|
+
| Menu | нав (не overlay) | `items`, `activeKey`, `onSelect` |
|
|
29
|
+
| Section | блок / auth | `title`, `description`, `icon`, `actions`, `variant` |
|
|
30
|
+
| NavSections | сайдбар + lazy | `groups`, `resolveSection` |
|
|
31
|
+
| TabSections | вкладки + lazy | `items`, `resolveSection` |
|
|
32
|
+
| Card / List / Item | карточка, список | Item: `as`, `active` |
|
|
33
|
+
| Block / Row / Column | стеки | `gap` |
|
|
34
|
+
| Loading / EmptyState | плейсхолдеры | |
|
|
35
|
+
| createLazyResolver | кэш `import()` | `(key) => import(…)` |
|
|
36
|
+
|
|
37
|
+
Слоты Header заполняет приложение: поиск `@elcrm/search`, кнопки `@elcrm/button`, иконки `@elcrm/icons`.
|
|
38
|
+
|
|
39
|
+
## Структура приложения (panel)
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
src/
|
|
43
|
+
App.tsx # Init: Alert, Modal, Notice, Form, LoadRoute
|
|
44
|
+
pages/Auth.tsx Main.tsx
|
|
45
|
+
modules/<Name>/
|
|
46
|
+
index.tsx
|
|
47
|
+
router.sections.ts
|
|
48
|
+
sections/<Key>.tsx # export default
|
|
49
|
+
modal/ # опционально
|
|
50
|
+
component/ # локальные обёртки (Header приложения)
|
|
51
|
+
style/theme.css theme-light.css theme-dark.css elcrm.css
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- Секции: `createLazyResolver((key) => import(\`./sections/${key}.tsx\`))`.
|
|
55
|
+
- Ключи роутера не смешивать (`section` vs `listTab` vs `accountTab`) — иначе lazy откроет не ту секцию.
|
|
56
|
+
- Auth-экран: `<Section variant="auth">`, поля из `@elcrm/form`, submit через `@elcrm/button` `onSend`.
|
|
57
|
+
|
|
58
|
+
## Можно
|
|
59
|
+
|
|
60
|
+
Токены только в theme-файлах приложения. Импорт `@elcrm/components/style.css` — опционально (единый файл).
|
|
61
|
+
|
|
62
|
+
## Нельзя
|
|
63
|
+
|
|
64
|
+
- `import "@elcrm/…/themes.css"` — пакеты больше не отдают темы.
|
|
65
|
+
- Свои `<button>` / нативные инпуты вместо `@elcrm/button` / `@elcrm/form` в UI приложения.
|
|
66
|
+
- `size="sm"|"md"` → `"s"|"m"` (`elcrm migrate size-sml`).
|
|
67
|
+
- Дублировать toolbar-токены в модульных CSS — общий класс + theme.
|
|
68
|
+
- Хардкодить цвета в компонентах модулей, если есть токен.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<!-- Сгенерировано `elcrm docs`. Источник: @elcrm/form. Обновление: снова elcrm docs. -->
|
|
2
|
+
|
|
3
|
+
# `@elcrm/form`
|
|
4
|
+
|
|
5
|
+
Поля ввода + `useForm`. Стили вшиты в JS — отдельный css-loader не нужен. Темы: `@elcrm/form/light.css` / `dark.css` (или токены приложения + `elcrm css`).
|
|
6
|
+
|
|
7
|
+
## Можно
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { useForm, StringField, PasswordField, Form } from "@elcrm/form";
|
|
11
|
+
|
|
12
|
+
const form = useForm({ login: "", password: "" });
|
|
13
|
+
|
|
14
|
+
<Form.Init onNotice={(msg) => { /* тост */ }} />
|
|
15
|
+
<StringField name="login" form={form} label="Логин" autoComplete="username" />
|
|
16
|
+
<PasswordField name="password" form={form} label="Пароль" />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`Form.Init` — если нужны notice / модалки для `ModalField` / `OptionsField`.
|
|
20
|
+
|
|
21
|
+
Связь: всегда `name` + `form`. Значения: `getValue` / `getValues` / `setValue` / `setValues` / `reset`.
|
|
22
|
+
Для Date/массивов/объектов — **новая ссылка**, иначе `Object.is` не уведомит. Принудительный ререндер: `setValue`.
|
|
23
|
+
|
|
24
|
+
Канонические поля: `StringField`, `PasswordField`, `TextareaField`, `NumberField`, `PercentField`, `MoneyField`, `MaskField`, `PhoneField`, `EmailField`, `UrlField`, `DateField`, `TimeField`, `SelectField`, `OptionsField`, `ModalField`, `RangeField`, `CheckField`, `RadioField`, `TagsField`, `RatingField`, `CodeField`, `FileField`, `DragDropField`, `RichTextField`, `ColorField`, `HiddenField`, `DisplayField`.
|
|
25
|
+
|
|
26
|
+
`before` / `after` — слоты (иконки). Размеры: `"s"` | `"m"` | `"l"`, не `sm`/`md`.
|
|
27
|
+
|
|
28
|
+
Токены: `--field-padding-block` / `--field-padding-inline`, `--popup-shadow`. Hover/focus/disabled border — **только цвет**, не shorthand `1px solid …`.
|
|
29
|
+
|
|
30
|
+
## Нельзя
|
|
31
|
+
|
|
32
|
+
- Нативные `<input>`, `<textarea>`, `<select>` в приложении — `elcrm test` это **error**. Исключение: пакет `@elcrm/form`.
|
|
33
|
+
- Алиасы: `ProgressField` → `RangeField`, `ReadonlyField` → `DisplayField`, `OtpField` → `CodeField`, `SegmentField` → `TabsField`, `DragonDropField` → `DragDropField`. Чинит `elcrm migrate form-aliases`.
|
|
34
|
+
- Проп `native` на полях — `elcrm test` предупреждает. У `StringField` `native` исторически для autofill; у `PasswordField` `native` значит «не рендерить» — не путать.
|
|
35
|
+
- Legacy-токены: `--field-note-padding*`, `--control-inner-padding`, `--control-side-padding`, `--field-side-padding`, `--date-popup-shadow` → `elcrm migrate form-tokens`.
|
|
36
|
+
- Пересоздавать `options` селекта на каждый рендер — константа модуля.
|
|
37
|
+
- Серверные пакеты `@elcrm/db|jwt|cron|tb` на фронте.
|
|
38
|
+
|
|
39
|
+
Подпути (tree-shake): `@elcrm/form/fields/Date`, `@elcrm/form/hooks/use`.
|