Огляд стану платформи за кодом обох репозиторіїв. Мета — скласти спільну картину перед фінальним етапом і домовитись про перелік того, що входить у здачу.
Ми пройшлися по коду фронтенду й бекенду, звірили екрани з серверним API і зібрали загальну картину. Це не перевірка роботи й не список претензій — це спроба побачити ціле, бо платформа виросла велика і тримати її всю в голові вже складно.
Загальне враження: зроблено значно більше, ніж здається збоку. Навчальне ядро — урок, домашні завдання, іспити, корекції, відвідуваність, бібліотеки контенту — це серйозна, добре продумана система, яка реально працює на живих групах. Нижче — те, що лишилось відкритим, і кілька місць, які варто подивитись до релізу.
Оцінки годин — наші попередні прикидки, зроблені ззовні. Ти краще знаєш код, тож будемо вдячні, якщо звіриш їх зі своїм відчуттям — саме для цього документ і зроблено.
Домени, які за кодом виглядають завершеними: є повна модель даних, серверне API та робочий інтерфейс. Частина з них уже під навантаженням — видно за доданими індексами й моніторингом.
Проведення заняття, слайди, презентація слів, конструктор речень, малювання поверх слайда, чат уроку, трансляція звуку учням.
Jitsi з токенами, демонстрація екрана з банером, кімнати для підгруп і закриття всіх однією кнопкою, ім'я з профілю.
Здача з чернеткою, перевірка, правки прямо в тексті відповіді, коментарі, чат студент↔викладач.
П'ять частин, оцінка за кожну відповідь, окрема оцінка комунікації, підсумковий звіт із розбивкою.
Ланцюжок «правка → відповідь → підтвердження» з раундами, режимом читання минулих кроків і окремим днем корекцій.
Потоки по рівнях, склад груп, розклад із канікулами, філії, статуси навчання й оплати студента в групі.
Автовідмітка присутності й запізнення, облік входів-виходів, ручна корекція викладачем.
Слова по рівнях, малі слова з теорією, шаблони речень, ідіоми, відео. Списки переюзуються між уроками.
Календар подій, масовий зсув розкладу, окремий тип «день корекцій», перерахунок порядку занять.
Ролі, гранулярні дозволи, меню під роль, заявки, канбан лідів, картки з коментарями.
Повний цикл із подвійним погодженням, відмовами, повторами та історією.
Особисті та групові чати з файлами, тікети з передачею між філіями — усе в реальному часі.
Це не про функціонал, а про речі, які можуть вистрілити на проді. Виносимо окремо, бо вони дешеві в роботі, але дорогі, якщо пройти повз.
На /profile у запит історії платежів не потрапляє ідентифікатор користувача (у маршруті його немає), а allPayments без фільтра повертає рахунки всіх користувачів. Схоже на недогляд у зв'язці маршрут → компонент.
payment-history.component.tsx:47 · AllPayments.php:18
@guardgetZoom і createZoom у схемі не мають директиви @guard — тобто доступні без авторизації. У резолвері GetZoom лишився dd(). Оскільки Zoom уже не використовується, найпростіше — видалити разом із ZoomService і трьома Zoom-пакетами з package.json.
lessons.graphql:373,397 · GetZoom.php:15
Шість каналів у routes/channels.php авторизуються через return true — notify.update.{userId}, correction.{studentId}, chat.list.{userId} та інші. Технічно це дозволяє підписатись на чужі сповіщення й корекції.
@guardstartLesson, endLesson, visitUpdate, homework, checkHomework перевіряють лише авторизацію, без ролі й належності до групи. На фронті кнопка «Почати урок» додатково має закоментовану перевірку часу (lesson-preview.component.tsx:109-115).
config/jitsi.php містить jwt_app_id і jwt_app_secret як дефолти, а у фронтовому .env.example лежить NEXT_PUBLIC_GOOGLE_CLIENT_SECRET. Заразом варто винести домен Jitsi у змінну оточення — зараз jitsi-lms-test.on-forge.com вписаний прямо в компонент (zoom-sdk.component.tsx:100).
RolePermissionSeeder заводить admin/teacher/student/applicant у нижньому регістрі, тоді як код звіряє з RolePermissionEnum у верхньому. На чинному сервері це не проявляється, але розгортання з нуля дасть права лише адміну.
Найкорисніша частина огляду. Це не відсутні функції, а місця, де інтерфейс обіцяє дію, і користувач упевнений, що вона сталася.
В інтерфейсі активації можна обрати DATE і задати період, мутація його зберігає. Але StartPoll розсилає тригери лише для NOW, а PollService::checkTriggers() обробляє NUMBER_LESSON, BEFORE_EXAM, AFTER_EXAM. Планувальник теж нічого не запускає. Адміністратор натискає «активувати» і вважає, що опитування пішло.
Інтерфейсу поінпутного оцінювання в домашці немає (є лише в іспиті), тож finish-check-modal надсилає inputs без поля mark. У CheckHomework.php:61-68 у цьому випадку записується mark_total = mark_max — тобто в базі накопичуються 100% за всі домашки. Або робити оцінювання, або не писати бали для не-іспитів.
Якщо викладач лише розмітив відповідь, але не написав текст, фронт відправляє correction: undefined (hw-exercises.component.tsx:149-168). У CheckCorrection.php:32 перевіряється !empty($args['correction']), тож замість нового раунду ланцюг закривається зі статусом «виконано».
Фронт готовий повністю — черга опитувань, модалка, мутація createLessonEvaluation, колонка в журналі групи. Але User::getActiveLessonEvaluationsAttribute() (User.php:381-384) повертає жорсткий [], тому вікно не з'являється ніколи, а в журналі зірочка завжди порожня. Схоже, лишилось із часів налагодження.
У журналі транзакцій обидві кнопки не мають onClick (transactions-popover.component.tsx:88,95). Поки онлайн-оплати немає, вони обіцяють дію, якої не існує.
У базі змодельовано все: тарифи, аванси, акції, сімейна знижка, статуси, ключі LiqPay. Але платіжного виклику в коді немає — ні формування підпису, ні callback-роуту, ні SDK у залежностях. LiqpayAccount лишається довідником, ключі ніде не читаються.
Майстер /payment для абітурієнта поки що на захардкоджених цифрах (6900 / 12420 / 16560), вибір рівня й категорії нікуди не зберігається. При цьому реальні тарифи вже є в allTariffs — дані для нього готові.
Є ще гілка dev_2 з початком OfferService і GetOffers — можливо, її варто підняти замість писати заново.
Із чотирьох вкладок працює одна — «Віджети». «Загальні», «Повідомлення» і «Фінанси» — форми з порожнім onSubmit, і бекенду під них теж немає: type Settings містить лише widgets. Тут спершу потрібне рішення від школи, що саме має бути, а вже потім робота.
Reverb, дровер, липкі тости, позначення прочитаним — усе працює. Але SendNotificationService::send() викликається лише із заміни викладача і чату. Домашка перевірена, урок завтра, борг, новий тікет (там виклик закоментований у CreateSupportTicket.php:45) — не сповіщають. Плюс мутації «прочитати всі» і «видалити» описані з обох боків, але жодна кнопка їх не викликає — це 4–6 год.
Відповіді збираються з розрізами по групі й рівню, але запиту, що повертає список результатів, у схемі немає — у Query лише allPolls і getPoll. Тобто дані є, подивитись їх цілісно неможливо.
Розділ «Навчання» має закоментовані вкладки, хоча Курси, Акції й Тести мають повний CRUD на бекенді і готові хуки на фронті — вони ще й приховані на рівні ролей (Role.php:57-59). Схожа історія з колонками статусів у /users. Варто просто вирішити: воскрешаємо чи прибираємо.
Три сценарії обсягу. Це прикидки ззовні — цифри потрібні радше щоб домовитись про межу здачі, ніж щоб когось ними міряти.
Частини 2 і 3 цього документа плюс прибрати макети з очей користувача.
Мінімум плюс усі відкриті напрямки, крім платіжної системи.
Разом із наскрізною оплатою і робочим майстром для абітурієнта.
У робочих гілках накопичилось 177 комітів поза main: lms-be — 61, lms-fe — 116, разом близько 7 300 доданих рядків і 9 незастосованих міграцій. У бекенді main ще й має 2 коміти, яких немає в dev (хотфікси 14–15.04), тож мерж потребує ручного розгрібання. Автотестів у проєкті практично немає, тож перевірка буде ручна. Це варто зробити завчасно, а не разом з релізом.
dev → main і прогнати міграції?Хочемо запланувати це заздалегідь і разом вирішити, як перевіряти без автотестів.dev_2 з OfferService піднімаємо чи закриваємо?Там початок офферів на оплату від 12.03 — щоб не робити двічі.Огляд зроблено читанням коду гілок develop (інтерфейс) і dev (сервер) станом на 23 липня 2026: 12 серверних модулів, 169 міграцій, близько 125 екранів і блоків інтерфейсу, звірка кожного екрана з відповідними GraphQL-операціями.
Обмеження, про яке чесно попереджаємо: це аналіз коду, а не тестування. Без доступу до стенда під ролями ми не могли перевірити фактичну поведінку — зокрема роботу breakout-кімнат Jitsi, реальну доставку звуку учням і повний прохід оплати. Якщо даси тестові акаунти на dev, доповнимо огляд перевіреними спостереженнями замість припущень.