Дорабоки интерфейса системы тестирования. Раздел 1 Шапка+Верхний brick
This commit is contained in:
+1
-1
@@ -15,7 +15,7 @@
|
||||
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>Система тестрования</title>
|
||||
<title>Система тестирования</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useNavigate, useParams, Link, useOutletContext } from 'react-router-dom';
|
||||
import { api } from '../api';
|
||||
import { formatTestAuthorLabel } from '../utils/formatUserName';
|
||||
@@ -70,6 +70,41 @@ function mapEditorToDraftQuestions(ed) {
|
||||
}));
|
||||
}
|
||||
|
||||
function buildDraftSnapshot({ title, description, passing, questions }) {
|
||||
return JSON.stringify({
|
||||
title: title ?? '',
|
||||
description: description ?? '',
|
||||
passing: String(passing ?? ''),
|
||||
questions: (questions || []).map((q) => ({
|
||||
text: q.text ?? '',
|
||||
hasMultipleAnswers: !!q.hasMultipleAnswers,
|
||||
options: (q.options || []).map((o) => ({
|
||||
text: o.text ?? '',
|
||||
isCorrect: !!o.isCorrect,
|
||||
})),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
function postDebugLog({ runId, hypothesisId, location, message, data }) {
|
||||
fetch('http://127.0.0.1:7419/ingest/a86fc408-7178-4abe-8dd9-f3e6bfb05d76', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Debug-Session-Id': '034e19',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sessionId: '034e19',
|
||||
runId,
|
||||
hypothesisId,
|
||||
location,
|
||||
message,
|
||||
data,
|
||||
timestamp: Date.now(),
|
||||
}),
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
export default function TestDetail() {
|
||||
const { id } = useParams();
|
||||
const nav = useNavigate();
|
||||
@@ -83,6 +118,7 @@ export default function TestDetail() {
|
||||
const [draftDescription, setDraftDescription] = useState('');
|
||||
const [draftPassing, setDraftPassing] = useState('70');
|
||||
const [draftQuestions, setDraftQuestions] = useState(() => [createEmptyQuestion()]);
|
||||
const [draftSnapshotOnLoad, setDraftSnapshotOnLoad] = useState(null);
|
||||
const [draftStatus, setDraftStatus] = useState('');
|
||||
const [deactivateBusy, setDeactivateBusy] = useState(false);
|
||||
const [importPreview, setImportPreview] = useState(null);
|
||||
@@ -106,6 +142,16 @@ export default function TestDetail() {
|
||||
const [assignLoadBusy, setAssignLoadBusy] = useState(false);
|
||||
const [attemptsList, setAttemptsList] = useState(undefined);
|
||||
const [attemptsErr, setAttemptsErr] = useState(null);
|
||||
const debugRunId = 'pre-fix';
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H1',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:component-start',
|
||||
message: 'TestDetail render start',
|
||||
data: { hasData: !!data, hasTaker: !!taker, hasErr: !!err, testId: id != null },
|
||||
});
|
||||
// #endregion
|
||||
|
||||
async function load() {
|
||||
setErr(null);
|
||||
@@ -127,14 +173,26 @@ export default function TestDetail() {
|
||||
setData(v);
|
||||
setChain(c);
|
||||
if (ed?.test) {
|
||||
setDraftTitle(ed.test.title || '');
|
||||
setAiGenTopic((ed.test.title || '').trim());
|
||||
setDraftDescription(ed.test.description || '');
|
||||
const loadedTitle = ed.test.title || '';
|
||||
const loadedDescription = ed.test.description || '';
|
||||
const th = ed.test.passingThreshold;
|
||||
setDraftPassing(
|
||||
th !== undefined && th !== null && String(th) !== '' ? String(th) : '70'
|
||||
const loadedPassing =
|
||||
th !== undefined && th !== null && String(th) !== '' ? String(th) : '70';
|
||||
const loadedQuestions = mapEditorToDraftQuestions(ed);
|
||||
|
||||
setDraftSnapshotOnLoad(
|
||||
buildDraftSnapshot({
|
||||
title: loadedTitle,
|
||||
description: loadedDescription,
|
||||
passing: loadedPassing,
|
||||
questions: loadedQuestions,
|
||||
})
|
||||
);
|
||||
setDraftQuestions(mapEditorToDraftQuestions(ed));
|
||||
setDraftTitle(loadedTitle);
|
||||
setAiGenTopic(loadedTitle.trim());
|
||||
setDraftDescription(loadedDescription);
|
||||
setDraftPassing(loadedPassing);
|
||||
setDraftQuestions(loadedQuestions);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.status === 401) {
|
||||
@@ -590,13 +648,40 @@ export default function TestDetail() {
|
||||
}
|
||||
|
||||
if (err) {
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H3',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:return-err',
|
||||
message: 'Returning error branch before memo hook',
|
||||
data: { hasErr: true },
|
||||
});
|
||||
// #endregion
|
||||
return <p className="error-text">{err}</p>;
|
||||
}
|
||||
if (!data && !taker) {
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H1',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:return-loading',
|
||||
message: 'Returning loading branch before memo hook',
|
||||
data: { hasData: false, hasTaker: false },
|
||||
});
|
||||
// #endregion
|
||||
return <p className="text-muted">Загрузка…</p>;
|
||||
}
|
||||
|
||||
if (taker) {
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H1',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:return-taker',
|
||||
message: 'Returning taker branch before memo hook',
|
||||
data: { hasTaker: true },
|
||||
});
|
||||
// #endregion
|
||||
const { test: t, hasActiveVersion } = taker.summary;
|
||||
const title = t?.title || 'Тест';
|
||||
return (
|
||||
@@ -635,6 +720,42 @@ export default function TestDetail() {
|
||||
const assignSelectedInList = assignPeople.filter((p) =>
|
||||
assignSelected.has(assignPersonKey(p))
|
||||
);
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H1',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:before-useMemo',
|
||||
message: 'Reached line right before hasDraftChanges useMemo',
|
||||
data: { hasData: !!data, hasTaker: !!taker },
|
||||
});
|
||||
// #endregion
|
||||
const hasDraftChanges = useMemo(() => {
|
||||
// #region agent log
|
||||
postDebugLog({
|
||||
runId: debugRunId,
|
||||
hypothesisId: 'H2',
|
||||
location: 'frontend/src/pages/TestDetail.jsx:inside-useMemo',
|
||||
message: 'Computing hasDraftChanges',
|
||||
data: {
|
||||
hasDraftSnapshotOnLoad: !!draftSnapshotOnLoad,
|
||||
titleLen: (draftTitle || '').length,
|
||||
descriptionLen: (draftDescription || '').length,
|
||||
passing: String(draftPassing || ''),
|
||||
questionsCount: Array.isArray(draftQuestions) ? draftQuestions.length : -1,
|
||||
},
|
||||
});
|
||||
// #endregion
|
||||
if (!draftSnapshotOnLoad) {
|
||||
return false;
|
||||
}
|
||||
const currentSnapshot = buildDraftSnapshot({
|
||||
title: draftTitle,
|
||||
description: draftDescription,
|
||||
passing: draftPassing,
|
||||
questions: draftQuestions,
|
||||
});
|
||||
return currentSnapshot !== draftSnapshotOnLoad;
|
||||
}, [draftDescription, draftPassing, draftQuestions, draftSnapshotOnLoad, draftTitle]);
|
||||
|
||||
return (
|
||||
<div className="test-detail-page test-detail-page--with-fixed-actions">
|
||||
@@ -667,7 +788,7 @@ export default function TestDetail() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{chain?.hasAnyAttempt && (
|
||||
{chain?.hasAnyAttempt && hasDraftChanges && (
|
||||
<div className="callout callout--warning" role="status" style={{ marginTop: '0.75rem' }}>
|
||||
При сохранении будет создана новая версия теста.
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user