fix(qfs): project ui, config naming, sync timestamps - v1.5.4
This commit is contained in:
@@ -203,6 +203,8 @@ let projectsCache = [];
|
||||
let projectNameByUUID = {};
|
||||
let projectCodeByUUID = {};
|
||||
let projectVariantByUUID = {};
|
||||
let configProjectUUIDByUUID = {};
|
||||
let configNameByUUID = {};
|
||||
let pendingMoveConfigUUID = '';
|
||||
let pendingMoveProjectCode = '';
|
||||
let pendingCreateConfigName = '';
|
||||
@@ -343,6 +345,45 @@ function findProjectByInput(input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function resolveUniqueConfigName(baseName, projectUUID, excludeUUID) {
|
||||
const cleanedBase = (baseName || '').trim();
|
||||
if (!cleanedBase) {
|
||||
return {error: 'Введите название'};
|
||||
}
|
||||
|
||||
let configs = [];
|
||||
if (projectUUID) {
|
||||
const resp = await fetch('/api/projects/' + projectUUID + '/configs?status=all');
|
||||
if (!resp.ok) {
|
||||
return {error: 'Не удалось проверить конфигурации проекта'};
|
||||
}
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
configs = Array.isArray(data.configurations) ? data.configurations : [];
|
||||
} else {
|
||||
configs = Object.keys(configProjectUUIDByUUID)
|
||||
.filter(uuid => !configProjectUUIDByUUID[uuid])
|
||||
.map(uuid => ({uuid: uuid, name: configNameByUUID[uuid] || ''}));
|
||||
}
|
||||
|
||||
const used = new Set(
|
||||
configs
|
||||
.filter(cfg => !excludeUUID || cfg.uuid !== excludeUUID)
|
||||
.map(cfg => (cfg.name || '').trim().toLowerCase())
|
||||
);
|
||||
|
||||
if (!used.has(cleanedBase.toLowerCase())) {
|
||||
return {name: cleanedBase, changed: false};
|
||||
}
|
||||
|
||||
let candidate = cleanedBase + '_копия';
|
||||
let suffix = 2;
|
||||
while (used.has(candidate.toLowerCase())) {
|
||||
candidate = cleanedBase + '_копия' + suffix;
|
||||
suffix++;
|
||||
}
|
||||
return {name: candidate, changed: true};
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
@@ -385,14 +426,23 @@ function closeRenameModal() {
|
||||
|
||||
async function renameConfig() {
|
||||
const uuid = document.getElementById('rename-uuid').value;
|
||||
const name = document.getElementById('rename-input').value.trim();
|
||||
const rawName = document.getElementById('rename-input').value.trim();
|
||||
|
||||
if (!name) {
|
||||
if (!rawName) {
|
||||
alert('Введите название');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await resolveUniqueConfigName(rawName, configProjectUUIDByUUID[uuid] || '', uuid);
|
||||
if (result.error) {
|
||||
alert(result.error);
|
||||
return;
|
||||
}
|
||||
const name = result.name;
|
||||
if (result.changed) {
|
||||
document.getElementById('rename-input').value = name;
|
||||
}
|
||||
const resp = await fetch('/api/configs/' + uuid + '/rename', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
@@ -416,7 +466,7 @@ async function renameConfig() {
|
||||
|
||||
function openCloneModal(uuid, currentName) {
|
||||
document.getElementById('clone-uuid').value = uuid;
|
||||
document.getElementById('clone-input').value = currentName + ' (копия)';
|
||||
document.getElementById('clone-input').value = currentName + '_копия';
|
||||
document.getElementById('clone-modal').classList.remove('hidden');
|
||||
document.getElementById('clone-modal').classList.add('flex');
|
||||
document.getElementById('clone-input').focus();
|
||||
@@ -430,14 +480,23 @@ function closeCloneModal() {
|
||||
|
||||
async function cloneConfig() {
|
||||
const uuid = document.getElementById('clone-uuid').value;
|
||||
const name = document.getElementById('clone-input').value.trim();
|
||||
const rawName = document.getElementById('clone-input').value.trim();
|
||||
|
||||
if (!name) {
|
||||
if (!rawName) {
|
||||
alert('Введите название');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await resolveUniqueConfigName(rawName, configProjectUUIDByUUID[uuid] || '', uuid);
|
||||
if (result.error) {
|
||||
alert(result.error);
|
||||
return;
|
||||
}
|
||||
const name = result.name;
|
||||
if (result.changed) {
|
||||
document.getElementById('clone-input').value = name;
|
||||
}
|
||||
const resp = await fetch('/api/configs/' + uuid + '/clone', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -851,6 +910,12 @@ async function loadConfigs() {
|
||||
}
|
||||
|
||||
const data = await resp.json();
|
||||
configProjectUUIDByUUID = {};
|
||||
configNameByUUID = {};
|
||||
(data.configurations || []).forEach(cfg => {
|
||||
configProjectUUIDByUUID[cfg.uuid] = cfg.project_uuid || '';
|
||||
configNameByUUID[cfg.uuid] = cfg.name || '';
|
||||
});
|
||||
renderConfigs(data.configurations || []);
|
||||
updatePagination(data.total);
|
||||
} catch(e) {
|
||||
|
||||
Reference in New Issue
Block a user