Files
iiko-connector/frontend/src/views/OlapQueriesPage.vue
2026-05-07 18:31:53 +03:00

146 lines
7.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<AppLayout>
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold text-gray-900">OLAP запросы</h1>
<router-link to="/olap/constructor" class="btn-primary">+ Создать запрос</router-link>
</div>
<div class="card overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Название</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Активен</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Последнее выполнение</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Результат</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Подключение</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Рестораны</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Создан</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Действия</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
<tr v-for="q in queries" :key="q.id" class="hover:bg-gray-50">
<td class="px-6 py-4 text-sm">{{ q.id }}</td>
<td class="px-6 py-4 text-sm font-medium">{{ q.name }}</td>
<td class="px-6 py-4 text-sm">
<span :class="q.active ? 'text-green-600' : 'text-red-600'">
{{ q.active ? 'Да' : 'Нет' }}
</span>
</td>
<td class="px-6 py-4 text-sm">{{ q.lastRun ? formatDate(q.lastRun) : '—' }}</td>
<td class="px-6 py-4 text-sm">
<span v-if="q.lastRunSuccess === null"></span>
<span v-else-if="q.lastRunSuccess" class="text-green-600">Успешно</span>
<span v-else class="text-red-600">Ошибка</span>
</td>
<td class="px-6 py-4 text-sm">{{ q.dbConnectionName }}</td>
<td class="px-6 py-4 text-sm">{{ q.restaurants }}</td>
<td class="px-6 py-4 text-sm">{{ formatDate(q.created) }}</td>
<td class="px-6 py-4 text-right space-x-2">
<router-link :to="`/olap/constructor/${q.id}`" class="text-blue-600 hover:text-blue-800">
<svg class="w-5 h-5 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</router-link>
<button @click="confirmDelete(q.id)" class="text-red-600 hover:text-red-800">
<svg class="w-5 h-5 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</td>
</tr>
<tr v-if="queries.length === 0">
<td colspan="6" class="px-6 py-12 text-center text-gray-500">Нет запросов. Создайте первый!</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Модальное окно удаления с улучшенной стилизацией -->
<Teleport to="body">
<Transition name="fade">
<div v-if="deleteModal.show" class="fixed inset-0 z-[9999] overflow-y-auto" @click.self="deleteModal.show = false">
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm"></div>
<div class="flex items-center justify-center min-h-screen p-4">
<div class="relative bg-white rounded-2xl shadow-xl max-w-md w-full transform transition-all">
<div class="p-6 text-center">
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4">
<svg class="h-6 w-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Удалить запрос?</h3>
<p class="text-sm text-gray-500 mb-6">Действие необратимо. Вы уверены?</p>
<div class="flex justify-center space-x-3">
<button @click="deleteModal.show = false" class="btn-secondary">Отмена</button>
<button @click="deleteQuery" class="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700 transition-colors">Удалить</button>
</div>
</div>
</div>
</div>
</div>
</Transition>
</Teleport>
</AppLayout>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import AppLayout from '@/components/Layout/AppLayout.vue'
import { useNotification } from '@/composables/useNotification'
const { showNotification } = useNotification()
const queries = ref([])
const deleteModal = ref({ show: false, id: null as number | null })
async function loadQueries() {
try {
const res = await fetch('/api/olap/queries')
if (!res.ok) throw new Error()
queries.value = await res.json()
} catch (e) {
showNotification('Ошибка загрузки запросов', 'error')
}
}
function formatDate(dateStr: string | null) {
return dateStr ? new Date(dateStr).toLocaleString() : '-'
}
function confirmDelete(id: number) {
deleteModal.value = { show: true, id }
}
async function deleteQuery() {
const id = deleteModal.value.id
if (!id) return
try {
const res = await fetch(`/api/olap/queries/${id}`, { method: 'DELETE' })
if (!res.ok) throw new Error()
showNotification('Запрос удалён', 'success')
await loadQueries()
} catch (e) {
showNotification('Ошибка удаления', 'error')
} finally {
deleteModal.value.show = false
}
}
onMounted(loadQueries)
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>