Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions docs/member-cards-search/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<!-- @format -->

# Участники: карточка и поиск

Изменения предназначены для Angular DEV. База: `bdb5cf4780be0daa3e441d6b152511fcbe638d4e`.

## Карточка

`MemberCardComponent` применяется только в каталоге `/office/members`.
Общий `InfoCardComponent`, карточки команды проекта, статистика и фильтры не переработаны.
Используются существующие данные `User.personal` и `User.relations`, полученные через
`MemberRepository` / `userFromRaw`; новых API-полей нет.

Имя и фамилия занимают две строки, специальность и возраст — отдельный блок.
Навыки выровнены слева и занимают максимум две строки. Ширины плашек измеряются
тем же шрифтом в невидимом слое; заранее резервируется место для `+N`.
При изменении данных, ширины или загрузке шрифта измерение обновляется.
Наблюдатель и подписка на шрифт освобождаются при уничтожении компонента.
Длинный навык обрезается многоточием; полный текст и скрытые навыки доступны в
`title`/семантике списка, а все данные — на странице профиля.

Карточка содержит одну ссылку на существующий профиль; CTA «Профиль» является
визуальной частью этой ссылки. Вложенных интерактивных элементов нет.

## Геометрия и визуальная проверка

Проверены настоящие компоненты Angular на локальной изолированной странице с
восемью синтетическими участниками, исходными стилями страницы и обёрткой office.
Данные на DEV/PROD не создавались и не изменялись. Это проверка локальной сборки,
а не скриншоты авторизованной live-сессии.

| Параметр | До | После |
| --------------------------------- | ------------------- | ----------- |
| Ширина карточки | 156 px | 156 px |
| Высота каждой карточки | 180 px | 248 px |
| Аватар | 70 × 70 px | 70 × 70 px |
| Аватар относительно рамки, x / y | 43 / −34 px | 43 / −34 px |
| Desktop, колонок карточек | 4 | 4 |
| 1024 px, колонок карточек | 4 с переполнением | 3 |
| Tablet / mobile, колонок карточек | 1 | 1 |
| Максимум строк навыков | прежнее отображение | 2 |

Высота увеличена одинаково для всех карточек, чтобы вместить читаемые имя,
вторичный текст, навыки и CTA. Ширина, отступы сетки и положение аватара внутри
карточки сохранены. Набор навыков и длина имени не меняют высоту.

Viewport: 1440 × 900, 768 × 1024, 390 × 844, масштаб 100%.
Горизонтального переполнения нет. Статистика имеет прежние 157 × 140,98 px;
на desktop из-за появления вертикальной полосы прокрутки центрируемая область
смещается на 7,5 px влево. Это штатная геометрия office, её CSS не менялся.
На tablet/mobile координаты статистики совпали с исходными.
Подробные измерения: [geometry.json](geometry.json).

Дополнительно на 1024 × 768 выявлено исходное переполнение внутри office:
четыре карточки не помещались рядом с фильтрами. Локальная media-правка
`MembersComponent` оставляет три колонки в диапазоне 1000–1279 px.
Проверены границы 1000/1279/1280/1440 px: переполнения нет, начиная с 1280 px
сохраняются четыре колонки. Общие стили office и статистики не менялись.
Измерения: [breakpoints.json](breakpoints.json).

| Поверхность | До | После |
| ------------------------ | --------------------------------------------------- | -------------------------------------------------- |
| Desktop | [Скриншот](screenshots/before-desktop.png) | [Скриншот](screenshots/after-desktop.png) |
| Tablet | [Скриншот](screenshots/before-tablet.png) | [Скриншот](screenshots/after-tablet.png) |
| Mobile | [Скриншот](screenshots/before-mobile.png) | [Скриншот](screenshots/after-mobile.png) |
| Tablet landscape 1024 px | [Скриншот](screenshots/before-tablet-landscape.png) | [Скриншот](screenshots/after-tablet-landscape.png) |

Проверены отсутствие навыков/специальности, длинная фамилия, 1–8 навыков,
точный счётчик скрытых навыков, переход фокуса Tab и видимая рамка фокуса.
Дополнительная фикстура с длинным навыком без пробелов и 50 навыками
показывает `+48`, сохраняет две строки и размер карточки:
[крайние случаи](screenshots/after-edge-cases.png).

В локальном браузере ошибок нет. Предупреждение NG0912 о двух существующих
`IconComponent` воспроизведено и на исходной, и на новой странице.

## Поиск: что воспроизвелось до изменений

24.09.2026 выполнены ограниченные read-only запросы к публичному endpoint,
который использует Angular: `/auth/public-users/?user_type=1&limit=1&fullname=…`.
Сохранены только коды ответа и количества: [live-search-before.json](live-search-before.json).

| Запрос | DEV, count | PROD, count |
| ---------------------------------- | ---------- | ----------- |
| Иван | 5 | 365 |
| иван | 0 | 10 |
| ИВАН | 0 | 0 |
| Иванов | 2 | 171 |
| иВаНоВ | 0 | 0 |
| Иван Иванов | 5 | 365 |
| иван иванов | 0 | 10 |
| Иван НесуществующаяФамилияПроверка | 5 | 365 |

Все ответы — HTTP 200. Проблема есть **и на DEV, и на PROD**.
Backend `UserFilter.fullname` в проверенных ветках dev/master одинаков:
слова соединялись через OR, поэтому полное имя возвращало совпадения только
по одному слову. Кириллический `icontains` на работающих окружениях фактически
регистрозависим. Точная настройка collation live-БД не запрашивалась.

Отдельная frontend-проблема: `throttleTime(300)` без trailing мог терять
последние символы быстрого ввода. Теперь только поле имени использует debounce,
обрезку крайних пробелов и объединение внутренних пробельных символов.
Resolver учитывает `fullname` из URL. Очистка поиска удаляет query-параметр;
пагинация получает актуальный total, поздняя страница старого поиска игнорируется.
Остальные фильтры сохраняют прежнюю обработку.

## Зависимость от backend и ограничения

Для исправления регистра и полного имени требуется сопутствующий
[backend PR #754](https://github.com/PROCOLLAB-github/api/pull/754).
URL, формат ответа и права доступа сохранены.
Никакой фильтрации всех пользователей в браузере нет.

На DEV/PROD страницы участников перенаправляют в login: безопасной
авторизованной сессии нет. Ввод поиска в live-браузере и live-проверка результата
после исправления не выполнены. Изменения не развёрнуты; доказательство
исправления — локальные Angular-тесты и тесты публичного API на PostgreSQL.

React, статистика, зависимости, workflows и схема БД не изменены.
Merge и deploy не выполнялись.

## Выполненные проверки

- `npm ci` — exit 0, Node 20.20.2.
- `npm run test:ci -- --pool=forks projects/social_platform/src/app/ui/pages/members projects/social_platform/src/app/api/member projects/social_platform/src/app/ui/widgets/info-card projects/social_platform/src/app/infrastructure/adapters/member`
— 9 файлов, **70/70**, exit 0.
- `npm run test:ci -- --pool=forks` — 387 файлов, **1791/1791**, exit 0,
без unhandled errors. Использована существующая конфигурация с CLI-выбором
pool; setup, skip/exclude и зависимости не менялись.
- `npm run lint:ts` — exit 0, 6 предупреждений в незатронутых файлах.
- `npx prettier --check` для всех изменённых TS/HTML/Markdown/JSON — exit 0.
SCSS исключён существующим `.prettierignore` и проверен Stylelint.
- `npx stylelint projects/social_platform/src/app/ui/pages/members/member-card/member-card.component.scss`
и `members.component.scss` — exit 0.
- `npm run build:prod` — exit 0; присутствуют предупреждения сборки о CommonJS.
- `git diff --check` — без ошибок.

Особенность проверки на Windows: существующий `build:sprite` с одинарными
кавычками вокруг glob генерирует пустой sprite. После команды восстановлен
только этот автоматически изменённый файл из HEAD; в diff его нет.
Браузерные скриншоты сняты с development-сборки с исходным валидным sprite.
Это не доказательство корректности иконок в локальном Windows prod-артефакте;
скрипт сборки в рамках задачи не менялся.
26 changes: 26 additions & 0 deletions docs/member-cards-search/breakpoints.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"columns": "169.328px 169.328px 169.328px",
"heights": [248],
"overflow": false,
"viewport": 1000
},
{
"columns": "212.438px 212.438px 212.438px",
"heights": [248],
"overflow": false,
"viewport": 1279
},
{
"columns": "156px 156px 156px 156px",
"heights": [248],
"overflow": false,
"viewport": 1280
},
{
"columns": "156px 156px 156px 156px",
"heights": [248],
"overflow": false,
"viewport": 1440
}
]
1 change: 1 addition & 0 deletions docs/member-cards-search/geometry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
110 changes: 110 additions & 0 deletions docs/member-cards-search/live-search-before.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
[
{
"host": "https://dev.procollab.ru",
"query": "Иван",
"status": 200,
"count": 5
},
{
"host": "https://dev.procollab.ru",
"query": "иван",
"status": 200,
"count": 0
},
{
"host": "https://dev.procollab.ru",
"query": "ИВАН",
"status": 200,
"count": 0
},
{
"host": "https://dev.procollab.ru",
"query": "Иванов",
"status": 200,
"count": 2
},
{
"host": "https://dev.procollab.ru",
"query": "иВаНоВ",
"status": 200,
"count": 0
},
{
"host": "https://dev.procollab.ru",
"query": "Иван Иванов",
"status": 200,
"count": 5
},
{
"host": "https://dev.procollab.ru",
"query": "иван иванов",
"status": 200,
"count": 0
},
{
"host": "https://dev.procollab.ru",
"query": " Иван Иванов ",
"status": 200,
"count": 5
},
{
"host": "https://dev.procollab.ru",
"query": "Иван НесуществующаяФамилияПроверка",
"status": 200,
"count": 5
},
{
"host": "https://api.procollab.ru",
"query": "Иван",
"status": 200,
"count": 365
},
{
"host": "https://api.procollab.ru",
"query": "иван",
"status": 200,
"count": 10
},
{
"host": "https://api.procollab.ru",
"query": "ИВАН",
"status": 200,
"count": 0
},
{
"host": "https://api.procollab.ru",
"query": "Иванов",
"status": 200,
"count": 171
},
{
"host": "https://api.procollab.ru",
"query": "иВаНоВ",
"status": 200,
"count": 0
},
{
"host": "https://api.procollab.ru",
"query": "Иван Иванов",
"status": 200,
"count": 365
},
{
"host": "https://api.procollab.ru",
"query": "иван иванов",
"status": 200,
"count": 10
},
{
"host": "https://api.procollab.ru",
"query": " Иван Иванов ",
"status": 200,
"count": 365
},
{
"host": "https://api.procollab.ru",
"query": "Иван НесуществующаяФамилияПроверка",
"status": 200,
"count": 365
}
]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading