{% extends 'admin/base.html.twig' %}
{% block title %}Учищиеся{% endblock %}
{% block content %}
<!-- Page Heading -->
<h1 class="h3 mb-2 text-gray-800">Просмотр учащихся</h1>
{# <p class="mb-4">DataTables is a third party plugin that is used to generate the demo table below.
For more information about DataTables, please visit the <a target="_blank"
href="https://datatables.net">official DataTables documentation</a>.</p> #}
<!-- DataTales Example -->
<div class="card shadow mb-4 mt-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Учащиеся</h6>
</div>
<div class="card-body">
{% if getUser() and getUser().getSettings().isCanEditStudents() %}
{% include 'components/add_item_button.html.twig' with { options: {
url: path('moderator_learner_edit'),
title: 'Новый учащийся'
} } %}
{% endif %}
<div id="students-table-filters" class="mb-4 d-flex align-items-center" style="gap: 20px; flex-wrap: wrap;">
{% include 'components/table_filter.html.twig' with { options: {
labelText: 'Уровень',
dropdownIdPrefix: 'level',
allItemsButtonText: 'Все уровни',
}} %}
{% include 'components/table_filter.html.twig' with { options: {
labelText: 'Статус',
dropdownIdPrefix: 'status',
allItemsButtonText: 'Все статусы',
}} %}
{% include 'components/table_filter.html.twig' with { options: {
labelText: 'Контакт Telegram найден',
dropdownIdPrefix: 'telegramUserIdSet',
allItemsButtonText: 'Не выбрано',
}} %}
{# Добавлен фильтр по области (city.region) #}
{% include 'components/table_filter.html.twig' with { options: {
labelText: 'Область',
dropdownIdPrefix: 'cityRegion',
allItemsButtonText: 'Все области',
}} %}
{% if getUser() and getUser().settings.canUseTestFeatures %}
{# <div>#}
{# {% embed 'components/dropdown.html.twig' with { options: {#}
{# labelAttrs: 'style="display: block"',#}
{# dropdownMenuAttrs: 'style="width: 500px; max-height: 600px; overflow-y: auto;"',#}
{# defaultItemText: "Выбрать",#}
{# labelText: "Зарегистрирован на мероприятие:",#}
{# buttonClass: 'btn-primary btn',#}
{# preventDropdownClose: true,#}
{# } } %}#}
{# {% block beforeDropdownItems %}#}
{# <li class="px-3 py-2">#}
{# Перечисляем фильтры для каждого календарного события: статичные элементы Да/Нет #}
{% for ceId, ce in calendarEvents %}
{% include 'components/table_filter.html.twig' with { options: {
labelText: 'Зарегистрирован: ' ~ ce.getText(),
dropdownIdPrefix: 'calendarEvent_' ~ ceId,
allItemsButtonText: 'Не выбрано',
multipleSelect: false,
itemsHtml: '<a class="dropdown-item" href="#" data-filter="Да">Да</a><a class="dropdown-item" href="#" data-filter="Нет">Нет</a>',
}} %}
{# <br>#}
{% endfor %}
{# {% endblock %}#}
{# {% endembed %}#}
{% endif %}
{# </div>#}
</div>
<div class="border p-3 rounded">
<h4>Общая статистика:</h4>
<div class="d-flex">
<div class="p-2">
{% for status, itemsByStatusCountItem in itemsByStatusCount %}
<div>
<label>
<input type="checkbox" data-calc-item-name="{{ status }}" data-calc-item-value="{{ itemsByStatusCountItem.count }}">
Всего со статусом <b>"{{ itemsByStatusCountItem.statusText }}"</b>: {{ itemsByStatusCountItem.count }}
</label>
</div>
{% endfor %}
</div>
<div class="p-2">
{% for level, itemsByStudentLevelCountItem in itemsByStudentLevelCount %}
<div>
<label>
<input type="checkbox" data-calc-item-name="level_{{ level }}" data-calc-item-value="{{ itemsByStudentLevelCountItem.count }}">
Всего c уровнем <b>"{{ itemsByStudentLevelCountItem.level }}"</b>: {{ itemsByStudentLevelCountItem.count }}
</label>
</div>
{% endfor %}
</div>
</div>
<div class="p-2">
<div>Всего учеников: {{ allStudentsCount }}</div>
<div>Всего числящихся учеников: {{ itemsWithCanSendCampaignStatusCount }}</div>
<div>Всего подписчиков: {{ itemsByStatusCount['subscriber_virtual_status']['count'] }}</div>
<div>Всего выбывших: {{ allLeavedStudentsCount }}</div>
<br>
<div>
Калькулятор: <span class="calc-result"><b>выберите значения</b></span>
</div>
</div>
</div>
<br>
{# <div>#}
{# Тексты писем:#}
{# </div>#}
{# <script>#}
{# let msg = null;#}
{# </script>#}
{# {% set i = -1 %}#}
{# {% set emails = "hh099@ya.ru\n" %}#}
{# {% for item in items %}#}
{# {% set i = i + 1 %}#}
{# {% if item.virtualStudentStatus != "subscriber" %}#}
{# <div>#}
{# <button class="copy-to-buffer" data-name="{{ item.name }}"#}
{# data-email="{{ item.email }}"#}
{# >{{ i + 1 }} - id: {{ item.id}} - {{ item.name }} {{ item.email }} - {{ item.virtualStudentStatus }}</button>#}
{# </div>#}
{# <br>#}
{# {% if item.id >= 328 and item.email %}#}
{# {% set emails = emails ~ item.email ~ "\n" %}#}
{# {% endif %}#}
{# {% endif %}#}
{# {% endfor %}#}
{# <textarea id="emails-textarea" class="form-control" rows="10" placeholder="Сюда будут скопированы все email обучающихся, не являющихся подписчиками, при нажатии на кнопку выше">{{ emails }}</textarea>#}
{# <script>#}
{# $('.copy-to-buffer').click(function () {#}
{# let btn = $(this)[0];#}
{# let name = $(this).data('name');#}
{# let email = $(this).data('email');#}
{# let msg = `${name}, добрый день! Отправляю информацию чтобы Телеграм работал.#}
{#Ссылка для телефонов Android (Samsung, Xiaomi, Huawei и др.):#}
{#https://play.google.com/store/apps/details?id=com.adguard.vpn#}
{#Программа для компьютера Windows AdGuard прикреплена к письму.`;#}
{# msg = email;#}
{# navigator.clipboard.writeText(msg).then(() => {#}
{# // alert('Текст скопирован в буфер обмена');#}
{# //change button class to primarey#}
{# $(btn).removeClass('btn-secondary').addClass('btn-primary');#}
{# }).catch(err => {#}
{# alert('Ошибка при копировании текста: ' + err);#}
{# });#}
{# });#}
{# </script>#}
<div class="mb-4 d-flex justify-content-end">
<button id="compare-table-btn" class="btn btn-primary mb-3">Поиск по списку ФИО</button>
</div>
<div class="table-responsive">
<table class="table table-bordered" id="students-data-table">
<thead>
<tr>
<th data-table-type="num">Номер п/п</th>
<th>Имя</th>
<th>Город</th>
<th data-table-dropdown-id="cityRegionDropdown"
data-table-badge-id="cityRegionBadge"
data-table-dropdown-text-for-all="Все области">Область</th>
<th>Телефон</th>
<th>E-mail</th>
<th data-table-dropdown-id="levelDropdown"
data-table-badge-id="levelBadge"
data-table-dropdown-text-for-all="Все уровни">Уровень</th>
<th data-export-exclude-col>Статус</th>
<th data-table-hide-col data-table-dropdown-id="statusDropdown"
data-table-badge-id="statusBadge"
data-table-dropdown-text-for-all="Все статусы">Статус (текст)</th>
<th data-export-exclude-col>Фото</th>
<th data-table-type="date-d-m-Y-asc">Дата последнего прохождния курса уровня</th>
<th>Комментарий</th>
{# Для каждого календарного события добавляем скрытый столбец (значение Да/Нет). #}
{% for ceId, ce in calendarEvents %}
<th data-table-hide-col
data-table-dropdown-id="calendarEvent_{{ ceId }}Dropdown"
data-table-badge-id="calendarEvent_{{ ceId }}Badge"
data-table-dropdown-text-for-all="Не выбрано"
data-export-exclude-col>{{ ce.getText() }}</th>
{% endfor %}
<th data-table-hide-col data-export-exclude-col>Идентификатор пользователя Telegram</th>
<th data-table-hide-col data-export-exclude-col
data-table-dropdown-id="telegramUserIdSetDropdown"
data-table-badge-id="telegramUserIdSetBadge"
data-table-dropdown-text-for-all="Не выбрано"
>Идентификатор пользователя Telegram установлен (да/нет)</th>
<th>Сведения</th>
<th data-export-exclude-col>Действия</th>
<th data-table-type="num" data-table-hide-col data-table-col-id="id">#</th>
<th data-table-hide-col>Дополнить комментарий учащегося</th>
</tr>
</thead>
{# <tfoot>
<tr>
<th>Имя</th>
<th>Уровень</th>
</tr>
</tfoot> #}
<tbody>
{% set studentIndex = -1 %}
{% for item in items %}
{% set studentIndex = studentIndex + 1 %}
{% set earnLevelCandidate = earnLevelCandidates[item.id] %}
<tr>
<td>{{ studentIndex + 1 }}</td>
<td>{{ getFirstLastNameWithOld(item, false, true) }}</td>
<td>{{ item.city ? item.city.name : "город не указан" }}</td>
<td>{{ item.city and item.city.region ? item.city.region.name : "область не указана" }}</td>
<td>{{ item.phone }}</td>
<td>{{ item.email }}</td>
<td>{{ item.student and item.student.level > -1 ? item.student.level : "нет уровня" }}</td>
<td>
<span href="#" class="btn btn-{{ item.virtualStudentStatusCssClass }} btn-icon-split w-max cursor-default">
<span class="icon text-white-50">
<i class="fas fa-tag"></i>
</span>
<span class="text">{{ item.virtualStudentStatusText }}</span>
</span>
</td>
<td>{{ item.virtualStudentStatusText }}</td>
<td>
{# {% if item.image %}#}
{# <img src="{{ path('image_view', {id: item.image.id}) }}"#}
{# alt="Фото {{ item.name }}"#}
{# class="cursor-pointer gallery-trigger"#}
{# style="max-width: 100px; max-height: 100px;"#}
{# data-gallery-index="{{ studentIndex }}"#}
{# data-gallery-modal-id="studentsPhotoGalleryModal"#}
{# >#}
{# {% else %}#}
{# –#}
{# {% endif %}#}
</td>
<td>
{% set outputEarnLevelCandidate = item.student and earnLevelCandidate and earnLevelCandidate.calendarEvent.earnLevel == item.student.level %}
{% if outputEarnLevelCandidate %}
{{ earnLevelCandidate.calendarEvent.startDate|date('d.m.Y') }}
{% else %}
–
{% endif %}
</td>
<td>{{ item.comment ?: "–" }}</td>
{# Для каждого события выводим 'Да' если есть кандидат для пары студент-событие, иначе 'Нет' #}
{% for ceId, ce in calendarEvents %}
{% set registered = item.student and ((candidatesByStudentAndCalendarEvent[item.student.id] is defined) and (candidatesByStudentAndCalendarEvent[item.student.id][(ceId)] is defined)) %}
<td>{{ registered ? 'Да' : 'Нет' }}</td>
{% endfor %}
<td>{{ item.telegramUserId ?: "–" }}</td>
<td>
{% set statusAdditon = item.canSendTelegramCampaigns ? "" : " (Рассылка Telegram отключена!)" %}
{{ item.telegramUserId ? 'Да' ~ statusAdditon : 'Нет' ~ statusAdditon }}
</td>
<td>
<b>Дата рождения</b>: {{ item.birthday ? item.birthday|date('d.m.Y') : "–" }}
<br><br>
<b>Род деятельности</b>: {{ item.job ? item.job.name : "–" }}
</td>
<td>
<a title="Редактировать" href="{{ path('moderator_learner_edit', {id: item.id}) }}" class="btn btn-primary btn-circle">
<i class="fas fa-edit"></i>
</a>
{% if getUser() and getUser().getSettings().isCanEditStudents() %}
<a title="Удалить" href="javascript:void(0);" class="btn btn-danger btn-circle"
onclick='(async ()=>{
let msg = "Удалить учащегося {{ (item.name)|e('js') }}?";
let answer = await showWindowModal("Подтверждение", msg, "Удалить", "Отмена");
if (answer === MODAL_WINDOW_BUTTON.BUTTON_1) {
window.location = "{{ path('moderator_student_delete', {id: item.id}) }}";
}
})()'>
<i class="fas fa-trash"></i>
</a>
{% endif %}
</td>
<td>{{ item.id }}</td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
{% set galleryItems = items %}
{% embed 'components/image_carousel_modal/image_carousel_modal.html.twig' with { options: {
modalId: 'studentsPhotoGalleryModal',
modalTitle: 'Галерея учащихся',
elementClickTriggerSelector: '#students-data-table img.gallery-trigger',
} } %}
{% block slides %}
{% set galleryIndex = -1 %}
{% for galleryItem in galleryItems %}
{% set galleryIndex = galleryIndex + 1 %}
{# {% set slideText = 'Фото: ' ~ galleryItem.name %}#}
{# {% embed 'components/image_carousel_modal/gallery_slide.html.twig' with { options: {#}
{# imageIndex: galleryIndex,#}
{# imageUrl: galleryItem.image ? path('image_view', {id: galleryItem.image.id}),#}
{# attrs: 'data-student-id="' ~ galleryItem.id ~ '"',#}
{# } } %}#}
{# {% block text %}#}
{# <div><b>Имя:</b> {{ galleryItem.name }}</div>#}
{# <div><b>Город:</b> {{ galleryItem.city ? galleryItem.cityText : '–' }}</div>#}
{# <div><b>Уровень:</b> {{ galleryItem.student ? galleryItem.student.levelText : "нет уровня" }}</div>#}
{# <div class="mb-0"><b>Телефон:</b> {{ galleryItem.phone ?: '–' }}</div>#}
{# <div class="mb-1"><b>E-mail:</b> {{ galleryItem.email ?: '–' }}</div>#}
{# {% endblock %}#}
{# {% endembed %}#}
{% endfor %}
{% endblock %}
{% endembed %}
{# {% if getUser() and (getUser().getSettings().canUseTestFeatures()) %}#}
<div class="mt-4 d-flex">
<button id="download-csv-btn" class="btn btn-primary">Скачать таблицу</button>
</div>
{# {% endif %}#}
</div>
</div>
</div>
{% endblock %}
{% block addJs %}
<script>
let studentsDataTableOptions = {
// "scrollY": "500px",
"lengthMenu": [[10, 20, 50, 100, 200, 300, 500, 700, -1], [10, 20, 50, 100, 200, 300, 500, 700, "Все"]],
drawRowNumbersForColIndex: 0,
};
let table = new CustomDataTable('#students-data-table', studentsDataTableOptions);
table.initDataTable();
// compare table button - collect filtered table data and run compare
$('#compare-table-btn').click(async () => {
let result = await showTableCompareModal();
if (result !== null) {
let compareList = result.compareList;
let tableData = table.getAllFilteredTableData();
let tableList = "";
for (let i = 0; i < tableData.length; i++) {
tableList += tableData[i][1] + "\n";
}
let compareResult = await compareData(tableList, compareList);
const modalContent = getCompareTableHtml(tableData, compareResult, "students-data-table",
"#students-table-filters", studentsDataTableOptions);
let answer = await showWindowModal('Результаты поиска в таблице по списку ФИО', modalContent, "Ок", "Отмена",
"max-width: 95%;");
}
});
$('#download-csv-btn').click(() => {
table.downloadCsv()
});
let galleries = [];
$('.gallery-modal').each(function() {
let modalId = $(this).attr('id');
let options = {};
if ($(this).is("#studentsPhotoGalleryModal")) {
options = {
...options,
slideIdAttrName: 'data-student-id',
dataTableInstance: table,
}
}
let gallery = new GalleryCarousel(modalId, options);
galleries.push(gallery);
});
function updateCalc() {
const $span = $('.calc-result');
const $inputs = $('input[data-calc-item-name]');
let total = 0;
let anyChecked = false;
$inputs.each(function() {
const $el = $(this);
if ($el.is(':checked')) {
anyChecked = true;
const raw = $el.attr('data-calc-item-value');
const n = Number(String(raw).replace(/[^0-9.-]+/g, ''));
if (!isNaN(n)) total += n;
}
});
if (!anyChecked) {
$span.html('<b>выберите значения</b>');
} else {
$span.html('<b>' + total + '</b>');
}
}
// Привязываем событие и инициализируем при загрузке
$(document).ready(function() {
$(document).on('change', 'input[data-calc-item-name]', updateCalc);
updateCalc();
});
</script>
{% endblock %}