/* ===== Public screens part 2 ===== */
const D2 = window.DATA;
function lessonTitle(lesson){ return typeof lesson==='string' ? lesson : (lesson?.t || 'Урок'); }
const bundledOperatorCourseIds = [
'aeronavigation-bpla-part-1',
'aeronavigation-bpla-part-2',
'aviation-meteorology-part-1',
'aviation-meteorology-part-2',
'kyrgyz-republic-legal-foundations-part-1',
'kyrgyz-republic-legal-foundations-part-2',
'uav-fpv-systems-part-1',
'uav-fpv-systems-part-2',
'first-aid-online',
];
function PublicArticleSkeleton({ showCover=true, showSideCards=2 }){
return (
{showCover &&
}
{Array.from({length:showSideCards}).map((_, index)=>(
))}
);
}
function PublicLandingSkeleton(){
return (
{Array.from({length:2}).map((_, index)=>(
))}
{Array.from({length:3}).map((_, index)=>(
))}
);
}
function PublicCatalogSkeleton({ cards=6, showFilters=true, showSearch=true, columnsClass='g3', showLead=false, leadSide=false }){
return (
{(showFilters || showSearch) && (
{showFilters && (
{Array.from({length:showLead ? 5 : 6}).map((_, index)=>
)}
)}
{showSearch &&
}
)}
{showLead && (
)}
{Array.from({length:cards}).map((_, index)=>(
))}
);
}
function PublicCourseDetailSkeleton(){
return (
{Array.from({length:3}).map((_, index)=>(
{Array.from({length:index===1 ? 3 : 4}).map((__, row)=>(
))}
))}
);
}
function PublicContactsSkeleton(){
return (
{Array.from({length:5}).map((_, index)=>(
))}
);
}
function PublicRacesSkeleton(){
return (
{Array.from({length:3}).map((_, index)=>(
))}
{Array.from({length:4}).map((_, index)=>(
))}
);
}
/* ---------------- COURSE HERO IMAGE CAROUSEL ---------------- */
function CourseHeroCarousel({ slides }){
const [cur, setCur] = useState(0);
const n = slides.length;
useEffect(()=>{
if(n < 2) return;
const timer = setInterval(()=>setCur(i => (i + 1) % n), 7000);
return ()=>clearInterval(timer);
},[n]);
const prev = () => setCur(i => (i - 1 + n) % n);
const next = () => setCur(i => (i + 1) % n);
return (
{slides.map((src, i) => (
))}
{n > 1 && (
<>
{slides.map((_, i) => (
setCur(i)} aria-label={`Слайд ${i+1}`}/>
))}
>
)}
);
}
/* ---------------- COURSE CAROUSEL ---------------- */
function CourseCarousel({ courses, go, enroll=[] }){
const trackRef = useRef(null);
function scroll(dir){
if(trackRef.current) trackRef.current.scrollBy({left: dir * 340, behavior:'smooth'});
}
return (
scroll(-1)} aria-label="Назад">
{courses.map(rc=>(
item.id===rc.id)}/>
))}
scroll(1)} aria-label="Вперёд">
);
}
/* ---------------- EDUCATION CATALOG ---------------- */
function Education({ go, enroll=[] }){
const cats = ['Актуальные','Оператор','Внешний пилот','Инженер','Аналитик данных','Продакшен','Сельхоз','Спорт'];
const featuredIds = ['operator-bpla','engineer-pilot-junior'];
const [active, setActive] = useState('Актуальные');
const [q, setQ] = useState('');
const baseCourse = D2.courses.find(c => c.id === 'certificate-operator');
const baseEnrollment = baseCourse ? enroll.find(item=>item.id===baseCourse.id) : null;
const list = D2.courses.filter(c =>
c.active !== false &&
c.cat !== 'База' &&
(active==='Актуальные' ? featuredIds.includes(c.id) : c.cat===active) &&
(c.title.toLowerCase().includes(q.toLowerCase()) || c.short.toLowerCase().includes(q.toLowerCase()))
);
return (
Образование
Каталог курсов Лаборатории БПЛА
Выберите программу под свою цель. Каждый курс — это онлайн-материалы, практика и аттестация с сертификатом.
{baseCourse && (
go('course', baseCourse.id)} role="button" tabIndex={0}>
БАЗА · С чего начать {baseEnrollment?.access==='open'&& Курс куплен }{baseEnrollment?.access==='pending'&& Оплата проверяется }
{baseCourse.title}
{baseCourse.short}
{baseCourse.duration}
{baseCourse.level}
{D2.fmt(baseCourse.price)} сом
{e.stopPropagation();go(baseEnrollment?.access==='open'?'player':'course', baseCourse.id);}}>
{baseEnrollment?.access==='open'?'Перейти к обучению':'Начать с базы'}
)}
{list.map(c=>(item.id===c.id)}/>))}
{list.length===0 &&
Ничего не найдено. Попробуйте изменить запрос.
}
);
}
/* ---------------- SERVICES ---------------- */
function ServicesPage({ go }){
const categories = D2.serviceCategories || [];
const services = D2.services || [];
const scrollCategory = slug=>document.getElementById(`services-${slug}`)?.scrollIntoView({behavior:'smooth',block:'start'});
return (
Цифровая трансформация
Услуги Лаборатории БПЛА
Применяем беспилотные технологии, лазерное сканирование, ИИ-аналитику и 3D-моделирование для бизнеса и государственных организаций.
go('contacts')}>Обсудить задачу
6 направлений
Решения под задачи вашей отрасли
{categories.map((category,index)=>
scrollCategory(category.slug)}>
{String(index+1).padStart(2,'0')}
{category.name} {category.short_description}
)}
{categories.map((category,index)=>{
const items=services.filter(item=>item.category_id===category.id);
if(!items.length) return null;
return
{String(index+1).padStart(2,'0')} {category.name} {category.short_description}
{items.map(item=>
go('service-detail',item.slug)}>
{category.name} {item.name} {plainTextFromHtml(item.description)}
{item.price} go('service-detail',item.slug)}>Подробнее
)}
;
})}
);
}
function ServiceOrderForm({ service }){
const [form,setForm]=useState({name:'',phone:''});
const [submitting,setSubmitting]=useState(false);
const [status,setStatus]=useState({type:'',text:''});
const update=(field,value)=>setForm(current=>({...current,[field]:value}));
async function submit(event){
event.preventDefault();
if(!form.name.trim()||!form.phone.trim())return setStatus({type:'error',text:'Заполните имя и телефон.'});
setSubmitting(true);setStatus({type:'',text:''});
try{
const res=await fetch('api/contact-form.php',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:form.name.trim(),contact:form.phone.trim(),message:`Заявка на услугу «${service.name}». Прошу связаться со мной для уточнения задачи.`,service_title:service.name,service_slug:service.slug})});
const data=await res.json().catch(()=>({ok:false,error:'Пустой ответ сервера'}));
if(!res.ok||data.ok===false)throw new Error(data.error||`HTTP ${res.status}`);
setForm({name:'',phone:''});setStatus({type:'success',text:'Заявка отправлена. Мы свяжемся с вами.'});
}catch(error){setStatus({type:'error',text:error.message||'Не удалось отправить заявку.'});}finally{setSubmitting(false);}
}
return ;
}
function ServiceDetail({ slug, go }){
const service = (D2.services || []).find(item=>item.slug===slug);
if(!service) return Услуга не найдена Возможно, она ещё не опубликована.
go('services')}>Все услуги ;
const related=(D2.services || []).filter(item=>item.category_id===service.category_id && item.id!==service.id).slice(0,3);
const gallery=[service.main_image,...(service.gallery||[]).map(item=>item.image)].filter(Boolean);
const openOrderForm=()=>document.getElementById('service-order-form')?.scrollIntoView({behavior:'smooth',block:'center'});
return
go('home')}>Главная go('services')}>Услуги {service.name}
{service.category_name} {service.name} {plainTextFromHtml(service.description)}
Оставить заявку go('services')}>Все услуги
Для кого Кому подходит услуга {service.audience}
Об услуге Что мы делаем
Результат Что получает заказчик {service.customer_result}
{gallery.length>1 &&
Галерея {gallery.map((image,index)=>
)}
}
{related.length>0 &&
Другие услуги категории {related.map(item=>
go('service-detail',item.slug)}> {item.name} {plainTextFromHtml(item.description)}
go('service-detail',item.slug)}>Подробнее )}
}
;
}
/* ---------------- PUBLIC TRIAL LESSON ---------------- */
function TrialLesson({ go, loggedIn=false, enroll=[] }){
const [completed,setCompleted] = useState(false);
const [activeMaterial,setActiveMaterial] = useState('pdf');
const course = D2.courses.find(item=>item.id==='operator-bpla');
const price = Number(course?.price || 8900);
const courseEnrollment=enroll.find(item=>item.id==='operator-bpla');
const coursePurchased=courseEnrollment?.access==='open';
const coursePending=courseEnrollment?.access==='pending';
const buyCourse = ()=>go(coursePurchased?'player':'payment', 'operator-bpla');
const trialMaterials = [
{key:'pdf',label:'PDF',icon:'doc',kind:'pdf',src:'api/trial-lesson-media.php?type=pdf',title:'Презентация PDF'},
{key:'audio',label:'Аудио',icon:'play',kind:'audio',src:'api/trial-lesson-media.php?type=audio',title:'Аудиолекция'},
{key:'presentation',label:'PPTX',icon:'layers',kind:'pdf',src:'api/trial-lesson-media.php?type=presentation',title:'Презентация PowerPoint'},
];
const selectedMaterial = trialMaterials.find(item=>item.key===activeMaterial) || null;
return (
go('home')}> На главную
Бесплатный урок
Без регистрации
Урок 1. Основы аэронавигации: как беспилотник ориентируется в пространстве
Посмотрите первый урок программы «Оператор БПЛА» бесплатно. Вы узнаете, какие системы помогают дрону определять положение, направление и высоту полёта.
Видеоурок
PDF и презентация
Аудиолекция
Начальный уровень
Свободный доступ
Пробный урок
Основы аэронавигации
Бесплатно
setCompleted(true)}
>
Ваш браузер не поддерживает воспроизведение видео.
{completed && (
Первый урок пройден!
Продолжите обучение на полном курсе «Оператор БПЛА»: 24 часа онлайн-занятий, симулятор, практика с инструктором и сертификат КРСУ.
{D2.fmt(price)} сом
{coursePurchased?'Перейти к обучению':coursePending?'Оплата проверяется':'Купить полный курс'}
setCompleted(false)}>Посмотреть ещё раз
)}
Нажмите «Воспроизвести» — вход и покупка для просмотра этого урока не нужны.
Материалы урока
PDF, аудио и презентация
{trialMaterials.map(item=>(
setActiveMaterial(item.key)}>
{item.label}
))}
{selectedMaterial &&
{selectedMaterial.title}
Без регистрации
{selectedMaterial.kind==='audio'
?
:
}
}
Продолжить обучение
Полный курс «Оператор БПЛА»
Комплексная подготовка от устройства и навигации беспилотника до безопасного пилотирования и практической школы «Высота» на Иссык-Куле.
{[
['clock','32 часа обучения'],
['video','50 видеоуроков'],
['users','Практика с инструктором'],
['award','Сертификат КРСУ'],
].map(([icon,label])=>
{label}
)}
);
}
/* ---------------- COURSE DETAIL ---------------- */
function CourseDetail({ id, go, loggedIn=false, enroll=[] }){
const requestedCourse = D2.courses.find(x=>x.id===id);
const c = bundledOperatorCourseIds.includes(id) || requestedCourse?.active===false
? (D2.courses.find(x=>x.id==='operator-bpla') || requestedCourse || D2.courses[0])
: (requestedCourse || D2.courses[0]);
const catClass = `course-hero--${(c.cat||'').toLowerCase().replace(/\s+/g,'-')}`;
const [open, setOpen] = useState(0);
const enrollment=enroll.find(item=>item.id===c.id);
const purchased=enrollment?.access==='open';
const pending=enrollment?.access==='pending';
const related = D2.courses.filter(x=>x.active!==false && x.id!==c.id && x.cat===c.cat).slice(0,3);
const rel = related.length?related:D2.courses.filter(x=>x.active!==false && x.id!==c.id).slice(0,3);
return (
go('education')}> Все курсы
{c.cat}
{c.level}
{purchased&& Курс куплен }
{!purchased&&pending&& Оплата проверяется }
{c.title}
{c.short}
{c.lessons} уроков материалов
{(c.slides && c.slides.length > 0)
?
: c.img
?
: }
{!(c.slides && c.slides.length > 0) && }
Чему вы научитесь
{c.outcomes.map(o=>(
{o}
))}
Программа курса
{c.modules.map((m,i)=>(
setOpen(open===i?-1:i)}>
{String(i+1).padStart(2,'0')}
{m.t}
{m.lessons.length} уроков
{m.lessons.map((l,j)=>(
{lessonTitle(l)}
))}
))}
Нужна консультация перед покупкой?
Поможем выбрать курс под вашу цель и расскажем об условиях.
go('contacts')}>Связаться
{/* sticky buy */}
);
}
/* ---------------- NEWS ---------------- */
function News({ go }){
const cats = ['Все','Лаборатория БПЛА','Партнёры','Беспилотная Авиация','Гонки','Наука-образование'];
const [active, setActive] = useState('Все');
const list = active==='Все'?D2.news:D2.news.filter(n=>n.cat===active);
const lead = list[0];
const leadImg = lead?.img || 'assets/pic_bg1.png';
return (
Новости
Единое информационное окно
Новости КРСУ, партнёров и профильных госпорталов — включая Госагентство гражданской авиации — в одной ленте.
{cats.map(c=>(setActive(c)}>{c} ))}
{lead && (
go('news-item', newsRouteParam(lead))} style={{cursor:'pointer'}}>
{lead.cat} {lead.date}
{lead.title}
{excerptFromHtml(lead.text, 240)}
Источник: {lead.src}
Читать полностью
)}
{list.slice(1).map(n=>())}
);
}
function NewsDetail({ id, go }){
const article = findNewsByParam(id, D2.news) || D2.news[0];
const related = D2.news.filter(item=>item.id!==article?.id).slice(0,3);
if(!article){
return (
);
}
return (
go('news')}> Все новости
{article.cat}
{article.date}
{article.title}
Источник: {article.src}
);
}
function RaceDetail({ id, go, loggedIn=false }){
const race = findRaceByParam(id, D2.races) || D2.races[0];
const related = D2.races.filter(item=>item.id!==race?.id).slice(0,3);
if(!race){
return (
);
}
return (
go('races')}> Все старты
{race.status}
{race.date}
{race.title}
{race.place}
{race.img && (
)}
Участие
Регистрация и доступ к материалам соревнований доступны через личный кабинет.
go('payment','league-membership')}>
{loggedIn ? 'Оплатить участие' : 'Войти и оплатить'}
{!!related.length && (
Другие старты
{related.map(item=>(
go('race-item', raceRouteParam(item))}>
{item.date}
{item.title}
))}
)}
);
}
function StaticContentPage({ id, go }){
const pages = D2.pages || {};
const page = pages[String(id || '')] || pages.offer || { title:'Страница', text:'Контент страницы пока не заполнен.
' };
const navItems = [
{id:'offer', title:'Договор оферты'},
{id:'payment', title:'Условия оплаты'},
{id:'privacy', title:'Политика конфиденциальности'},
];
return (
go('home')}> На главную
Документы
{page.title}
Актуальная версия страницы публикуется и редактируется прямо из административной панели сайта.
{(page.files||[]).length>0 && (
)}
{page.text && page.text.trim() &&
}
);
}
function PilotRatingPage({ go }){
const rating = D2.pilotRating || {};
const entries = (rating.entries || [])
.filter(item=>item.active !== false)
.sort((a,b)=>{
const pointsDifference = (Number(b.points) || 0) - (Number(a.points) || 0);
if(pointsDifference !== 0) return pointsDifference;
return (Number(a.sort_order) || 0) - (Number(b.sort_order) || 0);
});
const top = entries.slice(0,3);
return (
go('races')}> К гонкам
Спортивный рейтинг
{rating.title || 'Рейтинг пилотов'}
{rating.subtitle || 'Таблица лучших пилотов Лаборатории БПЛА и результаты последних стартов.'}
{rating.season || 'Сезон'}
Обновлено: {rating.updatedAt || '—'}
{top.map((pilot, index)=>(
{index + 1}
{pilot.photo
?
:
}
{pilot.name}
{pilot.team || 'Лаборатория БПЛА'}
{pilot.city && {pilot.city} }
{pilot.drone && {pilot.drone} }
{pilot.points} очков
Побед: {pilot.wins}
Лучший круг: {pilot.bestLap || '—'}
))}
Место
Пилот
Команда
Город
Очки
Победы
Лучший круг
{entries.map((pilot, index)=>(
{index + 1}
{pilot.photo ? : } {pilot.name} {pilot.drone ? {pilot.drone} : null}
{pilot.team || '—'}
{pilot.city || '—'}
{pilot.points}
{pilot.wins}
{pilot.bestLap || '—'}
))}
{!entries.length &&
Рейтинг пока не заполнен.
}
);
}
function LeagueMembershipPage({ go, loggedIn=false }){
const league = D2.leagueMembership || {};
const yearlyPrice = Number(league.yearlyPrice || 8760);
const features = league.features || [];
return (
go('races')}> К гонкам
Лига дронов
{league.title || 'Членство в Лиге'}
{league.subtitle || 'Платная регистрация для участия в Лиге Дронов и официальных заездах.'}
Что входит
{league.title || 'Членство в Лиге'}
{league.description || 'Членство открывает доступ к официальным стартам, материалам сезона и уведомлениям Лиги.'}
{features.map(item=>(
{item}
))}
Годовая подписка
{D2.fmt(yearlyPrice)} сом
в год
go('payment','league-membership')}>{loggedIn ? (league.cta || 'Оформить членство') : 'Войти и оформить'}
);
}
/* ---------------- CONTACTS ---------------- */
function Contacts(){
const c = D2.contacts;
const [form,setForm] = useState({name:'',contact:'',message:''});
const [submitting,setSubmitting] = useState(false);
const [status,setStatus] = useState({type:'',text:''});
function updateForm(field, value){
setForm(prev=>({...prev,[field]:value}));
}
async function submitForm(e){
e.preventDefault();
const name = form.name.trim();
const contact = form.contact.trim();
const message = form.message.trim();
if(!name || !contact || !message){
setStatus({type:'error', text:'Заполните имя, контакт и сообщение.'});
return;
}
setSubmitting(true);
setStatus({type:'', text:''});
try{
const res = await fetch('api/contact-form.php', {
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({name, contact, message}),
});
const data = await res.json().catch(()=>({ok:false,error:'Пустой ответ сервера'}));
if(!res.ok || data.ok===false) throw new Error(data.error || `HTTP ${res.status}`);
setForm({name:'',contact:'',message:''});
setStatus({type:'success', text:data.message || 'Сообщение отправлено.'});
}catch(err){
setStatus({type:'error', text:err.message || 'Не удалось отправить сообщение.'});
}finally{
setSubmitting(false);
}
}
return (
Контакты
Свяжитесь с лабораторией
Телефоны {c.phones.map(p=>
{p}
)}
);
}
function MediaGallery({ go }){
const media = (D2.media || []).filter(item=>item.active);
return (
Медиа-галерея
Обучалки, полезные видео по настройке и рекламные материалы
Здесь собраны видео из Telegram и хостинга лаборатории для студентов, абитуриентов и партнёров.
go('education')}>К курсам
go('home')}>На главную
{media.map((v)=>(
{v.kind==='image'
?
:
}
{v.cat}
{v.title}
{v.text ? {v.text} : null}
))}
);
}
function Faq({ go }){
const [open,setOpen]=useState(null);
const items=(D2.faq||[]).filter(x=>x.active);
return (
FAQ
Часто задаваемые вопросы
Всё, что вы хотели узнать о программе подготовки операторов БПЛА в КРСУ.
{items.length===0 &&
Вопросы скоро появятся.
}
{items.map((item,i)=>{
const isOpen=open===i;
return (
setOpen(isOpen?null:i)}
aria-expanded={isOpen}
>
{item.q}
{isOpen && (
)}
);
})}
Не нашли ответ на свой вопрос?
Напишите нам — ответим в течение рабочего дня.
go('contacts')}>Написать в лабораторию
);
}
Object.assign(window, { Education, CourseDetail, CourseCarousel, CourseHeroCarousel, News, Contacts, MediaGallery, PilotRatingPage, LeagueMembershipPage, Faq });