доработка редактора кода
This commit is contained in:
parent
6f42fe0ee6
commit
6e7bf47814
|
|
@ -2,16 +2,21 @@ package ru.oa2.lti.application.controller;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpSession;
|
import jakarta.servlet.http.HttpSession;
|
||||||
|
import org.springframework.http.HttpStatusCode;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import ru.oa2.lti.domain.model.auth.Payload;
|
|
||||||
import ru.oa2.lti.application.service.task.TaskService;
|
import ru.oa2.lti.application.service.task.TaskService;
|
||||||
import ru.oa2.lti.domain.model.auth.LtiLogin;
|
|
||||||
import ru.oa2.lti.domain.model.ResultRequest;
|
import ru.oa2.lti.domain.model.ResultRequest;
|
||||||
|
import ru.oa2.lti.domain.model.auth.LtiLogin;
|
||||||
|
import ru.oa2.lti.domain.model.auth.Payload;
|
||||||
|
import ru.oa2.lti.domain.model.results.ResultResponse;
|
||||||
|
import ru.oa2.lti.domain.model.task.RequestUpdateTask;
|
||||||
|
import ru.oa2.lti.domain.model.task.TaskData;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
@ -47,21 +52,36 @@ public class TaskController {
|
||||||
return "redirect:/error";
|
return "redirect:/error";
|
||||||
}
|
}
|
||||||
|
|
||||||
model.addAttribute("description", data);
|
model.addAttribute("description", data.getDescription());
|
||||||
|
|
||||||
|
if (Objects.requireNonNull(payload).getCoach()) {
|
||||||
|
model.addAttribute("initScript", data.getInitScript());
|
||||||
|
model.addAttribute("checkScript", data.getVerificationScript());
|
||||||
|
|
||||||
|
return "task-editor";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.requireNonNull(payload).getCoach()) {
|
|
||||||
return "task-editor";
|
|
||||||
}
|
|
||||||
return "task";
|
return "task";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity updateTask() {
|
public ResponseEntity updateTask(@RequestBody TaskData data,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
|
||||||
//TODO exception
|
var session = request.getSession();
|
||||||
taskService.saveTask("TODO", "TODO", "TODO");
|
var payload = (Payload) session.getAttribute("payload");
|
||||||
return ResponseEntity.accepted().build();
|
|
||||||
|
if (payload != null && payload.getCoach()) {
|
||||||
|
return ResponseEntity.accepted().body(
|
||||||
|
taskService.saveTask(RequestUpdateTask.builder()
|
||||||
|
.contextId(payload.getContextId())
|
||||||
|
.data(data)
|
||||||
|
.build())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ResponseEntity.status(HttpStatusCode.valueOf(403))
|
||||||
|
.body(new ResultResponse("forbidden"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/submit")
|
@PostMapping("/submit")
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@ package ru.oa2.lti.application.infrastructure.repository.entities;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.hibernate.annotations.JdbcTypeCode;
|
import org.hibernate.annotations.JdbcTypeCode;
|
||||||
import org.hibernate.type.SqlTypes;
|
import org.hibernate.type.SqlTypes;
|
||||||
import ru.oa2.lti.domain.model.task.TaskData;
|
import ru.oa2.lti.domain.model.task.TaskData;
|
||||||
import ru.oa2.lti.domain.model.task.TaskType;
|
import ru.oa2.lti.domain.model.task.TaskType;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@Setter
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "task")
|
@Table(name = "task")
|
||||||
public class Task {
|
public class Task {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package ru.oa2.lti.application.service.task;
|
||||||
|
|
||||||
import ru.oa2.lti.domain.model.ResultRequest;
|
import ru.oa2.lti.domain.model.ResultRequest;
|
||||||
import ru.oa2.lti.domain.model.results.ResultResponse;
|
import ru.oa2.lti.domain.model.results.ResultResponse;
|
||||||
|
import ru.oa2.lti.domain.model.task.RequestUpdateTask;
|
||||||
|
import ru.oa2.lti.domain.model.task.TaskData;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -10,7 +12,7 @@ public interface TaskService {
|
||||||
/*
|
/*
|
||||||
Получение задачи (контекста лабораторной работы)
|
Получение задачи (контекста лабораторной работы)
|
||||||
*/
|
*/
|
||||||
String getTask(UUID contextId);
|
TaskData getTask(UUID contextId);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Запуск проверки через скрипт
|
Запуск проверки через скрипт
|
||||||
|
|
@ -20,5 +22,5 @@ public interface TaskService {
|
||||||
/*
|
/*
|
||||||
Сохранение/обновление task-а
|
Сохранение/обновление task-а
|
||||||
*/
|
*/
|
||||||
void saveTask(String initScript, String checkScript, String description);
|
ResultResponse saveTask(RequestUpdateTask requestDataTask);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
package ru.oa2.lti.application.service.task;
|
package ru.oa2.lti.application.service.task;
|
||||||
|
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import ru.oa2.lti.application.infrastructure.lms.LMSService;
|
import ru.oa2.lti.application.infrastructure.lms.LMSService;
|
||||||
import ru.oa2.lti.application.infrastructure.runner.Runner;
|
|
||||||
import ru.oa2.lti.application.service.results.ResultService;
|
|
||||||
import ru.oa2.lti.domain.model.ResultRequest;
|
|
||||||
import ru.oa2.lti.domain.model.task.TaskData;
|
|
||||||
import ru.oa2.lti.application.infrastructure.repository.LMSContentRepository;
|
import ru.oa2.lti.application.infrastructure.repository.LMSContentRepository;
|
||||||
import ru.oa2.lti.application.infrastructure.repository.entities.LMSContent;
|
import ru.oa2.lti.application.infrastructure.repository.entities.LMSContent;
|
||||||
import ru.oa2.lti.application.infrastructure.repository.entities.Task;
|
import ru.oa2.lti.application.infrastructure.repository.entities.Task;
|
||||||
|
import ru.oa2.lti.application.infrastructure.runner.Runner;
|
||||||
|
import ru.oa2.lti.application.service.results.ResultService;
|
||||||
|
import ru.oa2.lti.domain.model.ResultRequest;
|
||||||
import ru.oa2.lti.domain.model.results.ResultResponse;
|
import ru.oa2.lti.domain.model.results.ResultResponse;
|
||||||
|
import ru.oa2.lti.domain.model.task.RequestUpdateTask;
|
||||||
|
import ru.oa2.lti.domain.model.task.TaskData;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@Transactional(rollbackOn = Throwable.class)
|
||||||
public class TaskServiceImpl implements TaskService {
|
public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
private final LMSContentRepository lmsContentRepository;
|
private final LMSContentRepository lmsContentRepository;
|
||||||
|
|
@ -36,7 +41,7 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTask(UUID contextId) {
|
public TaskData getTask(UUID contextId) {
|
||||||
var content = lmsContentRepository.getLMSContentByContentId(contextId);
|
var content = lmsContentRepository.getLMSContentByContentId(contextId);
|
||||||
|
|
||||||
if(content.isPresent()) {
|
if(content.isPresent()) {
|
||||||
|
|
@ -48,12 +53,12 @@ public class TaskServiceImpl implements TaskService {
|
||||||
|
|
||||||
//TODO string -> exception?
|
//TODO string -> exception?
|
||||||
if (!runner.run(contextId, data.getInitScript())) {
|
if (!runner.run(contextId, data.getInitScript())) {
|
||||||
return "Init script FAILED";
|
return new TaskData("Init script FAILED", "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.getDescription();
|
return data;
|
||||||
} else {
|
} else {
|
||||||
return "Not Page";
|
return new TaskData("Not Page", "", "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,7 +75,21 @@ public class TaskServiceImpl implements TaskService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveTask(String initScript, String checkScript, String description) {
|
public ResultResponse saveTask(RequestUpdateTask requestUpdateTask) {
|
||||||
//TODO
|
|
||||||
|
log.info("save");
|
||||||
|
var result = lmsContentRepository.getLMSContentByContentId(requestUpdateTask.getContextId());
|
||||||
|
|
||||||
|
//TODO доработать версионирование task-ов
|
||||||
|
if (result.isPresent()) {
|
||||||
|
var content = result.get();
|
||||||
|
var tasks = content.getTasks();
|
||||||
|
var currentTask = tasks.stream().findFirst();
|
||||||
|
currentTask.ifPresent(task -> task.setData(requestUpdateTask.getData()));
|
||||||
|
lmsContentRepository.save(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO обработка exception - failed / success
|
||||||
|
return new ResultResponse("success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ru.oa2.lti.domain.model.task;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class RequestUpdateTask {
|
||||||
|
UUID contextId;
|
||||||
|
TaskData data;
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package ru.oa2.lti.domain.model.task;
|
package ru.oa2.lti.domain.model.task;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class TaskData {
|
public class TaskData {
|
||||||
String initScript;
|
|
||||||
String description;
|
String description;
|
||||||
|
String initScript;
|
||||||
String verificationScript;
|
String verificationScript;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
document.getElementById('saveBtn').addEventListener('click', async () => {
|
||||||
|
|
||||||
|
const description = document.getElementById('description').value;
|
||||||
|
const initScript = document.getElementById('initScript').value;
|
||||||
|
const verificationScript = document.getElementById('checkScript').value;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
description,
|
||||||
|
initScript,
|
||||||
|
verificationScript
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/tool/lti/task', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
const statusContainer = document.getElementById('statusContainer');
|
||||||
|
const statusIcon = document.getElementById('icon');
|
||||||
|
const statusLabel = document.getElementById('statusLabel');
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
statusIcon.className = 'fa-solid fa-check-circle';
|
||||||
|
statusIcon.classList.add('status-success');
|
||||||
|
statusLabel.textContent = 'Задача успешно сохранена';
|
||||||
|
statusContainer.style.display = 'flex';
|
||||||
|
statusContainer.classList.remove('status-failed');
|
||||||
|
statusContainer.classList.add('status-success');
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || 'Ошибка сохранения');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const statusContainer = document.getElementById('statusContainer');
|
||||||
|
const statusIcon = document.getElementById('icon');
|
||||||
|
const statusLabel = document.getElementById('statusLabel');
|
||||||
|
|
||||||
|
statusIcon.className = 'fa-solid fa-times-circle';
|
||||||
|
statusIcon.classList.add('status-failed');
|
||||||
|
statusLabel.textContent = 'Ошибка: ' + error.message;
|
||||||
|
statusContainer.style.display = 'flex';
|
||||||
|
statusContainer.classList.remove('status-success');
|
||||||
|
statusContainer.classList.add('status-failed');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Доступ запрещён — 403</title>
|
||||||
|
<!-- Bootstrap 5 -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Font Awesome -->
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
.forbidden-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 500px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #e0e7ff;
|
||||||
|
}
|
||||||
|
.forbidden-icon {
|
||||||
|
font-size: 70px;
|
||||||
|
color: #dc3545;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
.error-code {
|
||||||
|
font-size: 100px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #6c757d;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #212529;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
.error-description {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #6c757d;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.back-btn {
|
||||||
|
background-color: #0d6efd;
|
||||||
|
color: white;
|
||||||
|
padding: 10px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.back-btn:hover {
|
||||||
|
background-color: #0b5ed7;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.05); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="forbidden-card">
|
||||||
|
<!-- Иконка "запрещено" -->
|
||||||
|
<div class="forbidden-icon">
|
||||||
|
<i class="fas fa-ban"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Код ошибки -->
|
||||||
|
<div class="error-code">403</div>
|
||||||
|
|
||||||
|
<!-- Основное сообщение -->
|
||||||
|
<div class="error-message">Доступ запрещён</div>
|
||||||
|
|
||||||
|
<!-- Описание -->
|
||||||
|
<p class="error-description" th:text="${message}">
|
||||||
|
У вас нет прав для просмотра этой страницы.
|
||||||
|
Пожалуйста, обратитесь к администратору, если считаете, что это ошибка.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Кнопка "Назад" -->
|
||||||
|
<a th:href="@{${redirectUrl} ?: '/'}" class="back-btn">
|
||||||
|
<i class="fas fa-arrow-left"></i> Вернуться назад
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bootstrap JS (опционально) -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Редактор задачи LTI</title>
|
<title>Редактор задачи LTI #TODO подтягивать name?</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
|
@ -115,19 +115,28 @@
|
||||||
<!-- Поле: Описание задачи (редактируемое) -->
|
<!-- Поле: Описание задачи (редактируемое) -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Описание задачи (HTML)</label>
|
<label class="form-label">Описание задачи (HTML)</label>
|
||||||
<textarea id="description" class="code-area" placeholder="Введите HTML-описание задачи..."><th:block th:utext="${description ?: ''}"/></textarea>
|
<textarea
|
||||||
|
id="description"
|
||||||
|
class="code-area"
|
||||||
|
placeholder="Введите HTML-описание задачи..."><th:block th:utext="${description ?: ''}"/></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Поле: initScript -->
|
<!-- Поле: initScript -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">initScript (JavaScript)</label>
|
<label class="form-label">initScript</label>
|
||||||
<textarea id="initScript" class="code-area" placeholder="JavaScript-код для инициализации среды..."><th:block th:text="${initScript ?: ''}"/></textarea>
|
<textarea
|
||||||
|
id="initScript"
|
||||||
|
class="code-area"
|
||||||
|
placeholder="Скрипт инициализации задания"><th:block th:text="${initScript ?: ''}"/></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Поле: checkScript -->
|
<!-- Поле: checkScript -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">checkScript (JavaScript)</label>
|
<label class="form-label">checkScript</label>
|
||||||
<textarea id="checkScript" class="code-area" placeholder="JavaScript-код для проверки результата..."><th:block th:text="${checkScript ?: ''}"/></textarea>
|
<textarea
|
||||||
|
id="checkScript"
|
||||||
|
class="code-area"
|
||||||
|
placeholder="Скрипт проверки задания"><th:block th:text="${checkScript ?: ''}"/></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -148,69 +157,17 @@
|
||||||
<i class="fas fa-save"></i> Сохранить задачу
|
<i class="fas fa-save"></i> Сохранить задачу
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="submitBtn" class="btn btn-primary submit-btn">
|
||||||
|
<i class="fas fa-paper-plane"></i> Отправить на проверку
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Скрытые поля -->
|
|
||||||
<input type="hidden" id="contextId" th:value="${contextId}" />
|
|
||||||
<input type="hidden" id="participantId" th:value="${participantId}" />
|
|
||||||
|
|
||||||
<!-- Скрипт отправки -->
|
|
||||||
<script th:src="@{/tool/lti/js/main.js}"></script>
|
<script th:src="@{/tool/lti/js/main.js}"></script>
|
||||||
<script>
|
<script th:src="@{/tool/lti/js/edit.js}"></script>
|
||||||
document.getElementById('saveBtn').addEventListener('click', async () => {
|
|
||||||
const contextId = document.getElementById('contextId').value;
|
|
||||||
const description = document.getElementById('description').value;
|
|
||||||
const initScript = document.getElementById('initScript').value;
|
|
||||||
const checkScript = document.getElementById('checkScript').value;
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
contextId,
|
|
||||||
description,
|
|
||||||
initScript,
|
|
||||||
checkScript
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/tool/lti/task', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
const statusContainer = document.getElementById('statusContainer');
|
|
||||||
const statusIcon = document.getElementById('icon');
|
|
||||||
const statusLabel = document.getElementById('statusLabel');
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
statusIcon.className = 'fa-solid fa-check-circle';
|
|
||||||
statusIcon.classList.add('status-success');
|
|
||||||
statusLabel.textContent = 'Задача успешно сохранена';
|
|
||||||
statusContainer.style.display = 'flex';
|
|
||||||
statusContainer.classList.remove('status-failed');
|
|
||||||
statusContainer.classList.add('status-success');
|
|
||||||
} else {
|
|
||||||
throw new Error(result.message || 'Ошибка сохранения');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const statusContainer = document.getElementById('statusContainer');
|
|
||||||
const statusIcon = document.getElementById('icon');
|
|
||||||
const statusLabel = document.getElementById('statusLabel');
|
|
||||||
|
|
||||||
statusIcon.className = 'fa-solid fa-times-circle';
|
|
||||||
statusIcon.classList.add('status-failed');
|
|
||||||
statusLabel.textContent = 'Ошибка: ' + error.message;
|
|
||||||
statusContainer.style.display = 'flex';
|
|
||||||
statusContainer.classList.remove('status-success');
|
|
||||||
statusContainer.classList.add('status-failed');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
Reference in New Issue