Добавил для групп поле численности.

В модалку на UI добавил отображение численности групп и вместимости аудиторий, проверку на доступность и вместимость.
This commit is contained in:
ProstoDenya01
2026-03-12 14:45:25 +03:00
parent 1b0a6c86ff
commit 03eaf6ab13
8 changed files with 89 additions and 6 deletions

View File

@@ -32,6 +32,7 @@ public class GroupController {
.map(g -> new GroupResponse( .map(g -> new GroupResponse(
g.getId(), g.getId(),
g.getName(), g.getName(),
g.getGroupSize(),
g.getEducationForm().getId(), g.getEducationForm().getId(),
g.getEducationForm().getName())) g.getEducationForm().getName()))
.toList(); .toList();
@@ -45,6 +46,9 @@ public class GroupController {
if (groupRepository.findByName(request.getName().trim()).isPresent()) { if (groupRepository.findByName(request.getName().trim()).isPresent()) {
return ResponseEntity.badRequest().body(Map.of("message", "Группа с таким названием уже существует")); return ResponseEntity.badRequest().body(Map.of("message", "Группа с таким названием уже существует"));
} }
if (request.getGroupSize() == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Численность группы обязательна"));
}
if (request.getEducationFormId() == null) { if (request.getEducationFormId() == null) {
return ResponseEntity.badRequest().body(Map.of("message", "Форма обучения обязательна")); return ResponseEntity.badRequest().body(Map.of("message", "Форма обучения обязательна"));
} }
@@ -56,12 +60,14 @@ public class GroupController {
StudentGroup group = new StudentGroup(); StudentGroup group = new StudentGroup();
group.setName(request.getName().trim()); group.setName(request.getName().trim());
group.setGroupSize(request.getGroupSize());
group.setEducationForm(efOpt.get()); group.setEducationForm(efOpt.get());
groupRepository.save(group); groupRepository.save(group);
return ResponseEntity.ok(new GroupResponse( return ResponseEntity.ok(new GroupResponse(
group.getId(), group.getId(),
group.getName(), group.getName(),
group.getGroupSize(),
group.getEducationForm().getId(), group.getEducationForm().getId(),
group.getEducationForm().getName())); group.getEducationForm().getName()));
} }

View File

@@ -3,6 +3,7 @@ package com.magistr.app.dto;
public class CreateGroupRequest { public class CreateGroupRequest {
private String name; private String name;
private Long groupSize;
private Long educationFormId; private Long educationFormId;
public String getName() { public String getName() {
@@ -13,6 +14,14 @@ public class CreateGroupRequest {
this.name = name; this.name = name;
} }
public Long getGroupSize() {
return groupSize;
}
public void setGroupSize(Long groupSize) {
this.groupSize = groupSize;
}
public Long getEducationFormId() { public Long getEducationFormId() {
return educationFormId; return educationFormId;
} }

View File

@@ -4,12 +4,14 @@ public class GroupResponse {
private Long id; private Long id;
private String name; private String name;
private Long groupSize;
private Long educationFormId; private Long educationFormId;
private String educationFormName; private String educationFormName;
public GroupResponse(Long id, String name, Long educationFormId, String educationFormName) { public GroupResponse(Long id, String name, Long groupSize, Long educationFormId, String educationFormName) {
this.id = id; this.id = id;
this.name = name; this.name = name;
this.groupSize = groupSize;
this.educationFormId = educationFormId; this.educationFormId = educationFormId;
this.educationFormName = educationFormName; this.educationFormName = educationFormName;
} }
@@ -22,6 +24,10 @@ public class GroupResponse {
return name; return name;
} }
public Long getGroupSize() {
return groupSize;
}
public Long getEducationFormId() { public Long getEducationFormId() {
return educationFormId; return educationFormId;
} }

View File

@@ -13,6 +13,9 @@ public class StudentGroup {
@Column(unique = true, nullable = false, length = 100) @Column(unique = true, nullable = false, length = 100)
private String name; private String name;
@Column(name = "group_size", nullable = false)
private Long groupSize;
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "education_form_id", nullable = false) @JoinColumn(name = "education_form_id", nullable = false)
private EducationForm educationForm; private EducationForm educationForm;
@@ -36,6 +39,14 @@ public class StudentGroup {
this.name = name; this.name = name;
} }
public Long getGroupSize() {
return groupSize;
}
public void setGroupSize(Long groupSize) {
this.groupSize = groupSize;
}
public EducationForm getEducationForm() { public EducationForm getEducationForm() {
return educationForm; return educationForm;
} }

View File

@@ -43,14 +43,16 @@ ON CONFLICT (name) DO NOTHING;
CREATE TABLE IF NOT EXISTS student_groups ( CREATE TABLE IF NOT EXISTS student_groups (
id BIGSERIAL PRIMARY KEY, id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL, name VARCHAR(100) UNIQUE NOT NULL,
group_size BIGINT NOT NULL,
education_form_id BIGINT NOT NULL REFERENCES education_forms(id), education_form_id BIGINT NOT NULL REFERENCES education_forms(id),
course INT CHECK (course BETWEEN 1 AND 6), course INT CHECK (course BETWEEN 1 AND 6),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Тестовая базовая группа для работы -- Тестовая базовая группа для работы
INSERT INTO student_groups (name, education_form_id, course) INSERT INTO student_groups (name, group_size, education_form_id, course)
VALUES ('ИВТ-21-1', 1, 3) VALUES ('ИВТ-21-1', 25, 1, 3),
('ИБ-41м', 15, 2, 2)
ON CONFLICT (name) DO NOTHING; ON CONFLICT (name) DO NOTHING;
-- ========================================== -- ==========================================

View File

@@ -68,6 +68,7 @@ export async function initGroups() {
<tr> <tr>
<td>${g.id}</td> <td>${g.id}</td>
<td>${escapeHtml(g.name)}</td> <td>${escapeHtml(g.name)}</td>
<td>${escapeHtml(g.groupSize)}</td>
<td><span class="badge badge-ef">${escapeHtml(g.educationFormName)}</span></td> <td><span class="badge badge-ef">${escapeHtml(g.educationFormName)}</span></td>
<td><button class="btn-delete" data-id="${g.id}">Удалить</button></td> <td><button class="btn-delete" data-id="${g.id}">Удалить</button></td>
</tr>`).join(''); </tr>`).join('');
@@ -77,13 +78,15 @@ export async function initGroups() {
e.preventDefault(); e.preventDefault();
hideAlert('create-group-alert'); hideAlert('create-group-alert');
const name = document.getElementById('new-group-name').value.trim(); const name = document.getElementById('new-group-name').value.trim();
const groupSize = document.getElementById('new-group-size').value;
const educationFormId = newGroupEfSelect.value; const educationFormId = newGroupEfSelect.value;
if (!name) { showAlert('create-group-alert', 'Введите название группы', 'error'); return; } if (!name) { showAlert('create-group-alert', 'Введите название группы', 'error'); return; }
if (!groupSize) { showAlert('create-group-alert', 'Введите размер группы', 'error'); return; }
if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; } if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; }
try { try {
const data = await api.post('/api/groups', { name, educationFormId: Number(educationFormId) }); const data = await api.post('/api/groups', { name, groupSize, educationFormId: Number(educationFormId) });
showAlert('create-group-alert', `Группа "${escapeHtml(data.name)}" создана`, 'success'); showAlert('create-group-alert', `Группа "${escapeHtml(data.name)}" создана`, 'success');
createGroupForm.reset(); createGroupForm.reset();
loadGroups(); loadGroups();

View File

@@ -79,8 +79,19 @@ export async function initUsers() {
// Заполнение select группами // Заполнение select группами
function renderGroupOptions() { function renderGroupOptions() {
if (!groups || groups.length === 0) {
lessonClassroomSelect.innerHTML = '<option value="">Нет доступных групп</option>';
return;
}
lessonGroupSelect.innerHTML = '<option value="">Выберите группу</option>' + lessonGroupSelect.innerHTML = '<option value="">Выберите группу</option>' +
groups.map(g => `<option value="${g.id}">${escapeHtml(g.name)}</option>`).join(''); groups.map(g => {
let optionText = escapeHtml(g.name);
if(g.groupSize) {
optionText += ` (численность: ${g.groupSize} чел.)`;
}
return `<option value="${g.id}">${optionText}</option>`;
}).join('');
} }
// Заполнение select дисциплинами // Заполнение select дисциплинами
@@ -90,10 +101,40 @@ export async function initUsers() {
} }
function renderClassroomsOptions() { function renderClassroomsOptions() {
if (!classrooms || classrooms.length ===0) {
lessonClassroomSelect.innerHTML = '<option value="">Нет доступных аудиторий</option>';
return;
}
const selectedGroupId = lessonGroupSelect.value;
const selectedGroup = groups?.find(g => g.id == selectedGroupId);
const groupSize = selectedGroup?.groupSize || 0;
lessonClassroomSelect.innerHTML = '<option value="">Выберите аудиторию</option>' + lessonClassroomSelect.innerHTML = '<option value="">Выберите аудиторию</option>' +
classrooms.map(c => `<option value="${c.id}">${escapeHtml(c.name)}</option>`).join(''); classrooms.map(c => {
let optionText = escapeHtml(c.name);
// Добавление текста с инфой о вместимости чел.
if(c.capacity) {
optionText += ` (вместимость: ${c.capacity} чел.)`;
}
// Если аудитория занята, то рисуем крестик допом
if (c.isAvailable === false) {
optionText += ` ❌ Занята`
// Если свободна, но меньше численности группы, отображаем воскл. знак
} else if (selectedGroupId && groupSize > 0 && c.capacity && groupSize > c.capacity) {
optionText += ` ⚠️ Недостаточно места`;
}
return `<option value="${c.id}">${optionText}</option>`;
}).join('');
} }
lessonGroupSelect.addEventListener('change', function() {
renderClassroomsOptions();
});
// NEW: функция обновления списка времени в зависимости от дня // NEW: функция обновления списка времени в зависимости от дня
function updateTimeOptions(dayValue) { function updateTimeOptions(dayValue) {
let times = []; let times = [];

View File

@@ -7,6 +7,10 @@
<label for="new-group-name">Название группы</label> <label for="new-group-name">Название группы</label>
<input type="text" id="new-group-name" placeholder="ИВТ-21-1" required> <input type="text" id="new-group-name" placeholder="ИВТ-21-1" required>
</div> </div>
<div class="form-group">
<label for="new-group-size">Численность группы</label>
<input type="text" id="new-group-size" placeholder="20" required>
</div>
<div class="form-group"> <div class="form-group">
<label for="new-group-ef">Форма обучения</label> <label for="new-group-ef">Форма обучения</label>
<select id="new-group-ef"> <select id="new-group-ef">
@@ -35,6 +39,7 @@
<tr> <tr>
<th>ID</th> <th>ID</th>
<th>Название</th> <th>Название</th>
<th>Численность (чел.)</th>
<th>Форма обучения</th> <th>Форма обучения</th>
<th>Действия</th> <th>Действия</th>
</tr> </tr>