«Мої пріоритети»
Метадані
Section titled “Метадані”| Поле | Значення |
|---|---|
| Статус | 🚧 в розробці — гілка vladbandurin/priorities-dock (9 комітів попереду origin/main, не запушена, гілки немає на remote). Реалізована лише поверхня калькулятора. Верифіковано локально: десктоп — немодальний пристикований <aside> справа, мобілка — нижній sheet, dockState персиститься між навігаціями. |
| Поверхні | web — постійний док на всіх публічних локаль-сторінках (рейка/нижній бар → панель/sheet); прихований у кабінеті /profile/*, де живе повний конструктор «Симуляції». MVP-точка входу — тизер шансів калькулятора. Поза MVP (закладено архітектурно): офер-картки пошуку (Фаза 2) і рейтингові списки (Фаза 3). |
| Доступність | безкоштовно, без акаунта — гість збирає, сортує і бачить статус локально (localStorage). Акаунт потрібен лише щоб зберегти/синхронізувати список. Платний анлок — тільки вердикт у «Симуляції вступу» (entitlement simulation, premium one-off). |
| Власник | фронт — Шура (за дизайн-спекою) |
| Останнє підтвердження | 2026-06-27 · гілка:vladbandurin/priorities-dock (85b457ca, локальна) + локальний Playwright-smoke (десктоп немодальний aside + мобільний bottom-sheet, dockState персиститься) + дизайн-спека .context/priorities-dock/ |
Призначення
Section titled “Призначення”«Мої пріоритети» — це безкоштовний верх воронки до платної «Симуляції вступу». Він закриває три тривоги абітурієнта-2026 ще до пейволу: «чи я взагалі проходжу хоч кудись на бюджет?», «як зібрати короткий список без 40 вкладок?» і «який порядок подати, поки це ще можна змінити?» (порядок заяв у ЄДЕБО після подачі незмінний — це головний емоційний важіль усього сезону).
Замість збирати стратегію вручну (Excel, зошит, скріни) абітурієнт одним кліком кидає вступи з тизера шансів калькулятора у постійний бічний док, бачить по кожному рядку статус «куди проходжу» (на бюджет / на контракт / не проходиш), перетягує порядок — і коли готовий, апселиться у повну (платну) симуляцію за справжнім алгоритмом розподілу.
Принцип «один список, скрізь». Док і сторінка-конструктор /profile/simulation показують той самий список (simulation_application на сервері / локальний дзеркальний для гостя). Док = легкий завжди-доступний вид + reorder; конструктор = повний верстак. Ніколи не два списки.
Свідомо ≠ «Вступлісти». «Вступлісти» = збережені каталоги оферів (публічні/приватні колекції). «Мої пріоритети» = один впорядкований робочий список заяв із наміром і статусом. На MVP вони лишаються окремими сутностями.
Межа free / платне (визначає всю цінність доку)
Section titled “Межа free / платне (визначає всю цінність доку)”| Дія | Доступ |
|---|---|
| Зібрати список (додати/видалити, намір бюджет/контракт) | безкоштовно (гість або акаунт) |
| Перетягнути порядок пріоритетів (drag + ▲▼) | безкоштовно |
| Статус «куди проходжу» по кожній заяві (на бюджет / на контракт / не проходиш / введи бал) | безкоштовно (клієнтська евристика) |
| Безкоштовний cascade-preview («за цим порядком бюджет спрацював би на №N — нижчі не розглядатимуться») | безкоштовно |
| Повна симуляція-вердикт (deferred-acceptance, широкий конкурс, прохідний-2026) | платно (entitlement simulation) |
Чому так можна: пейвол сидить лише на рендері сторінки /profile/simulation (useSimulationGate). Ендпоінти add/reorder/hydrate не за пейволом; гість ніколи не isBlocked. Док використовує лише ці безкоштовні шляхи + апселить платний вердикт.
⚠️ Статус-чіп — евристика, не вердикт. Він рахується клієнтсько проти прохідного балу 2025 (
derivePriorityStatus.ts), а не за живими даними 2026. Футер доку це чесно називає: «Статуси — орієнтовно, за торішнім балом. Точний вердикт 2026 — у повній симуляції.» Це і є демо платної цінності, тому довіра до чіпа — головний шарнір конверсії.
User flow
Section titled “User flow”Точка входу (MVP): https://abitly.org/nmt-calculator → тизер шансів «куди проходиш». Док присутній на кожній публічній сторінці (змонтований у кореневому [locale]/layout.tsx); прихований лише в кабінеті /profile/*.
- Гість рахує бал на калькуляторі НМТ →
ChancesTeaserSectionпоказує оффери «куди проходиш» (кожнаChanceCardуже несеofferId,probabilityhigh=бюджет / medium=контракт,passingScore2025,delta). - «+ у пріоритети» (
AddToPrioritiesButton, сусід картки) →usePriorities.addпише{ offerId, intent, offerMeta }уusePrioritiesStore(localStorageabitly_priorities). Намір авто:high→budget,medium→contract. Дедуп + кап ≤10 (на ліміті картка → «Список повний (10)»; дубль → «№N у пріоритетах»). Не тригерить реєстраційний CTA. Подіяpriorities_item_added. - Док засвічується. Десктоп — вертикальна рейка на правому краї (іконка + лічильник + кольорова смужка «N на бюджет»); мобілка — тонкий нижній бар. Мікро-пульс на щойно доданому рядку (1.4 с), aria-live анонс.
- Відкриття доку (клік рейки /
Alt+Pз будь-де, фізична клавішаKeyP→ працює і на укр-розкладці) →dockState='expanded', подіяpriorities_dock_opened. Десктоп розгортає немодальний пристикований<aside>~380px (без scrim, без focus-trap, без body-scroll-lock — сторінка позаду повністю клікабельна); мобілка — bottom-sheet із ручкою. - Перегляд списку — драг-рядки зі статус-чіпом «куди проходжу» (рахується клієнтсько: КБ із калькулятора vs
passingScore2025; без балу → «введи бал»). Хедер показуєN+ «X на бюджет / Y контракт», банер при5 на бюджет/10 всього. - Reorder — drag (framer
Reorder, debounce 400 мс →priorities_reordered method='drag') або ▲▼ на рядку (move→priorities_reordered method='arrows'); порядок =priority(index+1), оптимістично, автозбереження локально. - Безкоштовний cascade-preview під списком: «За цим порядком бюджет спрацював би на №N — нижчі вступи не розглядатимуться» + (якщо
№N > 1) «Мрія першою — це безпечно. Бюджет однаково спрацює на наступному вступі, де проходиш.» (вбиває міф «висока мрія спалює бюджет»). Рядки нижче першого бюджетного позначаютьсяbelowBudget(приглушені). - Видалення + undo —
removeфаєрить тост «Прибрано … · Скасувати» (5 с,insertAtповертає на місце), подіяpriorities_item_removed. - Гість → збереження — картка «💾 Збережи список — зараз лише на цьому пристрої» →
goSave:rememberRegCta('priorities_dock')+storeReturnUrl()→/auth/register. Подіяpriorities_save_prompt_clicked. - Міграція на акаунт — після auth
CallbackPageповертає на місце;usePrioritiesMigrationробить replay локального списку →simulationApi.add({items})(bulk, у порядку пріоритету; сервер дедупить). Подіяpriorities_migrated_on_auth {count, dropped}. Локальний список лишається джерелом відображення доку (read-side серверна гідрація — наступна фаза). - Апсел — футер доку «▸ Повна симуляція вступу» →
goFullSim(priorities_full_sim_cta_clicked) →router.push('/profile/simulation')→ платний вердикт «Симуляції вступу» (entitlementsimulation, Monobank-checkout).
flowchart TD
A[Калькулятор НМТ → тизер «куди проходиш»] -->|+ у пріоритети\npriorities_item_added| B[(usePrioritiesStore\nlocalStorage гостя)]
B --> C[Рейка засвітилась\nдесктоп: правий край · мобілка: нижній бар]
C -->|клік / Alt+P\npriorities_dock_opened| D[Док розгорнуто\nнемодальний aside / bottom-sheet]
D --> E[Статус-чіп «куди проходжу»\nевристика КБ vs прохідний-2025]
D -->|drag / ▲▼\npriorities_reordered| F[Reorder + cascade-preview\n«бюджет на №N — нижчі не розглянуть»]
F --> G{Гість?}
G -->|так\npriorities_save_prompt_clicked| H[/auth/register\nstoreReturnUrl]
H -->|після auth\npriorities_migrated_on_auth| I[replay → simulationApi.add\nсписок у кабінеті]
G -->|ні| I
F -->|футер: Повна симуляція\npriorities_full_sim_cta_clicked| J[/profile/simulation]
I -->|готовий → апсел| J
J -->|платний| K[«Симуляція вступу»\nentitlement simulation → Monobank]
Воронки та аналітика (sales funnels)
Section titled “Воронки та аналітика (sales funnels)”Це серце фічі: безкоштовна звичка (зібрав → відсортував → побачив статус) конвертується у платний вердикт. Канал як у решти сайту: track() → window.dataLayer → GTM GTM-W64R6XXK → GA4 G-XKDRSM3T0Z (dataLayer фаєриться лише на хості abitly.org — QA через GA4 DebugView). Усі події — IDs-only / PII-safe: лише offer_id, count, булеві прапори; ніколи назв ЗВО/спеціальностей чи сирих балів.
Точки входу у воронку
Section titled “Точки входу у воронку”| Поверхня | Що це | Подія / source |
|---|---|---|
Тизер шансів калькулятора (ChanceCard → AddToPrioritiesButton) | єдина точка входу MVP; PrioritySource має лише 'calculator_chances' | priorities_item_added {offer_id, intent, source:'calculator_chances', count, is_authenticated} |
| Рейка/нижній бар доку | завжди-доступний тригер на всіх публічних сторінках | подія лише на відкриття (priorities_dock_opened), не на показ рейки |
| Картка «Збережи список» (гість) | м’який прес у природний момент | priorities_save_prompt_clicked {count} → /auth/register |
| Футер доку «Повна симуляція вступу» | головний апсел у платну воронку | priorities_full_sim_cta_clicked {count} → /profile/simulation |
| Офер-картки пошуку / рейтингові списки | Фаза 2/3 — закладено в типах, ще не підключено | TODO: нові source (потрібно розширити PrioritySource) |
Шлях конверсії (free habit → paid verdict)
Section titled “Шлях конверсії (free habit → paid verdict)”flowchart LR
subgraph FREE[Безкоштовна звичка]
ADD[priorities_item_added] --> OPEN[priorities_dock_opened]
OPEN --> REORD[priorities_reordered]
REORD --> SAVE[priorities_save_prompt_clicked]
SAVE --> MIG[priorities_migrated_on_auth]
end
REORD -->|апсел| CTA[priorities_full_sim_cta_clicked]
MIG -->|апсел| CTA
CTA --> SIM[/profile/simulation]
subgraph PAID[Платна воронка «Симуляції»]
SIM --> PAYWALL[paywall-модалка]
PAYWALL --> BC[simulation_begin_checkout]
BC --> PUR[simulation_purchase]
end
Типізовані події цієї фічі (7, src/lib/analytics/events/priorities.ts)
Section titled “Типізовані події цієї фічі (7, src/lib/analytics/events/priorities.ts)”| Подія | Де фаєриться (чокпоінт) | Properties | Призначення у воронці |
|---|---|---|---|
priorities_item_added | usePriorities.add (тільки якщо список реально виріс — дедуп/кап = no-op без події) | offer_id, intent, source, count, is_authenticated | активація; count===1 = перше додавання |
priorities_item_removed | usePriorities.remove | offer_id, count, is_authenticated | відтік зі списку |
priorities_reordered | usePriorities.reorder (drag, debounce 400 мс) і usePriorities.move (▲▼) | count, method:'drag'|'arrows' | головна цінна дія — репетиція незмінного порядку |
priorities_dock_opened | PrioritiesDock.open (клік рейки / Alt+P) | count | залученість (рейка→відкриття) |
priorities_save_prompt_clicked | PrioritiesDock.goSave (картка «Збережи список») | count | гість → реєстрація (capture) |
priorities_migrated_on_auth | usePrioritiesMigration після auth | count, dropped | успішність переносу гостьового списку на акаунт |
priorities_full_sim_cta_clicked | PrioritiesDock.goFullSim (футер-апсел) | count | перехід у платну воронку |
Стик із платною воронкою «Симуляції»
Section titled “Стик із платною воронкою «Симуляції»”Після priorities_full_sim_cta_clicked користувач потрапляє у воронку «Симуляції вступу» та її paywall-події: simulation_constructor_viewed → simulation_begin_checkout {source, value, items} → (Monobank) → simulation_purchase {transaction_id, value} (+ simulation_payment_init_failed на помилці). Покупку-по-поверхні редірект Monobank стирає → читається когортно через лійку begin_checkout(source) → purchase.
Відомі прогалини інструментарію (із sales-аудиту)
Section titled “Відомі прогалини інструментарію (із sales-аудиту)”- Немає impression-події на показ
AddToPrioritiesButtonі на появу рейки → add-rate і rail→open рахуються неточно (знаменник невидимий). priorities_save_prompt_shownописаний у дизайн-спеці (§9), але в коді не реалізований — фаєриться лише*_clicked; прес пасивний (внизу відкритої панелі, не на рейці).droppedу міграції — all-or-nothing: будь-яка помилка →dropped = увесь батч; реальні часткові дропи на серверних капах (5/10) атрибутуються невірно.method:'arrows'фаєриться лише з ▲▼ на рядку; решта reorder —'drag'.- Кап/дедуп — тихий no-op без події → користувач на ліміті 10 (саме high-intent для апселу) не вимірюється окремо.
Стан GTM/GA4 та публікація
Section titled “Стан GTM/GA4 та публікація”- Не зроблено: події не зареєстровані як custom dimensions у GA4 і GTM-workspace не опубліковано (
TODO:— окремий людський крок, як і дляsimulation_*; ⚠️ GA4-property вже на 49/50 event-scoped CD — близько ліміту). До публікації події летять уdataLayer, але в GA4-звітах їх не видно. - Свідома прогалина: маркетингові лінки Meta/TikTok на калькулятор мусять нести UTM (
fbclidневидимий для GA4), інакше джерело трафіку у воронку доку губиться.
Дані та API
Section titled “Дані та API”| Джерело | Ендпоінт / entity | Призначення |
|---|---|---|
localStorage | ключ abitly_priorities (zustand+persist, skipHydration + ручний rehydrate) | гостьовий список + персист dockState (док лишається відкритим між навігаціями) |
| api-v2 | POST /profile/simulation (simulationApi.add, bulk {items:[{offerId,intent}]}) | logged-in add (fire-and-forget) + replay при міграції; сервер дедупить по (user, offer, intent) |
| api-v2 | POST /profile/simulation/hydrate · PUT /…/order | план — серверний статус «куди проходжу» і серверний reorder для logged-in (read-side, ще не підключено в доці) |
| Postgres | simulation_application | спільна сутність списку з «Симуляцією»: userId + offerId + intent + priority; ліміти ≤10 / ≤5 на бюджет |
Жодного нового бекенду в MVP. Док переюзає наявні ендпоінти «Симуляції» через фасад usePriorities(): гість — локальний стор, акаунт — simulationApi. Статус гостя рахується клієнтсько з offerMeta (кешується з картки, з якої додали). Можлива оптимізація (Фаза 2+) — серверний POST /profile/simulation/import замість клієнтського replay, якщо replay виявиться крихким на лімітах/дедупі.
Зв’язки з іншими фічами
Section titled “Зв’язки з іншими фічами”- «Симуляція вступу» — платна вершина цієї воронки; той самий список (
simulation_application), апсел-CTA веде в конструктор/profile/simulation; статус-чіп доку = безкоштовне демо платного вердикту. - Калькулятор конкурсного бала — єдина точка входу MVP: тизер шансів («куди проходиш») рендерить «+ у пріоритети»; КБ із калькулятора живить статус-чіп.
- Checkout Monobank — апсел далі веде через спільний
premium-флоу (entitlementsimulation). - Збережене / Вступлісти — свідомо інша сутність (збережені каталоги), не зливається з «Моїми пріоритетами» на MVP.
- Автентифікація — гостьовий список мігрує на акаунт при логіні/реєстрації (
storeReturnUrlповертає на місце).
| Репо | Шлях | Примітка |
|---|---|---|
abitly-frontend-v2 | src/components/features/priorities/PrioritiesDock.tsx | root-mounted: рейка/бар + немодальний aside / bottom-sheet; route-aware (прихований у /profile/*); Alt+P/ESC/клік-поза |
abitly-frontend-v2 | …/priorities/{PrioritiesRail,PrioritiesAside,PrioritiesMobileSheet,PrioritiesPanel,PriorityRow}.tsx | рейка · немодальний <aside> (без scrim/focus-trap) · мобільний sheet · панель із cascade-preview · рядок із drag/▲▼/видалити |
abitly-frontend-v2 | …/priorities/usePrioritiesStore.ts | zustand+persist, localStorage abitly_priorities, MAX_TOTAL=10/MAX_BUDGET=5, дедуп + renumber |
abitly-frontend-v2 | …/priorities/usePriorities.tsx | фасад гість-локальний ↔ серверний; володіє аналітикою, aria-live, undo-тостом, just-added-пульсом |
abitly-frontend-v2 | …/priorities/{derivePriorityStatus.ts,StatusChip.tsx,priorities.types.ts} | клієнтський 4-стан статусу (vs прохідний-2025), типи (PrioritySource='calculator_chances') |
abitly-frontend-v2 | …/priorities/{useDockHotkey.ts,usePrioritiesMigration.ts} | Alt+P toggle (фізична клавіша) · replay гостьового списку на сервер при auth |
abitly-frontend-v2 | …/priorities/AddToPrioritiesButton.tsx | «+ у пріоритети» на ChanceCard (free, локально; data-priorities-keepalive тримає aside відкритим) |
abitly-frontend-v2 | src/components/pages/calculator/components/registration-funnel/ChancesTeaserSection/** | MVP-поверхня: тизер шансів, де живе кнопка додавання |
abitly-frontend-v2 | src/lib/analytics/events/priorities.ts | 7 типізованих подій (окремо від simulation.ts) |
Обмеження та блокери
Section titled “Обмеження та блокери”- Не запушено / не в проді. Уся фіча — на локальній гілці
vladbandurin/priorities-dock(9 комітів попередуorigin/main, гілки немає на remote). Статус 🚧; верифікація — локальний Playwright-smoke, не prod. - MVP — лише поверхня калькулятора.
PrioritySourceмає єдине значення'calculator_chances'; офер-картки пошуку (Фаза 2) і рейтингові списки (Фаза 3) закладено архітектурно, але не підключено. - Статус — клієнтська евристика на даних 2025, не вердикт 2026 (
derivePriorityStatus.tsvspassingScore2025). Футер це чесно фреймить. Ризик довіри: гість, який повірив «на бюджет» і потім не вступив, — бренд-катастрофа в одношотовому сезоні. - Logged-in remove/reorder — лише локальні.
usePriorities.removeмає серверне видаленняdeferred(локальний стор ключований поofferId, а серверу потрібен application-id) → серверний список logged-in може розійтися з доком. Read-side серверна гідрація (hydrate) теж відкладена на пошуково-рейтингову фазу. - Міграція — best-effort клієнтський replay. Без серверного bulk-import;
droppedрахується all-or-nothing; на серверних капах (5/10) можливі тихі часткові дропи. Можлива оптимізація — сервернийPOST /profile/simulation/import. - Події не опубліковані. GTM-workspace не запаблішено, CD у GA4 не зареєстровано → у GA4-звітах подій ще не видно. Немає impression-події (add-rate/rail→open неточні);
priorities_save_prompt_shownзі спеки в коді відсутній. - Немодальний overlay не штовхає контент. Десктоп-aside — co-presence (як Claude-артефакт), а не Notion-push: накладається поверх правого краю, контент під ним не зсувається (свідоме рішення, але може перекривати правий стовпчик на вузьких лейаутах).
TODO:координація z-index/offset із кабінетнимBottomNavі тригеромFilterDrawerна пошуку.
Історія змін
Section titled “Історія змін”| Дата | Подія | Джерело |
|---|---|---|
| 2026-06-27 | дизайн-спека доку + план MVP калькулятора (free/paid межа, фасад, міграція, аналітика) | .context/priorities-dock/2026-06-27-priorities-dock-design.md + …-calculator-mvp-plan.md |
| 2026-06-27 | JTBD + UX-патерни + sales-funnel аудит (entry points, leak-гіпотези, backlog) | .context/priorities-dock/2026-06-27-research-design-bundle.json |
| 2026-06-27 | реалізація: персистентний sidebar + гостьовий стор + 7 подій + калькулятор-поверхня | гілка vladbandurin/priorities-dock 85b457ca (локальна) |
| 2026-06-27 | створено картку 🚧; статус за матрицею (гілка не запушена); локальний Playwright-smoke | ця картка |