сервисный журнал, фикс мультиклика на инструменты
This commit is contained in:
+39
-4
@@ -114,6 +114,9 @@ async def post_requests(
|
|||||||
if isinstance(endDate, str):
|
if isinstance(endDate, str):
|
||||||
endDate = datetime.strptime(endDate, "%Y-%m-%d").date()
|
endDate = datetime.strptime(endDate, "%Y-%m-%d").date()
|
||||||
|
|
||||||
|
if startDate > endDate:
|
||||||
|
startDate, endDate = endDate, startDate
|
||||||
|
|
||||||
jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate)
|
jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate)
|
||||||
if isinstance(jurnal_toolkits, list):
|
if isinstance(jurnal_toolkits, list):
|
||||||
if len(jurnal_toolkits) == 0:
|
if len(jurnal_toolkits) == 0:
|
||||||
@@ -143,10 +146,42 @@ async def post_requests(
|
|||||||
"endDate": endDate.strftime("%Y-%m-%d"),
|
"endDate": endDate.strftime("%Y-%m-%d"),
|
||||||
}
|
}
|
||||||
case "jurnal_service":
|
case "jurnal_service":
|
||||||
jurnal_service = await ServiceRecordsHandler.get()
|
startDate = request_data.get("body").get(
|
||||||
if jurnal_service:
|
"startDate", date.today() - timedelta(days=7)
|
||||||
resultData["status"] = "ok"
|
)
|
||||||
resultData["data"] = jurnal_service
|
if isinstance(startDate, str):
|
||||||
|
startDate = datetime.strptime(startDate, "%Y-%m-%d").date()
|
||||||
|
|
||||||
|
endDate = request_data.get("body").get("endDate", date.today())
|
||||||
|
if isinstance(endDate, str):
|
||||||
|
endDate = datetime.strptime(endDate, "%Y-%m-%d").date()
|
||||||
|
|
||||||
|
if startDate > endDate:
|
||||||
|
startDate, endDate = endDate, startDate
|
||||||
|
|
||||||
|
jurnal_service = await ServiceRecordsHandler.getLogs(startDate, endDate)
|
||||||
|
if isinstance(jurnal_service, list):
|
||||||
|
if len(jurnal_service) == 0:
|
||||||
|
resultData["status"] = "ok"
|
||||||
|
resultData["data"] = {
|
||||||
|
"requests": [],
|
||||||
|
"users": [],
|
||||||
|
"categories": [],
|
||||||
|
"startDate": startDate.strftime("%Y-%m-%d"),
|
||||||
|
"endDate": endDate.strftime("%Y-%m-%d"),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
users = await UserHandler.getAll()
|
||||||
|
categories = await CategoryHandler.getAll()
|
||||||
|
resultData["status"] = "ok"
|
||||||
|
resultData["data"] = {
|
||||||
|
"requests": jurnal_service,
|
||||||
|
"users": users,
|
||||||
|
"categories": categories,
|
||||||
|
"startDate": startDate.strftime("%Y-%m-%d"),
|
||||||
|
"endDate": endDate.strftime("%Y-%m-%d"),
|
||||||
|
}
|
||||||
|
# logger.info(resultData.get("data"))
|
||||||
case "users":
|
case "users":
|
||||||
users = await UserHandler.getAll()
|
users = await UserHandler.getAll()
|
||||||
if users:
|
if users:
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -160,7 +160,7 @@ async def manage_toolkit(reqData: dict = Depends(requestDict)):
|
|||||||
logger.info(f"Управление инструментами")
|
logger.info(f"Управление инструментами")
|
||||||
response = {"status": "error"}
|
response = {"status": "error"}
|
||||||
action = reqData.get("body").get("action")
|
action = reqData.get("body").get("action")
|
||||||
userId = reqData.get("body").get("UserId")
|
userId = reqData.get("body").get("userId")
|
||||||
toolkitData = reqData.get("body").get("formData")
|
toolkitData = reqData.get("body").get("formData")
|
||||||
if "category_id" in toolkitData:
|
if "category_id" in toolkitData:
|
||||||
toolkitData["category_id"] = int(toolkitData.get("category_id"))
|
toolkitData["category_id"] = int(toolkitData.get("category_id"))
|
||||||
|
|||||||
+342
-4
@@ -264,7 +264,7 @@ function fillTab(tabId, tabData) {
|
|||||||
renderJurnalToolkitsTab(tabId, tabData);
|
renderJurnalToolkitsTab(tabId, tabData);
|
||||||
break;
|
break;
|
||||||
case 'jurnal_service':
|
case 'jurnal_service':
|
||||||
renderSimpleTab(tabId, tabData, 'Сервисный журнал');
|
renderJurnalServicesTab(tabId, tabData);
|
||||||
break;
|
break;
|
||||||
case 'users':
|
case 'users':
|
||||||
renderSimpleTab(tabId, tabData, 'Пользователи системы');
|
renderSimpleTab(tabId, tabData, 'Пользователи системы');
|
||||||
@@ -1454,10 +1454,16 @@ function renderToolkitCards(tabId, tools, categoriesMap, filterText = '', catego
|
|||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
const cards = container.querySelectorAll('.toolkit-card');
|
const cards = container.querySelectorAll('.toolkit-card');
|
||||||
|
let activeModal = null;
|
||||||
|
|
||||||
cards.forEach(card => {
|
cards.forEach(card => {
|
||||||
card.addEventListener('click', async event => {
|
card.addEventListener('click', async event => {
|
||||||
|
if (activeModal) return;
|
||||||
|
|
||||||
const toolId = event.currentTarget.dataset.toolid;
|
const toolId = event.currentTarget.dataset.toolid;
|
||||||
|
activeModal = true;
|
||||||
await showToolkitDetailsModal(toolId);
|
await showToolkitDetailsModal(toolId);
|
||||||
|
activeModal = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3503,11 +3509,15 @@ async function initializeToolboxTable(data, toolboxOwn, quantityMonitoring) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Добавляем обработчики для изображений
|
// Добавляем обработчики для изображений
|
||||||
|
let activeModal = null;
|
||||||
document.querySelectorAll('.toolkit-image-link').forEach(link => {
|
document.querySelectorAll('.toolkit-image-link').forEach(link => {
|
||||||
link.addEventListener('click', async (e) => {
|
link.addEventListener('click', async (e) => {
|
||||||
|
if (activeModal) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const itemId = e.currentTarget.dataset.id;
|
const itemId = e.currentTarget.dataset.id;
|
||||||
|
activeModal = true;
|
||||||
await showToolkitDetailsModal(itemId);
|
await showToolkitDetailsModal(itemId);
|
||||||
|
activeModal = null;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -5371,7 +5381,7 @@ function renderRequestsTab(tabId, tabData) {
|
|||||||
<tr data-request-id="${request.id}">
|
<tr data-request-id="${request.id}">
|
||||||
<td>
|
<td>
|
||||||
<span class="badge bg-${request.action === 'Возврат' ? 'primary' : 'warning'}">${request.action}</span>
|
<span class="badge bg-${request.action === 'Возврат' ? 'primary' : 'warning'}">${request.action}</span>
|
||||||
<span class="text-muted">${request.created_at}</span>
|
<span class="small text-muted">${request.created_at}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}</td>
|
<td>${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}</td>
|
||||||
<td>${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'}</td>
|
<td>${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'}</td>
|
||||||
@@ -5851,8 +5861,8 @@ function renderJurnalToolkitsTab(tabId, tabData) {
|
|||||||
<span class="badge bg-${request.action === 'Возврат' ? 'primary' : 'warning'}">${request.action}</span>
|
<span class="badge bg-${request.action === 'Возврат' ? 'primary' : 'warning'}">${request.action}</span>
|
||||||
${statusBadge}
|
${statusBadge}
|
||||||
</td>
|
</td>
|
||||||
<td>${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}<br><span class="text-muted">${request.created_at}</span></td>
|
<td>${userMap[request.init_user_id] || `Пользователь ${request.init_user_id}`}<br><span class="small text-muted">${request.created_at}</span></td>
|
||||||
<td>${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`}<br><span class="text-muted">${request.decided_at}</span></td>
|
<td>${userMap[request.decision_user_id] || `Пользователь ${request.decision_user_id}`}<br><span class="small text-muted">${request.decided_at}</span></td>
|
||||||
<td>${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'}</td>
|
<td>${request.source_toolbox_id ? (toolboxMap[request.source_toolbox_id] || `Склад ${request.source_toolbox_id}`) : '-'}</td>
|
||||||
<td>${request.target_toolbox_id ? (toolboxMap[request.target_toolbox_id] || `Склад ${request.target_toolbox_id}`) : '-'}</td>
|
<td>${request.target_toolbox_id ? (toolboxMap[request.target_toolbox_id] || `Склад ${request.target_toolbox_id}`) : '-'}</td>
|
||||||
<td>${request.toolkit_id ? (toolkitMap[request.toolkit_id] || `Инструмент ${request.toolkit_id}`) : '-'}</td>
|
<td>${request.toolkit_id ? (toolkitMap[request.toolkit_id] || `Инструмент ${request.toolkit_id}`) : '-'}</td>
|
||||||
@@ -5889,6 +5899,334 @@ function renderJurnalToolkitsTab(tabId, tabData) {
|
|||||||
// Первоначальный рендеринг таблицы
|
// Первоначальный рендеринг таблицы
|
||||||
renderRequestsTable();
|
renderRequestsTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderJurnalServicesTab(tabId, tabData) {
|
||||||
|
const tabContent = document.getElementById(`${tabId}-tab-content`);
|
||||||
|
const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`);
|
||||||
|
|
||||||
|
const { requests, users, categories, startDate, endDate } = tabData;
|
||||||
|
|
||||||
|
if (requests.length === 0) {
|
||||||
|
tabContent.innerHTML = `
|
||||||
|
<div class="alert alert-info m-4" role="alert">
|
||||||
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
|
Нет данных за период ${startDate} - ${endDate}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Собираем списки для фильтров
|
||||||
|
const initUsers = [...new Set(requests.map(r => r.user_id))].filter(id => id !== null);
|
||||||
|
const userMap = {};
|
||||||
|
users.forEach(user => {
|
||||||
|
userMap[user.id] = user.username;
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoriesMap = {};
|
||||||
|
categories.forEach(cat => {
|
||||||
|
categoriesMap[cat.id] = { title: cat.title, description: cat.description };
|
||||||
|
});
|
||||||
|
|
||||||
|
// Собираем типы действий (ключи из details)
|
||||||
|
const actionTypes = [...new Set(requests.map(r => {
|
||||||
|
const details = r.details;
|
||||||
|
return Object.keys(details)[0]; // Берем первый ключ как тип действия
|
||||||
|
}))];
|
||||||
|
|
||||||
|
const savedFilters = loadFromStorage(tabId);
|
||||||
|
// Фильтры
|
||||||
|
let currentFilters = {
|
||||||
|
user: savedFilters?.user || 'all',
|
||||||
|
action: savedFilters?.action || 'all'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Рендерим дополнительный контейнер с фильтрами
|
||||||
|
tabOptionalContent.innerHTML = `
|
||||||
|
<div class="row align-items-center mb-3">
|
||||||
|
<!-- Фильтры слева -->
|
||||||
|
<div class="col-12 col-md-8 mb-2 mb-md-0">
|
||||||
|
<div class="row g-2 gap-1 mb-2">
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<i class="bi bi-person"></i>
|
||||||
|
</span>
|
||||||
|
<select class="form-select" id="${tabId}-user-filter">
|
||||||
|
<option value="all">Все пользователи</option>
|
||||||
|
<option value="system">Система</option>
|
||||||
|
${initUsers.map(userId => `
|
||||||
|
<option value="${userId}">${userMap[userId] || `Пользователь ${userId}`}</option>
|
||||||
|
`).join('')}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<button class="btn btn-outline-secondary" id="${tabId}-filter-reset-btn">
|
||||||
|
<i class="bi bi-x-circle me-1"></i>Сброс
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text">
|
||||||
|
<i class="bi bi-gear"></i>
|
||||||
|
</span>
|
||||||
|
<select class="form-select" id="${tabId}-action-filter">
|
||||||
|
<option value="all">Все действия</option>
|
||||||
|
${actionTypes.map(action => `
|
||||||
|
<option value="${action}">${action}</option>
|
||||||
|
`).join('')}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Фильтры справа -->
|
||||||
|
<div class="col-12 col-md-4 pe-3">
|
||||||
|
<div class="d-flex flex-wrap gap-2 justify-content-md-end">
|
||||||
|
<div class="input-group date">
|
||||||
|
<span class="input-group-text" style="width: 170px;">
|
||||||
|
<i class="bi bi-calendar me-1"></i>Дата начала:
|
||||||
|
</span>
|
||||||
|
<input type="date" class="form-control" id="${tabId}-date-from">
|
||||||
|
</div>
|
||||||
|
<div class="input-group date">
|
||||||
|
<span class="input-group-text" style="width: 170px;">
|
||||||
|
<i class="bi bi-calendar me-1"></i>Дата окончания:
|
||||||
|
</span>
|
||||||
|
<input type="date" class="form-control" id="${tabId}-date-to">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-outline-primary" id="${tabId}-date-update-btn">
|
||||||
|
<i class="bi bi-arrow-clockwise me-1"></i>Обновить журнал
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const filterResetBtn = document.getElementById(`${tabId}-filter-reset-btn`);
|
||||||
|
filterResetBtn.addEventListener('click', () => {
|
||||||
|
currentFilters = {
|
||||||
|
user: 'all',
|
||||||
|
action: 'all'
|
||||||
|
};
|
||||||
|
document.getElementById(`${tabId}-user-filter`).value = currentFilters.user;
|
||||||
|
document.getElementById(`${tabId}-action-filter`).value = currentFilters.action;
|
||||||
|
saveToStorage(tabId, currentFilters);
|
||||||
|
renderServicesTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
const startDateInput = document.getElementById(`${tabId}-date-from`);
|
||||||
|
const endDateInput = document.getElementById(`${tabId}-date-to`);
|
||||||
|
startDateInput.value = startDate;
|
||||||
|
endDateInput.value = endDate;
|
||||||
|
|
||||||
|
const refreshDateBtn = document.getElementById(`${tabId}-date-update-btn`);
|
||||||
|
refreshDateBtn.addEventListener('click', async () => {
|
||||||
|
const newStartDate = startDateInput.value;
|
||||||
|
const newEndDate = endDateInput.value;
|
||||||
|
const newDateRequestData = {
|
||||||
|
tabId: tabId,
|
||||||
|
startDate: newStartDate,
|
||||||
|
endDate: newEndDate
|
||||||
|
};
|
||||||
|
if (newStartDate && newEndDate) {
|
||||||
|
tabContent.innerHTML = `
|
||||||
|
<div class="alert alert-info m-4" role="alert">
|
||||||
|
<i class="bi bi-info-circle me-2"></i>
|
||||||
|
Загрузка данных...
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
const newPeriodData = await apiRequest('/', newDateRequestData);
|
||||||
|
if (newPeriodData.status == 'ok') {
|
||||||
|
renderJurnalServicesTab(tabId, newPeriodData.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Рендерим основной контейнер с таблицей сервисных событий
|
||||||
|
tabContent.innerHTML = `
|
||||||
|
<div class="card border-0 shadow-sm">
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover mb-0" id="${tabId}-services-table">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th width="110">Дата</th>
|
||||||
|
<th width="200">Пользователь</th>
|
||||||
|
<th width="200">Действие</th>
|
||||||
|
<th>Детали</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="${tabId}-services-body">
|
||||||
|
<!-- События будут вставлены здесь -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="text-center p-3 border-top" id="${tabId}-no-services" style="display: none;">
|
||||||
|
<i class="bi bi-inbox fs-1 text-muted"></i>
|
||||||
|
<p class="text-muted mt-2">Нет событий по выбранным фильтрам</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Функция для фильтрации событий
|
||||||
|
function filterServices() {
|
||||||
|
let filtered = requests;
|
||||||
|
|
||||||
|
// Фильтр по пользователю
|
||||||
|
if (currentFilters.user !== 'all') {
|
||||||
|
if (currentFilters.user === 'system') {
|
||||||
|
filtered = filtered.filter(r => r.user_id === null);
|
||||||
|
} else {
|
||||||
|
filtered = filtered.filter(r => r.user_id == currentFilters.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Фильтр по типу действия
|
||||||
|
if (currentFilters.action !== 'all') {
|
||||||
|
filtered = filtered.filter(r => {
|
||||||
|
const details = r.details;
|
||||||
|
return Object.keys(details)[0] === currentFilters.action;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для рендеринга строк таблицы
|
||||||
|
function renderServicesTable() {
|
||||||
|
const tbody = document.getElementById(`${tabId}-services-body`);
|
||||||
|
const noServicesDiv = document.getElementById(`${tabId}-no-services`);
|
||||||
|
const filteredServices = filterServices();
|
||||||
|
|
||||||
|
if (filteredServices.length === 0) {
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
noServicesDiv.style.display = 'block';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
noServicesDiv.style.display = 'none';
|
||||||
|
|
||||||
|
tbody.innerHTML = filteredServices.map(service => {
|
||||||
|
const actionType = Object.keys(service.details)[0];
|
||||||
|
const actionData = service.details[actionType];
|
||||||
|
|
||||||
|
// Определяем пользователя
|
||||||
|
let userName = 'Система';
|
||||||
|
if (service.user_id) {
|
||||||
|
userName = userMap[service.user_id] || `Пользователь ${service.user_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Форматируем детали в зависимости от типа действия
|
||||||
|
let detailsHtml = '';
|
||||||
|
|
||||||
|
if (actionType === 'Авторизован пользователь') {
|
||||||
|
// Для авторизации
|
||||||
|
detailsHtml = `
|
||||||
|
<div class="fw-semibold">${actionData}</div>
|
||||||
|
`;
|
||||||
|
} else if (actionType.includes('Добавлен') || actionType.includes('Обновлен') || actionType.includes('Добавлена')) {
|
||||||
|
// Для добавления/обновления сущностей
|
||||||
|
const entityName = actionData.title || actionData.username || actionData.login || '';
|
||||||
|
detailsHtml = `
|
||||||
|
<div class="fw-semibold">${entityName}</div>
|
||||||
|
${actionData.description ? `<div class="text-muted small mt-1">${actionData.description}</div>` : ''}
|
||||||
|
${actionData.id ? `<div class="text-muted small">ID: ${actionData.id}</div>` : ''}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Для инструментов добавляем дополнительные поля
|
||||||
|
if (actionData.specifications && Object.keys(actionData.specifications).length > 0) {
|
||||||
|
detailsHtml += `<div class="mt-2"><strong>Характеристики:</strong></div>`;
|
||||||
|
detailsHtml += `<div class="small text-muted">`;
|
||||||
|
for (const [key, value] of Object.entries(actionData.specifications)) {
|
||||||
|
detailsHtml += `<div>${key}: ${value}</div>`;
|
||||||
|
}
|
||||||
|
detailsHtml += `</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionData.external_link) {
|
||||||
|
detailsHtml += `<div class="mt-2"><strong>Ссылка:</strong> <a href="${actionData.external_link}" target="_blank">${actionData.external_link}</a></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionData.quantity_min || actionData.quantity_min_extra) {
|
||||||
|
detailsHtml += `<div class="mt-2"><strong>Мониторинг остатков:</strong></div>`;
|
||||||
|
if (actionData.quantity_min && actionData.quantity_min_extra) {
|
||||||
|
detailsHtml += `<div class="small text-muted">Минимальное количество: ${actionData.quantity_min}</div>`;
|
||||||
|
detailsHtml += `<div class="small text-muted">Минимальное критическое количество: ${actionData.quantity_min_extra}</div>`;
|
||||||
|
} else if (actionData.quantity_min && !actionData.quantity_min_extra) {
|
||||||
|
detailsHtml += `<div class="small text-muted">Минимальное количество: ${actionData.quantity_min}</div>`;
|
||||||
|
} else if (actionData.quantity_min_extra && !actionData.quantity_min) {
|
||||||
|
detailsHtml += `<div class="small text-muted">Минимальное критическое количество: ${actionData.quantity_min_extra}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionData.category_id) {
|
||||||
|
detailsHtml += `<div class="mt-2"><span class="fw-bold">Категория:</span> <span class="fw-medium">${categoriesMap[actionData.category_id].title}</span> [${categoriesMap[actionData.category_id].description}]</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionData.image) {
|
||||||
|
detailsHtml += `<div class="mt-2, fw-bold">Изображения:</div>`;
|
||||||
|
detailsHtml += `<div class="small text-muted">Основное:<div>`;
|
||||||
|
detailsHtml += `<div class="mt-2"><img src="${actionData.image.main}" class="img-thumbnail" style="width: 64px; height: 64px;" alt="Основное изображение инструмента">`;
|
||||||
|
if (actionData.image.additional) {
|
||||||
|
detailsHtml += `<div class="small text-muted">Дополнительные:<div>`;
|
||||||
|
detailsHtml += `<div class="d-flex mt-2">`;
|
||||||
|
actionData.image.additional.forEach(img => {
|
||||||
|
detailsHtml += `<div><img src="${img}" class="img-thumbnail" style="width: 64px; height: 64px;" alt="Дополнительное изображение инструмента"></div>`;
|
||||||
|
});
|
||||||
|
detailsHtml += `</div>`;
|
||||||
|
}
|
||||||
|
detailsHtml += `</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr data-service-id="${service.id}">
|
||||||
|
<td>
|
||||||
|
<span class="text-muted">${service.created_at}</span>
|
||||||
|
</td>
|
||||||
|
<td>${userName}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-info">${actionType}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
${detailsHtml}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Назначаем обработчики событий для фильтров
|
||||||
|
document.getElementById(`${tabId}-user-filter`).addEventListener('change', function () {
|
||||||
|
currentFilters.user = this.value;
|
||||||
|
saveToStorage(tabId, currentFilters);
|
||||||
|
renderServicesTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById(`${tabId}-action-filter`).addEventListener('change', function () {
|
||||||
|
currentFilters.action = this.value;
|
||||||
|
saveToStorage(tabId, currentFilters);
|
||||||
|
renderServicesTable();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Устанавливаем сохраненные значения фильтров
|
||||||
|
if (savedFilters) {
|
||||||
|
document.getElementById(`${tabId}-user-filter`).value = currentFilters.user;
|
||||||
|
document.getElementById(`${tabId}-action-filter`).value = currentFilters.action;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Первоначальный рендеринг таблицы
|
||||||
|
renderServicesTable();
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
await getCookieData();
|
await getCookieData();
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -287,3 +287,35 @@ class ServiceRecordsHandler:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка получения записей: {str(e)}")
|
logger.error(f"Ошибка получения записей: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def getLogs(startDate: date, endDate: date):
|
||||||
|
from db import CRUD
|
||||||
|
|
||||||
|
try:
|
||||||
|
start_dt = datetime.combine(startDate, time.min)
|
||||||
|
end_dt = datetime.combine(endDate, time.max)
|
||||||
|
|
||||||
|
query = (
|
||||||
|
select(ServicesRecords)
|
||||||
|
.where(
|
||||||
|
ServicesRecords.created_at.between(start_dt, end_dt),
|
||||||
|
)
|
||||||
|
.order_by(ServicesRecords.created_at.desc())
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Получение записей за период %s - %s", startDate, endDate)
|
||||||
|
|
||||||
|
records = await CRUD.read(query, True)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"%d записей за период %s - %s успешно получены",
|
||||||
|
len(records),
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
)
|
||||||
|
|
||||||
|
return [record.toDict() for record in records]
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Ошибка получения записей")
|
||||||
|
return []
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class ToolkitHandler:
|
|||||||
f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}"
|
f"Инструмент {editedToolkit.title} успешно обновлен, изменены данные: {kwargs.keys()}"
|
||||||
)
|
)
|
||||||
await ServiceRecordsHandler.add(
|
await ServiceRecordsHandler.add(
|
||||||
user_id, {f"Обновлен инструмент {toolkit.title}": editedToolkit.toDict()}
|
user_id, {f"Обновлен инструмент": editedToolkit.toDict()}
|
||||||
)
|
)
|
||||||
return editedToolkit.toDict()
|
return editedToolkit.toDict()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user