analysis complete
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
let analysisMatriciMap = {};
|
||||
let analysisLoadedCache = {};
|
||||
let analysisAssignedState = {};
|
||||
let analysisSelectedState = {};
|
||||
|
||||
function loadAnalysisMatrixNames() {
|
||||
return $.ajax({
|
||||
@@ -57,6 +60,475 @@
|
||||
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||
});
|
||||
}
|
||||
function readInitialAssignedAnalyses() {
|
||||
const jsonEl = document.getElementById("analysisAssignedInitialData");
|
||||
if (!jsonEl) {
|
||||
analysisAssignedState = {};
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
analysisAssignedState =
|
||||
JSON.parse(jsonEl.textContent || "{}") || {};
|
||||
} catch (e) {
|
||||
analysisAssignedState = {};
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedPartIds() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return [];
|
||||
|
||||
return Array.from(
|
||||
modal.querySelectorAll(".analysis-part-checkbox:checked"),
|
||||
).map((el) => String(el.value));
|
||||
}
|
||||
|
||||
function getCurrentSelectedAnalysisRecordKeys() {
|
||||
return Object.keys(analysisSelectedState).filter(function (key) {
|
||||
return analysisSelectedState[key] === true;
|
||||
});
|
||||
}
|
||||
|
||||
function renderAssignedAnalysesForPart(partId) {
|
||||
const container = document.getElementById(
|
||||
"analysisAssignedListPart" + partId,
|
||||
);
|
||||
if (!container) return;
|
||||
|
||||
const items = Array.isArray(analysisAssignedState[String(partId)])
|
||||
? analysisAssignedState[String(partId)]
|
||||
: [];
|
||||
|
||||
if (!items.length) {
|
||||
container.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = items
|
||||
.map(function (item) {
|
||||
const recordKey = item.analysis_recordkey || "";
|
||||
const title = item.analysis_name || "Unnamed analysis";
|
||||
const method = item.analysis_method || "";
|
||||
|
||||
return `
|
||||
<div class="analysis-assigned-chip" data-recordkey="${escapeHtml(recordKey)}">
|
||||
<div class="analysis-assigned-chip-text">
|
||||
<div class="analysis-assigned-chip-title">${escapeHtml(title)}</div>
|
||||
${method ? `<div class="analysis-assigned-chip-method">${escapeHtml(method)}</div>` : ""}
|
||||
</div>
|
||||
<button type="button"
|
||||
class="analysis-remove-btn"
|
||||
data-part-id="${escapeHtml(partId)}"
|
||||
data-recordkey="${escapeHtml(recordKey)}"
|
||||
title="Remove analysis">×</button>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderAssignedAnalysesForSelectedParts() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal.querySelectorAll(".analysis-part-item").forEach(function (item) {
|
||||
const partId = item.getAttribute("data-part-id");
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
});
|
||||
}
|
||||
|
||||
function syncSelectedAnalysisRows() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal
|
||||
.querySelectorAll(".analysis-analysis-item")
|
||||
.forEach(function (item) {
|
||||
const recordKey = item.getAttribute("data-recordkey") || "";
|
||||
if (recordKey && analysisSelectedState[recordKey]) {
|
||||
item.classList.add("analysis-selected");
|
||||
} else {
|
||||
item.classList.remove("analysis-selected");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildAnalysisPayloadFromRow(rowEl) {
|
||||
return {
|
||||
analysis_recordkey: rowEl.getAttribute("data-recordkey") || "",
|
||||
analysis_name: rowEl.getAttribute("data-analysis-name") || "",
|
||||
analysis_method: rowEl.getAttribute("data-analysis-method") || "",
|
||||
analysis_level: rowEl.getAttribute("data-analysis-level") || "",
|
||||
is_web_selectable: rowEl.getAttribute("data-web") === "1" ? 1 : 0,
|
||||
is_accredited:
|
||||
rowEl.getAttribute("data-accredited") === "1" ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
function addAnalysisToLocalState(partId, payload, iddatadb, idmatrice) {
|
||||
const key = String(partId);
|
||||
if (!Array.isArray(analysisAssignedState[key])) {
|
||||
analysisAssignedState[key] = [];
|
||||
}
|
||||
|
||||
const exists = analysisAssignedState[key].some(function (item) {
|
||||
return item.analysis_recordkey === payload.analysis_recordkey;
|
||||
});
|
||||
|
||||
if (!exists) {
|
||||
analysisAssignedState[key].push({
|
||||
id: null,
|
||||
part_id: parseInt(partId, 10),
|
||||
iddatadb: iddatadb || null,
|
||||
idmatrice: idmatrice || null,
|
||||
analysis_recordkey: payload.analysis_recordkey,
|
||||
analysis_name: payload.analysis_name,
|
||||
analysis_method: payload.analysis_method,
|
||||
analysis_level:
|
||||
payload.analysis_level !== ""
|
||||
? parseInt(payload.analysis_level, 10)
|
||||
: null,
|
||||
is_web_selectable: payload.is_web_selectable,
|
||||
is_accredited: payload.is_accredited,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeAnalysisFromLocalState(partId, recordKey) {
|
||||
const key = String(partId);
|
||||
if (!Array.isArray(analysisAssignedState[key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
analysisAssignedState[key] = analysisAssignedState[key].filter(
|
||||
function (item) {
|
||||
return item.analysis_recordkey !== recordKey;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function saveAnalysisAssociation(partId, payload, callback) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const iddatadb =
|
||||
modal.querySelector("#analysisModalIddatadb")?.value || "";
|
||||
const partItem = modal.querySelector(
|
||||
'.analysis-part-item[data-part-id="' + partId + '"]',
|
||||
);
|
||||
const idmatrice = partItem
|
||||
? partItem.getAttribute("data-matrix-id") || ""
|
||||
: "";
|
||||
|
||||
$.ajax({
|
||||
url: "save_part_analysis.php",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
part_id: partId,
|
||||
iddatadb: iddatadb,
|
||||
idmatrice: idmatrice !== "NO_MATRIX" ? idmatrice : "",
|
||||
analysis_recordkey: payload.analysis_recordkey,
|
||||
analysis_name: payload.analysis_name,
|
||||
analysis_method: payload.analysis_method,
|
||||
analysis_level: payload.analysis_level,
|
||||
is_web_selectable: payload.is_web_selectable,
|
||||
is_accredited: payload.is_accredited,
|
||||
},
|
||||
})
|
||||
.done(function () {
|
||||
addAnalysisToLocalState(
|
||||
partId,
|
||||
payload,
|
||||
iddatadb,
|
||||
idmatrice !== "NO_MATRIX" ? idmatrice : null,
|
||||
);
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
if (typeof callback === "function") callback(true);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error saving analysis association";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||
message = xhr.responseJSON.message;
|
||||
}
|
||||
alert(message);
|
||||
if (typeof callback === "function") callback(false);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAnalysisAssociation(partId, recordKey, callback) {
|
||||
$.ajax({
|
||||
url: "delete_part_analysis.php",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
part_id: partId,
|
||||
analysis_recordkey: recordKey,
|
||||
},
|
||||
})
|
||||
.done(function () {
|
||||
removeAnalysisFromLocalState(partId, recordKey);
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
if (typeof callback === "function") callback(true);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error deleting analysis association";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||
message = xhr.responseJSON.message;
|
||||
}
|
||||
alert(message);
|
||||
if (typeof callback === "function") callback(false);
|
||||
});
|
||||
}
|
||||
function setAnalysisLoadingState(isLoading) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const loadingEl = modal.querySelector("#analysisLoadingBox");
|
||||
if (!loadingEl) return;
|
||||
|
||||
if (isLoading) {
|
||||
loadingEl.classList.remove("d-none");
|
||||
} else {
|
||||
loadingEl.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showAnalysisError(message) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (emptyEl) emptyEl.classList.add("d-none");
|
||||
|
||||
if (errorEl) {
|
||||
errorEl.textContent = message || "Error loading analyses";
|
||||
errorEl.classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showAnalysisEmpty(message) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
|
||||
if (emptyEl) {
|
||||
emptyEl.textContent =
|
||||
message || "No analyses found for this matrix";
|
||||
emptyEl.classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function renderAnalysesList(analyses) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (!listEl) return;
|
||||
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
|
||||
if (!Array.isArray(analyses) || analyses.length === 0) {
|
||||
showAnalysisEmpty("No analyses found for this matrix");
|
||||
return;
|
||||
}
|
||||
|
||||
if (emptyEl) emptyEl.classList.add("d-none");
|
||||
|
||||
listEl.innerHTML = analyses
|
||||
.map(function (item) {
|
||||
const recordKey = item.RecordKey || "";
|
||||
const analysisName =
|
||||
item.NomeAnalisiTraduzione ||
|
||||
item.NomeAnalisi ||
|
||||
"Unnamed analysis";
|
||||
const methodName = item.MetodoNome || "";
|
||||
const selectable = item.SelezionabileSuWeb === true;
|
||||
const accredited = item.Accreditato === true;
|
||||
const level = item.Livello ?? "";
|
||||
const searchText = (
|
||||
analysisName +
|
||||
" " +
|
||||
methodName
|
||||
).toLowerCase();
|
||||
|
||||
let badges = "";
|
||||
if (selectable) {
|
||||
badges += '<span class="badge bg-success">Web</span>';
|
||||
} else {
|
||||
badges += '<span class="badge bg-secondary">Not web</span>';
|
||||
}
|
||||
|
||||
if (accredited) {
|
||||
badges +=
|
||||
'<span class="badge bg-info text-dark">Accredited</span>';
|
||||
}
|
||||
|
||||
if (level !== "") {
|
||||
badges +=
|
||||
'<span class="badge bg-light text-dark border">Level ' +
|
||||
escapeHtml(level) +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
return `
|
||||
<div
|
||||
class="list-group-item analysis-analysis-item"
|
||||
data-recordkey="${escapeHtml(recordKey)}"
|
||||
data-analysis-name="${escapeHtml(analysisName)}"
|
||||
data-analysis-method="${escapeHtml(methodName)}"
|
||||
data-analysis-level="${escapeHtml(level)}"
|
||||
data-web="${selectable ? "1" : "0"}"
|
||||
data-accredited="${accredited ? "1" : "0"}"
|
||||
data-search="${escapeHtml(searchText)}"
|
||||
>
|
||||
<div class="fw-semibold">${escapeHtml(analysisName)}</div>
|
||||
|
||||
${methodName ? `<div class="analysis-analysis-meta mt-1">${escapeHtml(methodName)}</div>` : ""}
|
||||
|
||||
<div class="analysis-analysis-badges mt-2">
|
||||
${badges}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
filterAnalysisList();
|
||||
syncSelectedAnalysisRows();
|
||||
}
|
||||
|
||||
function filterAnalysisList() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
const items = modal.querySelectorAll(".analysis-analysis-item");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
|
||||
const webOnly = webOnlyEl ? webOnlyEl.checked : false;
|
||||
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach((item) => {
|
||||
const isWeb = item.getAttribute("data-web") === "1";
|
||||
const searchText = (
|
||||
item.getAttribute("data-search") || ""
|
||||
).toLowerCase();
|
||||
|
||||
let visible = true;
|
||||
|
||||
if (webOnly && !isWeb) {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
if (visible && searchValue && !searchText.includes(searchValue)) {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
item.style.display = visible ? "" : "none";
|
||||
|
||||
if (visible) {
|
||||
visibleCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (errorEl) {
|
||||
errorEl.classList.add("d-none");
|
||||
}
|
||||
|
||||
if (emptyEl) {
|
||||
if (items.length === 0) {
|
||||
emptyEl.textContent = "No analyses found for this matrix";
|
||||
emptyEl.classList.remove("d-none");
|
||||
} else if (visibleCount === 0) {
|
||||
emptyEl.textContent = "No analyses match the current filters";
|
||||
emptyEl.classList.remove("d-none");
|
||||
} else {
|
||||
emptyEl.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadAnalysesByMatrix(matrixId) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||
showAnalysisEmpty("No matrix selected");
|
||||
return;
|
||||
}
|
||||
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
if (emptyEl) {
|
||||
emptyEl.textContent = "";
|
||||
emptyEl.classList.add("d-none");
|
||||
}
|
||||
|
||||
if (analysisLoadedCache[String(matrixId)]) {
|
||||
renderAnalysesList(analysisLoadedCache[String(matrixId)]);
|
||||
return;
|
||||
}
|
||||
|
||||
setAnalysisLoadingState(true);
|
||||
|
||||
$.ajax({
|
||||
url: "get_analisi_matrice_filter.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id_matrice: matrixId,
|
||||
},
|
||||
})
|
||||
.done(function (response) {
|
||||
const analyses = Array.isArray(response.value)
|
||||
? response.value
|
||||
: [];
|
||||
analysisLoadedCache[String(matrixId)] = analyses;
|
||||
renderAnalysesList(analyses);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error loading analyses";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
|
||||
message = xhr.responseJSON.error;
|
||||
}
|
||||
showAnalysisError(message);
|
||||
})
|
||||
.always(function () {
|
||||
setAnalysisLoadingState(false);
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelectedPartsInfo() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
@@ -112,8 +584,24 @@
|
||||
if (matrixLabelEl) {
|
||||
matrixLabelEl.textContent = matrixLabel || "-";
|
||||
}
|
||||
analysisSelectedState = {};
|
||||
|
||||
const selectedPartIds = getSelectedPartIds();
|
||||
selectedPartIds.forEach(function (partId) {
|
||||
const assigned = Array.isArray(
|
||||
analysisAssignedState[String(partId)],
|
||||
)
|
||||
? analysisAssignedState[String(partId)]
|
||||
: [];
|
||||
|
||||
assigned.forEach(function (item) {
|
||||
if (item.analysis_recordkey) {
|
||||
analysisSelectedState[item.analysis_recordkey] = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
updateSelectedPartsInfo();
|
||||
loadAnalysesByMatrix(matrixId);
|
||||
}
|
||||
|
||||
function clearAnalysisSelection() {
|
||||
@@ -135,6 +623,16 @@
|
||||
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||
if (matrixLabelEl) matrixLabelEl.textContent = "-";
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
if (webOnlyEl) webOnlyEl.checked = false;
|
||||
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
if (searchEl) searchEl.value = "";
|
||||
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
|
||||
showAnalysisEmpty("Select a matrix to load analyses");
|
||||
updateSelectedPartsInfo();
|
||||
}
|
||||
|
||||
@@ -142,6 +640,11 @@
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
analysisLoadedCache = {};
|
||||
analysisSelectedState = {};
|
||||
readInitialAssignedAnalyses();
|
||||
renderAssignedAnalysesForSelectedParts();
|
||||
|
||||
modal.querySelectorAll(".analysis-matrix-item").forEach((btn) => {
|
||||
btn.addEventListener("click", function () {
|
||||
modal
|
||||
@@ -171,6 +674,99 @@
|
||||
});
|
||||
}
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
if (webOnlyEl) {
|
||||
webOnlyEl.addEventListener("change", function () {
|
||||
filterAnalysisList();
|
||||
});
|
||||
}
|
||||
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
if (searchEl) {
|
||||
searchEl.addEventListener("input", function () {
|
||||
filterAnalysisList();
|
||||
});
|
||||
}
|
||||
modal.addEventListener("click", function (e) {
|
||||
const removeBtn = e.target.closest(".analysis-remove-btn");
|
||||
if (removeBtn) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const partId = removeBtn.getAttribute("data-part-id");
|
||||
const recordKey = removeBtn.getAttribute("data-recordkey");
|
||||
|
||||
deleteAnalysisAssociation(partId, recordKey, function () {
|
||||
const stillUsed = Object.keys(analysisAssignedState).some(
|
||||
function (pid) {
|
||||
return (
|
||||
Array.isArray(analysisAssignedState[pid]) &&
|
||||
analysisAssignedState[pid].some(
|
||||
function (item) {
|
||||
return (
|
||||
item.analysis_recordkey ===
|
||||
recordKey
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (!stillUsed) {
|
||||
delete analysisSelectedState[recordKey];
|
||||
}
|
||||
|
||||
syncSelectedAnalysisRows();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const row = e.target.closest(".analysis-analysis-item");
|
||||
if (!row) return;
|
||||
|
||||
const selectedPartIds = getSelectedPartIds();
|
||||
if (!selectedPartIds.length) {
|
||||
alert("Select at least one part first");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = buildAnalysisPayloadFromRow(row);
|
||||
if (!payload.analysis_recordkey) {
|
||||
alert("Invalid analysis record key");
|
||||
return;
|
||||
}
|
||||
|
||||
const recordKey = payload.analysis_recordkey;
|
||||
const alreadySelected = !!analysisSelectedState[recordKey];
|
||||
|
||||
if (alreadySelected) {
|
||||
selectedPartIds.forEach(function (partId) {
|
||||
deleteAnalysisAssociation(partId, recordKey);
|
||||
});
|
||||
|
||||
delete analysisSelectedState[recordKey];
|
||||
syncSelectedAnalysisRows();
|
||||
return;
|
||||
}
|
||||
|
||||
let pending = selectedPartIds.length;
|
||||
let atLeastOneSaved = false;
|
||||
|
||||
selectedPartIds.forEach(function (partId) {
|
||||
saveAnalysisAssociation(partId, payload, function (ok) {
|
||||
if (ok) atLeastOneSaved = true;
|
||||
|
||||
pending--;
|
||||
if (pending <= 0 && atLeastOneSaved) {
|
||||
analysisSelectedState[recordKey] = true;
|
||||
syncSelectedAnalysisRows();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const firstActiveMatrix = modal.querySelector(
|
||||
".analysis-matrix-item.active",
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user