Добавил запрос на создание препода от кафедры
This commit is contained in:
@@ -396,6 +396,37 @@
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.department-workspace-grid-wide {
|
||||
grid-template-columns: repeat(4, minmax(220px, 1fr));
|
||||
}
|
||||
|
||||
.teacher-request-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
#teacher-requests-tbody input,
|
||||
#teacher-requests-tbody select {
|
||||
min-width: 150px;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.65rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--bg-card-border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-primary);
|
||||
font: inherit;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
#teacher-requests-tbody input:focus,
|
||||
#teacher-requests-tbody select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-glow);
|
||||
}
|
||||
|
||||
.department-subject-import-card,
|
||||
.department-teachers-card {
|
||||
display: flex;
|
||||
@@ -696,6 +727,10 @@
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.department-workspace-grid-wide {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.academic-year-semesters {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@@ -109,15 +109,23 @@ function invalidateCache(url) {
|
||||
'/api/specialties',
|
||||
'/api/education-forms',
|
||||
'/api/users',
|
||||
'/api/teacher-requests',
|
||||
'/api/department/teacher-requests',
|
||||
'/api/department/teachers',
|
||||
'/api/equipments',
|
||||
'/api/classrooms'
|
||||
];
|
||||
for (const prefix of cacheKeysToClear) {
|
||||
if (url.includes(prefix)) {
|
||||
for (const cacheKey of cache.keys()) {
|
||||
if (cacheKey.includes(prefix)) {
|
||||
cache.delete(cacheKey);
|
||||
}
|
||||
const affectedPrefixes = new Set(cacheKeysToClear.filter(prefix => url.includes(prefix)));
|
||||
if (url.includes('/api/teacher-requests')) {
|
||||
affectedPrefixes.add('/api/users');
|
||||
}
|
||||
if (url.includes('/api/department/teachers')) {
|
||||
affectedPrefixes.add('/api/users/teachers');
|
||||
}
|
||||
for (const prefix of affectedPrefixes) {
|
||||
for (const cacheKey of cache.keys()) {
|
||||
if (cacheKey.includes(prefix)) {
|
||||
cache.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ export async function initDepartmentWorkspace() {
|
||||
const importForm = document.getElementById('department-subject-import-form');
|
||||
const importRowsContainer = document.getElementById('subject-import-rows');
|
||||
const importAddButton = document.getElementById('subject-import-add');
|
||||
const assignTeacherForm = document.getElementById('department-teacher-assign-form');
|
||||
const assignTeacherSelect = document.getElementById('department-teacher-select');
|
||||
const assignTeacherComment = document.getElementById('department-teacher-assign-comment');
|
||||
const teacherRequestForm = document.getElementById('department-teacher-request-form');
|
||||
const teacherRequestsTbody = document.getElementById('department-teacher-requests-tbody');
|
||||
const teacherRequestCount = document.getElementById('department-teacher-request-count');
|
||||
const subjectsTbody = document.getElementById('department-subjects-tbody');
|
||||
const subjectCount = document.getElementById('department-subject-count');
|
||||
const commentsCard = document.getElementById('department-comments-card');
|
||||
@@ -25,6 +31,8 @@ export async function initDepartmentWorkspace() {
|
||||
const commentsCloseButton = document.getElementById('department-comments-close');
|
||||
let lastTeachers = null;
|
||||
let lastWorkload = null;
|
||||
let allTeachers = [];
|
||||
let teacherRequests = null;
|
||||
let teachersErrorMessage = '';
|
||||
let workloadErrorMessage = '';
|
||||
|
||||
@@ -34,6 +42,8 @@ export async function initDepartmentWorkspace() {
|
||||
startInput?.addEventListener('change', handleStartDateChange);
|
||||
endInput?.addEventListener('change', loadWorkload);
|
||||
importForm?.addEventListener('submit', importSubjects);
|
||||
assignTeacherForm?.addEventListener('submit', assignTeacherToDepartment);
|
||||
teacherRequestForm?.addEventListener('submit', submitTeacherRequest);
|
||||
importAddButton?.addEventListener('click', () => renderImportRows([...readImportRows(), { code: '', name: '' }]));
|
||||
importRowsContainer?.addEventListener('click', (event) => {
|
||||
const removeBtn = event.target.closest('.subject-import-remove');
|
||||
@@ -61,6 +71,7 @@ export async function initDepartmentWorkspace() {
|
||||
departmentField.hidden = true;
|
||||
departmentSelect.value = userDepartmentId || '';
|
||||
}
|
||||
await loadAllTeachers();
|
||||
renderImportRows([{ code: '', name: '' }]);
|
||||
await loadWorkspace();
|
||||
} catch (error) {
|
||||
@@ -74,7 +85,9 @@ export async function initDepartmentWorkspace() {
|
||||
teachersErrorMessage = '';
|
||||
workloadErrorMessage = '';
|
||||
renderTeachers();
|
||||
await Promise.all([loadSubjects(), loadTeachers(), loadWorkload()]);
|
||||
teacherRequests = null;
|
||||
renderTeacherRequests();
|
||||
await Promise.all([loadSubjects(), loadTeachers(), loadWorkload(), loadTeacherRequests()]);
|
||||
}
|
||||
|
||||
async function loadSubjects() {
|
||||
@@ -165,6 +178,68 @@ export async function initDepartmentWorkspace() {
|
||||
}
|
||||
}
|
||||
|
||||
async function assignTeacherToDepartment(event) {
|
||||
event.preventDefault();
|
||||
const alertId = 'department-teacher-assign-alert';
|
||||
hideAlert(alertId);
|
||||
const teacherId = assignTeacherSelect?.value;
|
||||
const departmentId = selectedDepartmentId();
|
||||
if (!departmentId) {
|
||||
showAlert(alertId, 'Выберите кафедру', 'error');
|
||||
return;
|
||||
}
|
||||
if (!teacherId) {
|
||||
showAlert(alertId, 'Выберите преподавателя', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.post(`/api/department/teachers/${encodeURIComponent(teacherId)}/assignments`, {
|
||||
departmentId: Number(departmentId),
|
||||
comment: assignTeacherComment?.value.trim() || null
|
||||
});
|
||||
showAlert(alertId, 'Преподаватель привязан к кафедре', 'success');
|
||||
if (assignTeacherComment) assignTeacherComment.value = '';
|
||||
await Promise.all([loadTeachers(), loadWorkload()]);
|
||||
} catch (error) {
|
||||
showAlert(alertId, error.message || 'Ошибка привязки преподавателя', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitTeacherRequest(event) {
|
||||
event.preventDefault();
|
||||
const alertId = 'department-teacher-request-alert';
|
||||
hideAlert(alertId);
|
||||
const departmentId = selectedDepartmentId();
|
||||
const username = document.getElementById('teacher-request-username')?.value.trim();
|
||||
const fullName = document.getElementById('teacher-request-fullname')?.value.trim();
|
||||
const jobTitle = document.getElementById('teacher-request-jobtitle')?.value.trim();
|
||||
const comment = document.getElementById('teacher-request-comment')?.value.trim();
|
||||
if (!departmentId) {
|
||||
showAlert(alertId, 'Выберите кафедру', 'error');
|
||||
return;
|
||||
}
|
||||
if (!username || !fullName) {
|
||||
showAlert(alertId, 'Заполните логин и ФИО преподавателя', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.post('/api/department/teacher-requests', {
|
||||
departmentId: Number(departmentId),
|
||||
username,
|
||||
fullName,
|
||||
jobTitle,
|
||||
comment
|
||||
});
|
||||
showAlert(alertId, 'Заявка отправлена администратору', 'success');
|
||||
teacherRequestForm.reset();
|
||||
await loadTeacherRequests();
|
||||
} catch (error) {
|
||||
showAlert(alertId, error.message || 'Ошибка отправки заявки', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function openComments(subjectId, subjectName) {
|
||||
commentsCard.hidden = false;
|
||||
commentsTitle.textContent = `Комментарии: ${subjectName || 'дисциплина'}`;
|
||||
@@ -215,14 +290,46 @@ export async function initDepartmentWorkspace() {
|
||||
lastTeachers = null;
|
||||
teachersErrorMessage = '';
|
||||
renderTeachers();
|
||||
renderTeacherSelect();
|
||||
try {
|
||||
const teachers = await api.get(`/api/department/teachers?departmentId=${encodeURIComponent(selectedDepartmentId() || '')}`);
|
||||
lastTeachers = Array.isArray(teachers) ? teachers : [];
|
||||
renderTeachers();
|
||||
renderTeacherSelect();
|
||||
} catch (error) {
|
||||
lastTeachers = [];
|
||||
teachersErrorMessage = error.message || 'Ошибка загрузки преподавателей';
|
||||
renderTeachers();
|
||||
renderTeacherSelect();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAllTeachers() {
|
||||
try {
|
||||
const teachers = await api.get('/api/users/teachers');
|
||||
allTeachers = Array.isArray(teachers) ? teachers : [];
|
||||
renderTeacherSelect();
|
||||
} catch (error) {
|
||||
allTeachers = [];
|
||||
if (assignTeacherSelect) {
|
||||
assignTeacherSelect.innerHTML = `<option value="">${escapeHtml(error.message || 'Ошибка загрузки преподавателей')}</option>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTeacherRequests() {
|
||||
teacherRequests = null;
|
||||
renderTeacherRequests();
|
||||
try {
|
||||
const requests = await api.get(`/api/department/teacher-requests?departmentId=${encodeURIComponent(selectedDepartmentId() || '')}`);
|
||||
teacherRequests = Array.isArray(requests) ? requests : [];
|
||||
renderTeacherRequests();
|
||||
} catch (error) {
|
||||
teacherRequests = [];
|
||||
if (teacherRequestsTbody) {
|
||||
teacherRequestsTbody.innerHTML = `<tr><td colspan="6" class="loading-row">${escapeHtml(error.message || 'Ошибка загрузки заявок')}</td></tr>`;
|
||||
}
|
||||
if (teacherRequestCount) teacherRequestCount.textContent = '0 заявок';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,6 +426,49 @@ export async function initDepartmentWorkspace() {
|
||||
container.innerHTML = `<div class="loading-row">${escapeHtml(teachersErrorMessage || 'Преподаватели не найдены')}</div>`;
|
||||
}
|
||||
|
||||
function renderTeacherSelect() {
|
||||
if (!assignTeacherSelect) return;
|
||||
if (lastTeachers === null) {
|
||||
assignTeacherSelect.innerHTML = '<option value="">Загрузка...</option>';
|
||||
return;
|
||||
}
|
||||
const assignedIds = new Set((lastTeachers || []).map(teacher => String(teacher.id)));
|
||||
const availableTeachers = allTeachers.filter(teacher => !assignedIds.has(String(teacher.id)));
|
||||
assignTeacherSelect.innerHTML = '<option value="">Выберите преподавателя</option>' +
|
||||
availableTeachers.map(teacher => `
|
||||
<option value="${escapeHtml(teacher.id)}">
|
||||
${escapeHtml(teacher.fullName || teacher.username || `ID ${teacher.id}`)}
|
||||
</option>
|
||||
`).join('');
|
||||
if (!availableTeachers.length) {
|
||||
assignTeacherSelect.innerHTML = '<option value="">Нет доступных преподавателей</option>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderTeacherRequests() {
|
||||
if (!teacherRequestsTbody) return;
|
||||
if (teacherRequests === null) {
|
||||
teacherRequestsTbody.innerHTML = '<tr><td colspan="6" class="loading-row">Загрузка...</td></tr>';
|
||||
if (teacherRequestCount) teacherRequestCount.textContent = 'Загрузка...';
|
||||
return;
|
||||
}
|
||||
if (teacherRequestCount) teacherRequestCount.textContent = requestCountLabel(teacherRequests.length);
|
||||
if (!teacherRequests.length) {
|
||||
teacherRequestsTbody.innerHTML = '<tr><td colspan="6" class="loading-row">Заявок пока нет</td></tr>';
|
||||
return;
|
||||
}
|
||||
teacherRequestsTbody.innerHTML = teacherRequests.map(request => `
|
||||
<tr>
|
||||
<td><span class="badge ${requestStatusClass(request.status)}">${escapeHtml(requestStatusLabel(request.status))}</span></td>
|
||||
<td>${escapeHtml(request.username || '-')}</td>
|
||||
<td>${escapeHtml(request.fullName || '-')}</td>
|
||||
<td>${escapeHtml(request.jobTitle || '-')}</td>
|
||||
<td>${escapeHtml(request.comment || '-')}</td>
|
||||
<td>${escapeHtml(request.reviewComment || formatDateTime(request.reviewedAt) || '-')}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function selectedDepartmentId() {
|
||||
return role === ROLE_DEPARTMENT ? userDepartmentId : departmentSelect.value;
|
||||
}
|
||||
@@ -366,6 +516,30 @@ function subjectCountLabel(count) {
|
||||
return `${count} дисциплин`;
|
||||
}
|
||||
|
||||
function requestCountLabel(count) {
|
||||
const tail = count % 100;
|
||||
if (tail >= 11 && tail <= 14) return `${count} заявок`;
|
||||
if (count % 10 === 1) return `${count} заявка`;
|
||||
if ([2, 3, 4].includes(count % 10)) return `${count} заявки`;
|
||||
return `${count} заявок`;
|
||||
}
|
||||
|
||||
function requestStatusLabel(status) {
|
||||
return {
|
||||
PENDING: 'На рассмотрении',
|
||||
APPROVED: 'Одобрена',
|
||||
REJECTED: 'Отклонена'
|
||||
}[status] || status || '-';
|
||||
}
|
||||
|
||||
function requestStatusClass(status) {
|
||||
return {
|
||||
PENDING: 'badge-ef',
|
||||
APPROVED: 'badge-student',
|
||||
REJECTED: 'badge-admin'
|
||||
}[status] || 'badge-ef';
|
||||
}
|
||||
|
||||
function renderTeacherMetric(item, workloadErrorMessage) {
|
||||
return `
|
||||
<article class="metric-card">
|
||||
|
||||
@@ -21,6 +21,9 @@ const ROLE_BADGE = {
|
||||
export async function initUsers() {
|
||||
const usersTbody = document.getElementById('users-tbody');
|
||||
const createForm = document.getElementById('create-form');
|
||||
const teacherRequestsTbody = document.getElementById('teacher-requests-tbody');
|
||||
const teacherRequestsRefresh = document.getElementById('teacher-requests-refresh');
|
||||
let departments = [];
|
||||
|
||||
async function loadUsers() {
|
||||
try {
|
||||
@@ -33,6 +36,53 @@ export async function initUsers() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDepartments() {
|
||||
departments = await api.get('/api/departments');
|
||||
}
|
||||
|
||||
async function loadTeacherRequests() {
|
||||
if (!teacherRequestsTbody) return;
|
||||
teacherRequestsTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Загрузка...</td></tr>';
|
||||
hideAlert('teacher-requests-alert');
|
||||
try {
|
||||
const requests = await api.get('/api/teacher-requests?status=PENDING');
|
||||
renderTeacherRequests(Array.isArray(requests) ? requests : []);
|
||||
} catch (error) {
|
||||
teacherRequestsTbody.innerHTML = `<tr><td colspan="7" class="loading-row">${escapeHtml(error.message || 'Ошибка загрузки заявок')}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderTeacherRequests(requests) {
|
||||
if (!requests.length) {
|
||||
teacherRequestsTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Нет заявок на рассмотрении</td></tr>';
|
||||
return;
|
||||
}
|
||||
teacherRequestsTbody.innerHTML = requests.map(request => `
|
||||
<tr data-request-id="${escapeHtml(request.id)}">
|
||||
<td>
|
||||
<select class="teacher-request-department" data-native-select="true">
|
||||
${departments.map(department => `
|
||||
<option value="${escapeHtml(department.id)}" ${String(department.id) === String(request.departmentId) ? 'selected' : ''}>
|
||||
${escapeHtml(department.departmentName || department.name || department.id)}
|
||||
</option>
|
||||
`).join('')}
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" class="teacher-request-username" value="${escapeHtml(request.username || '')}" maxlength="50"></td>
|
||||
<td><input type="text" class="teacher-request-fullname" value="${escapeHtml(request.fullName || '')}" maxlength="255"></td>
|
||||
<td><input type="text" class="teacher-request-jobtitle" value="${escapeHtml(request.jobTitle || '')}" maxlength="255"></td>
|
||||
<td><input type="text" class="teacher-request-password" placeholder="Минимум 8 символов"></td>
|
||||
<td>${escapeHtml(request.comment || '-')}</td>
|
||||
<td>
|
||||
<div class="teacher-request-actions">
|
||||
<button type="button" class="btn btn-sm btn-primary teacher-request-approve">Одобрить</button>
|
||||
<button type="button" class="btn btn-sm btn-danger-subtle teacher-request-reject">Отклонить</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function renderUsers(users) {
|
||||
if (!users || !users.length) {
|
||||
usersTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Нет пользователей</td></tr>';
|
||||
@@ -71,6 +121,10 @@ export async function initUsers() {
|
||||
showAlert('create-alert', 'Заполните все поля', 'error');
|
||||
return;
|
||||
}
|
||||
if (password.length < 8) {
|
||||
showAlert('create-alert', 'Пароль должен быть не короче 8 символов', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await api.post('/api/users', {
|
||||
@@ -89,6 +143,54 @@ export async function initUsers() {
|
||||
}
|
||||
});
|
||||
|
||||
teacherRequestsRefresh?.addEventListener('click', loadTeacherRequests);
|
||||
|
||||
teacherRequestsTbody?.addEventListener('click', async (event) => {
|
||||
const approveButton = event.target.closest('.teacher-request-approve');
|
||||
const rejectButton = event.target.closest('.teacher-request-reject');
|
||||
if (!approveButton && !rejectButton) return;
|
||||
|
||||
const row = event.target.closest('tr[data-request-id]');
|
||||
const requestId = row?.dataset.requestId;
|
||||
if (!requestId) return;
|
||||
|
||||
if (approveButton) {
|
||||
const payload = {
|
||||
departmentId: Number(row.querySelector('.teacher-request-department')?.value),
|
||||
username: row.querySelector('.teacher-request-username')?.value.trim(),
|
||||
fullName: row.querySelector('.teacher-request-fullname')?.value.trim(),
|
||||
jobTitle: row.querySelector('.teacher-request-jobtitle')?.value.trim(),
|
||||
password: row.querySelector('.teacher-request-password')?.value
|
||||
};
|
||||
if (!payload.departmentId || !payload.username || !payload.fullName || !payload.password) {
|
||||
showAlert('teacher-requests-alert', 'Заполните кафедру, логин, ФИО и пароль', 'error');
|
||||
return;
|
||||
}
|
||||
if (payload.password.length < 8) {
|
||||
showAlert('teacher-requests-alert', 'Пароль должен быть не короче 8 символов', 'error');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await api.post(`/api/teacher-requests/${encodeURIComponent(requestId)}/approve`, payload);
|
||||
showAlert('teacher-requests-alert', 'Преподаватель создан, заявка одобрена', 'success');
|
||||
await Promise.all([loadTeacherRequests(), loadUsers()]);
|
||||
} catch (error) {
|
||||
showAlert('teacher-requests-alert', error.message || 'Ошибка одобрения заявки', 'error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const reviewComment = prompt('Причина отклонения');
|
||||
if (reviewComment === null) return;
|
||||
try {
|
||||
await api.post(`/api/teacher-requests/${encodeURIComponent(requestId)}/reject`, { reviewComment });
|
||||
showAlert('teacher-requests-alert', 'Заявка отклонена', 'success');
|
||||
await loadTeacherRequests();
|
||||
} catch (error) {
|
||||
showAlert('teacher-requests-alert', error.message || 'Ошибка отклонения заявки', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
usersTbody.addEventListener('click', async (e) => {
|
||||
const deleteBtn = e.target.closest('.btn-archive-user');
|
||||
if (!deleteBtn) return;
|
||||
@@ -114,5 +216,6 @@ export async function initUsers() {
|
||||
}
|
||||
});
|
||||
|
||||
await loadUsers();
|
||||
await loadDepartments();
|
||||
await Promise.all([loadUsers(), loadTeacherRequests()]);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="form-alert" id="department-workspace-alert" role="alert"></div>
|
||||
</div>
|
||||
|
||||
<div class="department-workspace-grid">
|
||||
<div class="department-workspace-grid department-workspace-grid-wide">
|
||||
<div class="card create-card">
|
||||
<div class="card-header-row">
|
||||
<h2>Загрузка дисциплин</h2>
|
||||
@@ -42,6 +42,52 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card create-card">
|
||||
<div class="card-header-row">
|
||||
<h2>Добавить преподавателя</h2>
|
||||
</div>
|
||||
<form id="department-teacher-assign-form" class="stack-form">
|
||||
<div class="form-group">
|
||||
<label for="department-teacher-select">Преподаватель</label>
|
||||
<select id="department-teacher-select" data-native-select="true">
|
||||
<option value="">Загрузка...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="department-teacher-assign-comment">Комментарий</label>
|
||||
<input type="text" id="department-teacher-assign-comment" placeholder="Например, ведет лабораторные работы">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-md btn-secondary">Привязать к кафедре</button>
|
||||
<div class="form-alert" id="department-teacher-assign-alert" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card create-card">
|
||||
<div class="card-header-row">
|
||||
<h2>Заявка на преподавателя</h2>
|
||||
</div>
|
||||
<form id="department-teacher-request-form" class="stack-form">
|
||||
<div class="form-group">
|
||||
<label for="teacher-request-username">Логин</label>
|
||||
<input type="text" id="teacher-request-username" placeholder="ivanov_teacher" maxlength="50" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="teacher-request-fullname">ФИО</label>
|
||||
<input type="text" id="teacher-request-fullname" placeholder="Иванов Иван Иванович" maxlength="255" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="teacher-request-jobtitle">Должность</label>
|
||||
<input type="text" id="teacher-request-jobtitle" placeholder="Доцент" maxlength="255">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="teacher-request-comment">Комментарий</label>
|
||||
<input type="text" id="teacher-request-comment" placeholder="Что важно проверить администратору">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-md btn-primary">Отправить заявку</button>
|
||||
<div class="form-alert" id="department-teacher-request-alert" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header-row">
|
||||
<h2>Преподаватели и нагрузка</h2>
|
||||
@@ -50,6 +96,30 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header-row">
|
||||
<h2>Заявки кафедры</h2>
|
||||
<span class="muted" id="department-teacher-request-count">0 заявок</span>
|
||||
</div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Статус</th>
|
||||
<th>Логин</th>
|
||||
<th>ФИО</th>
|
||||
<th>Должность</th>
|
||||
<th>Комментарий</th>
|
||||
<th>Рассмотрение</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="department-teacher-requests-tbody">
|
||||
<tr><td colspan="6" class="loading-row">Загрузка...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header-row">
|
||||
<h2>Дисциплины кафедры</h2>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- ===== Users Tab ===== -->
|
||||
<div class="card create-card">
|
||||
<div class="card create-card">
|
||||
<h2>Новый пользователь</h2>
|
||||
<form id="create-form">
|
||||
<div class="form-row">
|
||||
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-password">Пароль</label>
|
||||
<input type="text" id="new-password" placeholder="password" required>
|
||||
<input type="text" id="new-password" placeholder="Минимум 8 символов" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new-role">Роль</label>
|
||||
@@ -38,9 +38,35 @@
|
||||
</div>
|
||||
<div class="form-alert" id="create-alert" role="alert"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header-row">
|
||||
<h2>Заявки на преподавателей</h2>
|
||||
<button type="button" class="btn btn-sm btn-secondary" id="teacher-requests-refresh">Обновить</button>
|
||||
</div>
|
||||
<div class="form-alert" id="teacher-requests-alert" role="alert"></div>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Кафедра</th>
|
||||
<th>Логин</th>
|
||||
<th>ФИО</th>
|
||||
<th>Должность</th>
|
||||
<th>Пароль</th>
|
||||
<th>Комментарий</th>
|
||||
<th>Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="teacher-requests-tbody">
|
||||
<tr><td colspan="7" class="loading-row">Загрузка...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Все пользователи</h2>
|
||||
<div class="table-wrap">
|
||||
<table id="users-table">
|
||||
|
||||
Reference in New Issue
Block a user