Show BIM comment API failures

This commit is contained in:
CODEX 2026-06-20 18:58:39 +03:00
parent 2b89977083
commit 2aab1bc9b4
3 changed files with 55 additions and 12 deletions

View File

@ -2197,6 +2197,12 @@ header {
border-top: 1px solid rgba(255, 255, 255, 0.04);
}
.comment-card-modal__footer-error {
grid-column: 1 / -1;
min-height: 0;
padding: 0 2px 2px;
}
.comment-card-modal__footer .primary-btn,
.comment-card-modal__footer .secondary-btn {
min-width: 0;

View File

@ -544,6 +544,13 @@ const formatDateTime = (value) => {
}
};
const setCommentModalError = (message = "") => {
if (!commentModalError) return;
const text = String(message || "").trim();
commentModalError.textContent = text;
commentModalError.hidden = !text;
};
const bringFloatingWindowToFront = (id) => {
if (!id) return;
floatingWindowOrder = [...floatingWindowOrder.filter((item) => item !== id), id];
@ -938,8 +945,8 @@ const readAttachmentFiles = async (fileList) => {
.filter((file) => file && file.size <= COMMENT_FILE_LIMIT_BYTES)
.slice(0, 8);
const skipped = Array.from(fileList || []).filter((file) => file && file.size > COMMENT_FILE_LIMIT_BYTES).length;
if (skipped && commentModalError) {
commentModalError.textContent = `Файлы больше 50 МБ не добавлены: ${skipped}`;
if (skipped) {
setCommentModalError(`Файлы больше 50 МБ не добавлены: ${skipped}`);
}
const readOne = (file) => new Promise((resolve) => {
const reader = new FileReader();
@ -1215,7 +1222,7 @@ const openCommentModalForTarget = () => {
? `Последнее редактирование: новая точка ${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}`
: "Последнее редактирование: новый комментарий";
}
if (commentModalError) commentModalError.textContent = "";
setCommentModalError("");
resetCommentModalAttachments();
renderCommentThread(null);
commentModalBackdrop?.classList.remove("hidden");
@ -1242,7 +1249,7 @@ const openCommentModalForComment = (commentId) => {
if (commentModalEditedMeta) {
commentModalEditedMeta.textContent = `Последнее редактирование: ${formatDateTime(comment.updatedAt || comment.createdAt)}`;
}
if (commentModalError) commentModalError.textContent = "";
setCommentModalError("");
resetCommentModalAttachments();
commentDraftSubitems = Array.isArray(comment.subitems)
? comment.subitems.map((item) => ({ ...item }))
@ -1266,7 +1273,7 @@ const closeCommentModal = () => {
activeCommentId = null;
commentModalMode = "create";
commentModalCommentId = null;
if (commentModalError) commentModalError.textContent = "";
setCommentModalError("");
renderCommentsPanel();
renderCommentTargets();
};
@ -1329,9 +1336,9 @@ const applyCommentModal = async ({ closeOnSave = false } = {}) => {
const title = commentTitleInput?.value?.trim() || "";
const body = commentBodyInput?.value?.trim() || "";
const replyBody = commentReplyInput?.value?.trim() || "";
if (commentModalError) commentModalError.textContent = "";
setCommentModalError("");
if (commentModalMode === "create" && !commentContextTarget?.worldPos) {
if (commentModalError) commentModalError.textContent = "Не выбрана точка комментария";
setCommentModalError("Не выбрана точка комментария");
return;
}
commentSaveInFlight = true;
@ -1399,7 +1406,7 @@ const applyCommentModal = async ({ closeOnSave = false } = {}) => {
} catch (err) {
console.error(err);
const message = err.message || "Не удалось сохранить комментарий";
if (commentModalError) commentModalError.textContent = message;
setCommentModalError(message);
setStatus(message);
} finally {
commentSaveInFlight = false;
@ -3627,8 +3634,38 @@ const guessTypeFromName = (name) => {
return null;
};
const isApiRequestUrl = (url) => {
try {
return new URL(url, window.location.href).pathname.startsWith("/api/");
} catch (_) {
return String(url || "").startsWith("/api/");
}
};
const formatFetchFailureMessage = (url, res, text, data) => {
const apiRequest = isApiRequestUrl(url);
if (apiRequest && res.status === 401) {
return "BIM API не принял запрос: нужна авторизация.";
}
if (apiRequest && res.status === 404) {
return "BIM API недоступен на этом адресе. Открой viewer через node server/index.js, не через статический http-server.";
}
if (apiRequest && /^\s*<!doctype html/i.test(text || "")) {
return `BIM API вернул HTML вместо JSON (HTTP ${res.status}). Проверь, что открыт backend-сервер BIM Viewer.`;
}
return (data && (data.message || data.error)) || text || `HTTP ${res.status}`;
};
const fetchJSON = async (url, options = {}) => {
const res = await fetch(url, options);
let res = null;
try {
res = await fetch(url, options);
} catch (err) {
if (isApiRequestUrl(url)) {
throw new Error(`BIM API недоступен: ${err?.message || "network error"}`);
}
throw err;
}
const text = await res.text();
let data = null;
try {
@ -3637,7 +3674,7 @@ const fetchJSON = async (url, options = {}) => {
// ignore parse errors, text will be used for message
}
if (!res.ok) {
const message = (data && data.message) || text || `HTTP ${res.status}`;
const message = formatFetchFailureMessage(url, res, text, data);
throw new Error(message);
}
return data;

View File

@ -413,9 +413,9 @@
<div class="comment-attachment-grid comment-attachment-grid--reply" id="commentReplyAttachmentGrid"></div>
<div class="comment-thread__list" id="commentThreadList"></div>
</section>
<div class="error-text" id="commentModalError"></div>
</div>
<div class="comment-card-modal__footer">
<div class="error-text comment-card-modal__footer-error" id="commentModalError" hidden></div>
<button class="secondary-btn" id="commentCancelButton" type="button">Отменить</button>
<button class="primary-btn" id="commentApplyButton" type="button">Применить</button>
<button class="primary-btn" id="commentSaveButton" type="button">Сохранить</button>
@ -570,6 +570,6 @@
<input id="fileInput" class="hidden" type="file"
accept=".xkt,.glb,.gltf,.bim,.las,.laz,.obj,.stl">
<script type="module" src="./dcViewer.js?v=35"></script>
<script type="module" src="./dcViewer.js?v=36"></script>
</body>
</html>