Технічний огляд коду · робочий документ

English Prime LMS — де ми зараз

Огляд стану платформи за кодом обох репозиторіїв. Мета — скласти спільну картину перед фінальним етапом і домовитись про перелік того, що входить у здачу.

23 липня 2026 · lms-fe (develop) + lms-be (dev)

Ми пройшлися по коду фронтенду й бекенду, звірили екрани з серверним API і зібрали загальну картину. Це не перевірка роботи й не список претензій — це спроба побачити ціле, бо платформа виросла велика і тримати її всю в голові вже складно.

Загальне враження: зроблено значно більше, ніж здається збоку. Навчальне ядро — урок, домашні завдання, іспити, корекції, відвідуваність, бібліотеки контенту — це серйозна, добре продумана система, яка реально працює на живих групах. Нижче — те, що лишилось відкритим, і кілька місць, які варто подивитись до релізу.

Оцінки годин — наші попередні прикидки, зроблені ззовні. Ти краще знаєш код, тож будемо вдячні, якщо звіриш їх зі своїм відчуттям — саме для цього документ і зроблено.

Частина 1

Що вже працює

Домени, які за кодом виглядають завершеними: є повна модель даних, серверне API та робочий інтерфейс. Частина з них уже під навантаженням — видно за доданими індексами й моніторингом.

Онлайн-урок

Проведення заняття, слайди, презентація слів, конструктор речень, малювання поверх слайда, чат уроку, трансляція звуку учням.

Відеозв'язок

Jitsi з токенами, демонстрація екрана з банером, кімнати для підгруп і закриття всіх однією кнопкою, ім'я з профілю.

Домашні завдання

Здача з чернеткою, перевірка, правки прямо в тексті відповіді, коментарі, чат студент↔викладач.

Іспити

П'ять частин, оцінка за кожну відповідь, окрема оцінка комунікації, підсумковий звіт із розбивкою.

Корекції

Ланцюжок «правка → відповідь → підтвердження» з раундами, режимом читання минулих кроків і окремим днем корекцій.

Групи й потоки

Потоки по рівнях, склад груп, розклад із канікулами, філії, статуси навчання й оплати студента в групі.

Відвідуваність

Автовідмітка присутності й запізнення, облік входів-виходів, ручна корекція викладачем.

Бібліотека контенту

Слова по рівнях, малі слова з теорією, шаблони речень, ідіоми, відео. Списки переюзуються між уроками.

Розклад

Календар подій, масовий зсув розкладу, окремий тип «день корекцій», перерахунок порядку занять.

Користувачі й права

Ролі, гранулярні дозволи, меню під роль, заявки, канбан лідів, картки з коментарями.

Заміна викладача

Повний цикл із подвійним погодженням, відмовами, повторами та історією.

Чати й підтримка

Особисті та групові чати з файлами, тікети з передачею між філіями — усе в реальному часі.

Частина 2

Місця, які варто подивитись до релізу

Це не про функціонал, а про речі, які можуть вистрілити на проді. Виносимо окремо, бо вони дешеві в роботі, але дорогі, якщо пройти повз.

Історія оплат у профілі віддає більше, ніж треба

пріоритет3–5 год

На /profile у запит історії платежів не потрапляє ідентифікатор користувача (у маршруті його немає), а allPayments без фільтра повертає рахунки всіх користувачів. Схоже на недогляд у зв'язці маршрут → компонент.

payment-history.component.tsx:47 · AllPayments.php:18

Старі Zoom-ендпоінти лишились без @guard

пріоритет1–3 год

getZoom і createZoom у схемі не мають директиви @guard — тобто доступні без авторизації. У резолвері GetZoom лишився dd(). Оскільки Zoom уже не використовується, найпростіше — видалити разом із ZoomService і трьома Zoom-пакетами з package.json.

lessons.graphql:373,397 · GetZoom.php:15

Канали трансляції відкриті

варто закрити4–6 год

Шість каналів у routes/channels.php авторизуються через return truenotify.update.{userId}, correction.{studentId}, chat.list.{userId} та інші. Технічно це дозволяє підписатись на чужі сповіщення й корекції.

Дії з уроком і домашками — тільки @guard

варто закрити12–20 год

startLesson, endLesson, visitUpdate, homework, checkHomework перевіряють лише авторизацію, без ролі й належності до групи. На фронті кнопка «Почати урок» додатково має закоментовану перевірку часу (lesson-preview.component.tsx:109-115).

Секрети зі значеннями за замовчуванням у репозиторії

варто закрити4–8 год

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).

Сідер ролей створює ролі в іншому регістрі

варто закрити2–4 год

RolePermissionSeeder заводить admin/teacher/student/applicant у нижньому регістрі, тоді як код звіряє з RolePermissionEnum у верхньому. На чинному сервері це не проявляється, але розгортання з нуля дасть права лише адміну.

Частина 3

Те, що виглядає робочим, але мовчки не спрацьовує

Найкорисніша частина огляду. Це не відсутні функції, а місця, де інтерфейс обіцяє дію, і користувач упевнений, що вона сталася.

Тригер опитування «за датою» не обробляється

8–12 год

В інтерфейсі активації можна обрати DATE і задати період, мутація його зберігає. Але StartPoll розсилає тригери лише для NOW, а PollService::checkTriggers() обробляє NUMBER_LESSON, BEFORE_EXAM, AFTER_EXAM. Планувальник теж нічого не запускає. Адміністратор натискає «активувати» і вважає, що опитування пішло.

Бали за домашні завдання проставляються повними

12–20 год

Інтерфейсу поінпутного оцінювання в домашці немає (є лише в іспиті), тож finish-check-modal надсилає inputs без поля mark. У CheckHomework.php:61-68 у цьому випадку записується mark_total = mark_max — тобто в базі накопичуються 100% за всі домашки. Або робити оцінювання, або не писати бали для не-іспитів.

«Надіслати» в корекції може закрити ланцюг

3–6 год

Якщо викладач лише розмітив відповідь, але не написав текст, фронт відправляє correction: undefined (hw-exercises.component.tsx:149-168). У CheckCorrection.php:32 перевіряється !empty($args['correction']), тож замість нового раунду ланцюг закривається зі статусом «виконано».

Оцінка уроку зірочками не доходить до студента

4–8 год

Фронт готовий повністю — черга опитувань, модалка, мутація createLessonEvaluation, колонка в журналі групи. Але User::getActiveLessonEvaluationsAttribute() (User.php:381-384) повертає жорсткий [], тому вікно не з'являється ніколи, а в журналі зірочка завжди порожня. Схоже, лишилось із часів налагодження.

Кнопки «Сплатити» і «Завантажити PDF» без обробників

див. нижче

У журналі транзакцій обидві кнопки не мають onClick (transactions-popover.component.tsx:88,95). Поки онлайн-оплати немає, вони обіцяють дію, якої не існує.

Частина 4

Відкриті напрямки

Онлайн-оплата

найбільший блок110–170 год

У базі змодельовано все: тарифи, аванси, акції, сімейна знижка, статуси, ключі LiqPay. Але платіжного виклику в коді немає — ні формування підпису, ні callback-роуту, ні SDK у залежностях. LiqpayAccount лишається довідником, ключі ніде не читаються.

Майстер /payment для абітурієнта поки що на захардкоджених цифрах (6900 / 12420 / 16560), вибір рівня й категорії нікуди не зберігається. При цьому реальні тарифи вже є в allTariffs — дані для нього готові.

Є ще гілка dev_2 з початком OfferService і GetOffers — можливо, її варто підняти замість писати заново.

Налаштування

50–80 год

Із чотирьох вкладок працює одна — «Віджети». «Загальні», «Повідомлення» і «Фінанси» — форми з порожнім onSubmit, і бекенду під них теж немає: type Settings містить лише widgets. Тут спершу потрібне рішення від школи, що саме має бути, а вже потім робота.

Сповіщення — механізм є, подій мало

24–40 год

Reverb, дровер, липкі тости, позначення прочитаним — усе працює. Але SendNotificationService::send() викликається лише із заміни викладача і чату. Домашка перевірена, урок завтра, борг, новий тікет (там виклик закоментований у CreateSupportTicket.php:45) — не сповіщають. Плюс мутації «прочитати всі» і «видалити» описані з обох боків, але жодна кнопка їх не викликає — це 4–6 год.

Аналітика опитувань

20–30 год

Відповіді збираються з розрізами по групі й рівню, але запиту, що повертає список результатів, у схемі немає — у Query лише allPolls і getPoll. Тобто дані є, подивитись їх цілісно неможливо.

Закоментований функціонал

16–24 год

Розділ «Навчання» має закоментовані вкладки, хоча Курси, Акції й Тести мають повний CRUD на бекенді і готові хуки на фронті — вони ще й приховані на рівні ролей (Role.php:57-59). Схожа історія з колонками статусів у /users. Варто просто вирішити: воскрешаємо чи прибираємо.

Частина 5

Скільки це приблизно

Три сценарії обсягу. Це прикидки ззовні — цифри потрібні радше щоб домовитись про межу здачі, ніж щоб когось ними міряти.

Мінімум до релізу

90–150 год

Частини 2 і 3 цього документа плюс прибрати макети з очей користувача.

Без онлайн-оплати

280–420 год

Мінімум плюс усі відкриті напрямки, крім платіжної системи.

Повний обсяг

390–630 год

Разом із наскрізною оплатою і робочим майстром для абітурієнта.

Ще один пункт, який варто спланувати окремо

У робочих гілках накопичилось 177 комітів поза main: lms-be — 61, lms-fe — 116, разом близько 7 300 доданих рядків і 9 незастосованих міграцій. У бекенді main ще й має 2 коміти, яких немає в dev (хотфікси 14–15.04), тож мерж потребує ручного розгрібання. Автотестів у проєкті практично немає, тож перевірка буде ручна. Це варто зробити завчасно, а не разом з релізом.

Частина 6

Що хочемо обговорити

01Онлайн-оплата входить у вересневу здачу?Якщо ні — фіксуємо, що оплати вносяться вручну, і прибираємо неробочі кнопки, щоб не вводити школу в оману.
02Коли зручно зробити мерж devmain і прогнати міграції?Хочемо запланувати це заздалегідь і разом вирішити, як перевіряти без автотестів.
03Коли переводимо відео на бойовий Jitsi?Зараз домен тестового інстансу зашитий у код.
04Що має бути в «Налаштуваннях»?Три вкладки без бекенду — потрібен перелік від школи, або ховаємо їх до наступного етапу.
05Які події мають надсилати сповіщення?Механізм готовий, лишилось домовитись про список подій.
06Гілку dev_2 з OfferService піднімаємо чи закриваємо?Там початок офферів на оплату від 12.03 — щоб не робити двічі.
07Чи потрібна школі зведена аналітика опитувань?Дані вже збираються, питання лише в тому, чи це в скоупі.
08Який фінальний перелік вважаємо здачею?Головне питання. Без письмового переліку список доробок поповнюватиметься нескінченно — це нормально для живого продукту, але тоді потрібна межа релізу.
Метод

Як складено цей документ

Огляд зроблено читанням коду гілок develop (інтерфейс) і dev (сервер) станом на 23 липня 2026: 12 серверних модулів, 169 міграцій, близько 125 екранів і блоків інтерфейсу, звірка кожного екрана з відповідними GraphQL-операціями.

Обмеження, про яке чесно попереджаємо: це аналіз коду, а не тестування. Без доступу до стенда під ролями ми не могли перевірити фактичну поведінку — зокрема роботу breakout-кімнат Jitsi, реальну доставку звуку учням і повний прохід оплати. Якщо даси тестові акаунти на dev, доповнимо огляд перевіреними спостереженнями замість припущень.