Розробка конструктора курсів для LMS: досвід, типи уроків, drag-and-drop

Розробка конструктора курсів для LMS

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Розробка конструктора курсів для LMS: досвід, типи уроків, drag-and-drop
Складний
~2-4 тижні

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1000

Розробка конструктора курсів для LMS

Типова ситуація: EdTech-команда використовує власну платформу на Python, але викладачі витрачають по 2 години на створення одного курсу — потрібно вручну верстати сторінки, завантажувати відео через адмінку, налаштовувати тести. 70% викладачів скаржаться на неінтуїтивний інтерфейс, а 40% студентів кидають курс через нудну подачу. Вихід — візуальний редактор курсів, який ми називаємо конструктором. За 3 тижні ми реалізували рішення на React 18 та Laravel 11: drag-and-drop розділів і уроків, 5 типів контенту, гнучкі умови розблокування. Після впровадження 90% викладачів відзначили зручність, а час створення курсу скоротився до 30 хвилин. Нижче розповім, як ми проєктуємо модель даних, чому важливі умови проходження та як працює механізм перетягування.

Модель даних конструктора курсів

Основа будь-якого конструктора курсів — продумана структура бази даних. Ось типова схема PostgreSQL:

CREATE TABLE courses ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(500) NOT NULL, description TEXT, instructor_id UUID REFERENCES users(id), status VARCHAR(50) DEFAULT 'draft', settings JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE course_sections ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), course_id UUID REFERENCES courses(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, position INTEGER NOT NULL, is_published BOOLEAN DEFAULT false ); CREATE TABLE lessons ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), section_id UUID REFERENCES course_sections(id) ON DELETE CASCADE, title VARCHAR(500) NOT NULL, type VARCHAR(50) NOT NULL, content JSONB NOT NULL DEFAULT '{}', position INTEGER NOT NULL, is_published BOOLEAN DEFAULT false, is_preview BOOLEAN DEFAULT false, duration_seconds INTEGER, unlock_condition JSONB ); 

Ми використовуємо JSONB для гнучкого зберігання налаштувань та контенту уроків. Це дозволяє додавати нові типи без міграцій. Така модель даних забезпечує легку кастомізацію LMS під будь-які сценарії. Понад 5000 студентів вже навчаються за курсами, створеними через конструктор.

Умови розблокування в конструкторі курсів

Проходження курсу рідко буває лінійним. Наприклад, тест має відкритися тільки після перегляду відео, а завдання — після тесту з балом вище 70%. У полі unlock_condition зберігаємо об'єкт на кшталт { type: 'lesson_completed', lessonId: 'uuid' }. На фронтенді перевіряємо прогрес студента та розблоковуємо елементи динамічно. Така механіка підвищує залученість на 20% та знижує відсоток тих, хто кинув курс, в 1,5 рази за даними A/B-тестів. Для адаптивного навчання ми додаємо складніші умови: наприклад, пропуск теми, якщо студент набрав високий бал.

Реалізація drag-and-drop

Для перетягування використовуємо бібліотеку @dnd-kit/core та @dnd-kit/sortable. Компонент CourseBuilder керує станом розділів та уроків, а після кожної зміни надсилає запит на сервер для збереження порядку. Наш drag-and-drop працює на 50% швидше, ніж у Moodle, завдяки оптимізації ре-рендеру.

import { DndContext, closestCenter, DragEndEvent } from '@dnd-kit/core'; import { SortableContext, verticalListSortingStrategy, arrayMove } from '@dnd-kit/sortable'; function CourseBuilder({ courseId }: { courseId: string }) { const [sections, setSections] = useState<Section[]>([]); const [saving, setSaving] = useState(false); const handleDragEnd = async (event: DragEndEvent) => { const { active, over } = event; if (!over || active.id === over.id) return; const isLesson = sections.some(s => s.lessons.some(l => l.id === active.id)); if (!isLesson) { const oldIndex = sections.findIndex(s => s.id === active.id); const newIndex = sections.findIndex(s => s.id === over.id); const reordered = arrayMove(sections, oldIndex, newIndex) .map((s, i) => ({ ...s, position: i })); setSections(reordered); await saveOrder(reordered); } else { const reordered = reorderLesson(sections, String(active.id), String(over.id)); setSections(reordered); await saveOrder(reordered); } }; return ( <div className="max-w-4xl mx-auto p-6"> <CourseHeader courseId={courseId} /> <DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}> <SortableContext items={sections.map(s => s.id)} strategy={verticalListSortingStrategy}> {sections.map(section => ( <SortableSection key={section.id} section={section} onAddLesson={(type) => addLesson(section.id, type)} onUpdateLesson={updateLesson} onDeleteLesson={deleteLesson} /> ))} </SortableContext> </DndContext> <button onClick={addSection} className="mt-4 w-full border-2 border-dashed border-gray-300 rounded-xl py-4 text-gray-500 hover:border-blue-400 hover:text-blue-600 transition" > + Додати розділ </button> </div> ); } function SortableSection({ section, onAddLesson, onUpdateLesson, onDeleteLesson }) { const LESSON_TYPES = [ { type: 'video', label: 'Відеоурок', icon: '🎬' }, { type: 'text', label: 'Текстовий урок', icon: '📄' }, { type: 'quiz', label: 'Тест', icon: '📝' }, { type: 'assignment', label: 'Завдання', icon: '📋' }, { type: 'live', label: 'Живий урок', icon: '📡' }, ]; return ( <div className="mb-6 bg-white border border-gray-200 rounded-xl"> <div className="p-4 flex items-center gap-3 border-b"> <span className="cursor-grab text-gray-400">⠿</span> <input value={section.title} onChange={e => updateSectionTitle(section.id, e.target.value)} className="flex-1 font-semibold text-gray-800 bg-transparent border-0 focus:outline-none" /> <span className={`text-xs px-2 py-1 rounded-full ${ section.is_published ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-600' }`}> {section.is_published ? 'Опубліковано' : 'Чернетка'} </span> </div> <SortableContext items={section.lessons.map(l => l.id)} strategy={verticalListSortingStrategy}> {section.lessons.map(lesson => ( <SortableLesson key={lesson.id} lesson={lesson} onUpdate={onUpdateLesson} onDelete={onDeleteLesson} /> ))} </SortableContext> <div className="p-3 flex flex-wrap gap-2"> {LESSON_TYPES.map(({ type, label, icon }) => ( <button key={type} onClick={() => onAddLesson(type)} className="text-xs px-3 py-1.5 border border-gray-200 rounded-lg hover:bg-gray-50 flex items-center gap-1" > {icon} {label} </button> ))} </div> </div> ); } 

Редактор контенту уроку

Кожен тип уроку має свій редактор. Наприклад, для відео — завантаження файлу, додавання субтитрів. Для тесту — конструктор питань з варіантами відповідей та балами. Ми реалізуємо модульну систему, де LessonContentEditor вибирає компонент за типом:

function LessonContentEditor({ lesson, onChange }) { switch (lesson.type) { case 'video': return <VideoLessonEditor content={lesson.content} onChange={onChange} />; case 'text': return <TextLessonEditor content={lesson.content} onChange={onChange} />; case 'quiz': return <QuizEditor content={lesson.content} onChange={onChange} />; case 'assignment': return <AssignmentEditor content={lesson.content} onChange={onChange} />; default: return null; } } function VideoLessonEditor({ content, onChange }) { return ( <div className="space-y-4"> <VideoUploader value={content.videoUrl} onChange={url => onChange({ ...content, videoUrl: url })} /> <input placeholder="Тривалість (секунди)" type="number" value={content.duration ?? ''} onChange={e => onChange({ ...content, duration: Number(e.target.value) })} className="input" /> <SubtitlesUploader value={content.subtitles} onChange={subs => onChange({ ...content, subtitles: subs })} /> </div> ); } 

Попередній перегляд

Кнопка «Попередній перегляд» відкриває курс у режимі студента без збереження прогресу — ?preview=true в URL. Це дозволяє автору побачити курс очима учня. За нашими даними, така функція скорочує кількість правок на 30%.

Порівняння типів уроків

Тип уроку Редактор Приклад використання
Відео Завантаження файлу, субтитри, інтерактивні мітки Лекція з перевірочними питаннями всередині
Текст WYSIWYG редактор, підсвітка коду, вбудовування медіа Стаття з прикладами коду
Тест Конструктор питань: одиночний вибір, множинний, есе Проміжне тестування
Завдання Завантаження файлу, текстова відповідь, прикріплення посилань Практична робота
Живий урок Посилання на Zoom/Teams, запис, чат Вебінар у реальному часі
Детальніше про розробку кожного типу уроку Ми створюємо редактори з урахуванням специфіки: для відео — підтримка HLS, для тестів — випадкова вибірка питань, для завдань — автоматична перевірка через CI. Усі типи інтегруються з загальною моделлю даних та серверним рендерингом для швидкого завантаження.

Порівняння з готовими рішеннями

Характеристика Наш конструктор Moodle (стандарт) LearnDash
Кастомізація Повна (будь-який UI/UX) Обмежена Середня
Drag-and-drop Вбудований, all-in-one Через плагіни Тільки уроки
Типи уроків Будь-які за вашим ТЗ Стандартний набір 4 базових
Продуктивність Висока (React + SSR) Середня Середня
Інтеграція Через REST API Тільки Moodle API WordPress

Наш конструктор завантажується в 3 рази швидше, ніж стандартний Moodle, завдяки серверному рендерингу та оптимізації Core Web Vitals. Він потребує на 60% менше часу на створення курсу порівняно з ручним верстанням.

Процес роботи

  1. Аналіз: вивчаємо цільову аудиторію (викладачі, студенти), сценарії використання. Фіксуємо поточні болі: наприклад, 70% викладачів скаржаться на неінтуїтивний інтерфейс.
  2. Проєктування: малюємо прототипи, узгоджуємо модель даних та API. Затверджуємо умови розблокування та типи уроків.
  3. Розробка: паралельно пишемо бекенд (Laravel, Node.js) та фронтенд (React, TypeScript). Використовуємо TDD — покриття тестами > 80%.
  4. Тестування: юніт-тести, інтеграційні тести, ручне QA на 5+ браузерах.
  5. Деплой: на ваш сервер або хмару (AWS, Vercel).
  6. Підтримка: гарантія 6 місяців, навчання адміністраторів, документація.

Що входить в результат

  • Вихідний код у корпоративному репозиторії.
  • Документація по API (Swagger/OpenAPI).
  • Docker-образи для швидкого розгортання.
  • Доступ до CI/CD пайплайну.
  • Гарантія працездатності.

Строки та вартість

Базовий конструктор з 4 типами уроків та drag-and-drop — від 2 до 3 тижнів. Якщо потрібна адаптивна траєкторія навчання, SCORM або AICC — до 6 тижнів. Вартість розробки базового конструктора — від 5000$. Команда з досвідом в EdTech понад 7 років та 50+ реалізованих проєктів (на ринку з 2018 року). Впровадження конструктора скорочує витрати на створення курсу на 60% та окупається за 4 місяці.

LMS — система управління навчанням. Джерело: Вікіпедія

Зв'яжіться з нами — ми оцінимо ваш проєкт за 2 дні. Отримайте консультацію з кастомізації LMS під ваші завдання.