Новый экранчик для обзора дашей
This commit is contained in:
@@ -180,14 +180,15 @@ export const api = {
|
||||
getEnvironmentDatabases: (id) => fetchApi(`/environments/${id}/databases`),
|
||||
|
||||
// Dashboards
|
||||
getDashboards: (envId, options = {}) => {
|
||||
const params = new URLSearchParams({ env_id: envId });
|
||||
if (options.search) params.append('search', options.search);
|
||||
if (options.page) params.append('page', options.page);
|
||||
if (options.page_size) params.append('page_size', options.page_size);
|
||||
return fetchApi(`/dashboards?${params.toString()}`);
|
||||
},
|
||||
getDatabaseMappings: (sourceEnvId, targetEnvId) => fetchApi(`/dashboards/db-mappings?source_env_id=${sourceEnvId}&target_env_id=${targetEnvId}`),
|
||||
getDashboards: (envId, options = {}) => {
|
||||
const params = new URLSearchParams({ env_id: envId });
|
||||
if (options.search) params.append('search', options.search);
|
||||
if (options.page) params.append('page', options.page);
|
||||
if (options.page_size) params.append('page_size', options.page_size);
|
||||
return fetchApi(`/dashboards?${params.toString()}`);
|
||||
},
|
||||
getDashboardDetail: (envId, dashboardId) => fetchApi(`/dashboards/${dashboardId}?env_id=${envId}`),
|
||||
getDatabaseMappings: (sourceEnvId, targetEnvId) => fetchApi(`/dashboards/db-mappings?source_env_id=${sourceEnvId}&target_env_id=${targetEnvId}`),
|
||||
|
||||
// Datasets
|
||||
getDatasets: (envId, options = {}) => {
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div
|
||||
class="bg-white border-r border-gray-200 flex flex-col h-screen fixed left-0 top-0 z-30 transition-[width] duration-200 ease-in-out
|
||||
class="fixed left-0 top-0 z-30 flex h-screen flex-col border-r border-slate-200 bg-white shadow-sm transition-[width] duration-200 ease-in-out
|
||||
{isExpanded ? 'w-sidebar' : 'w-sidebar-collapsed'}
|
||||
{isMobileOpen
|
||||
? 'translate-x-0 w-sidebar'
|
||||
@@ -192,7 +192,7 @@
|
||||
>
|
||||
<!-- Header -->
|
||||
<div
|
||||
class="flex items-center p-4 border-b border-gray-200 {isExpanded
|
||||
class="flex items-center border-b border-slate-200 p-4 {isExpanded
|
||||
? 'justify-between'
|
||||
: 'justify-center'}"
|
||||
>
|
||||
@@ -214,7 +214,7 @@
|
||||
<div>
|
||||
<!-- Category Header -->
|
||||
<div
|
||||
class="flex items-center justify-between px-4 py-3 cursor-pointer transition-colors hover:bg-gray-100
|
||||
class="flex cursor-pointer items-center justify-between px-4 py-3 transition-colors hover:bg-slate-100
|
||||
{activeCategory === category.id
|
||||
? 'bg-primary-light text-primary md:border-r-2 md:border-primary'
|
||||
: ''}"
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<!-- Drawer Overlay -->
|
||||
{#if isOpen}
|
||||
<div
|
||||
class="fixed inset-0 bg-black/40 backdrop-blur-sm z-50"
|
||||
class="fixed inset-0 z-50 bg-black/35 backdrop-blur-sm"
|
||||
on:click={handleOverlayClick}
|
||||
on:keydown={(e) => e.key === 'Escape' && handleClose()}
|
||||
role="button"
|
||||
@@ -200,13 +200,13 @@
|
||||
>
|
||||
<!-- Drawer Panel -->
|
||||
<div
|
||||
class="fixed right-0 top-0 h-full w-full max-w-[560px] bg-slate-900 shadow-[-8px_0_30px_rgba(0,0,0,0.3)] flex flex-col z-50 transition-transform duration-300 ease-out"
|
||||
class="fixed right-0 top-0 z-50 flex h-full w-full max-w-[560px] flex-col border-l border-slate-200 bg-white shadow-[-8px_0_30px_rgba(15,23,42,0.15)] transition-transform duration-300 ease-out"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Task drawer"
|
||||
>
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-5 py-3.5 border-b border-slate-800 bg-slate-900">
|
||||
<div class="flex items-center justify-between border-b border-slate-200 bg-white px-5 py-3.5">
|
||||
<div class="flex items-center gap-2.5">
|
||||
{#if !activeTaskId && recentTasks.length > 0}
|
||||
<span class="flex items-center justify-center p-1.5 mr-1 text-cyan-400">
|
||||
@@ -221,7 +221,7 @@
|
||||
<Icon name="back" size={16} strokeWidth={2} />
|
||||
</button>
|
||||
{/if}
|
||||
<h2 class="text-sm font-semibold text-slate-100 tracking-tight">
|
||||
<h2 class="text-sm font-semibold tracking-tight text-slate-900">
|
||||
{activeTaskId ? ($t.tasks?.details_logs || 'Task Details & Logs') : 'Recent Tasks'}
|
||||
</h2>
|
||||
{#if shortTaskId}
|
||||
@@ -235,7 +235,7 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="px-2.5 py-1 text-xs font-semibold rounded-md border border-slate-700 text-slate-300 bg-slate-800/60 hover:bg-slate-800 transition-colors"
|
||||
class="rounded-md border border-slate-300 bg-slate-50 px-2.5 py-1 text-xs font-semibold text-slate-700 transition-colors hover:bg-slate-100"
|
||||
on:click={goToReportsPage}
|
||||
>
|
||||
{$t.nav?.reports || "Reports"}
|
||||
@@ -261,7 +261,7 @@
|
||||
/>
|
||||
{:else if loadingTasks}
|
||||
<div class="flex flex-col items-center justify-center p-12 text-slate-500">
|
||||
<div class="w-8 h-8 border-2 border-slate-200 border-t-blue-500 rounded-full animate-spin mb-4"></div>
|
||||
<div class="mb-4 h-8 w-8 animate-spin rounded-full border-2 border-slate-200 border-t-blue-500"></div>
|
||||
<p>Loading tasks...</p>
|
||||
</div>
|
||||
{:else if recentTasks.length > 0}
|
||||
|
||||
@@ -89,14 +89,14 @@
|
||||
</script>
|
||||
|
||||
<nav
|
||||
class="bg-white border-b border-gray-200 fixed top-0 right-0 left-0 h-16 flex items-center justify-between px-4 z-40
|
||||
class="fixed left-0 right-0 top-0 z-40 flex h-16 items-center justify-between border-b border-slate-200 bg-white px-4 shadow-sm
|
||||
{isExpanded ? 'md:left-[240px]' : 'md:left-16'}"
|
||||
>
|
||||
<!-- Left section: Hamburger (mobile) + Logo -->
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- Hamburger Menu (mobile only) -->
|
||||
<button
|
||||
class="p-2 rounded-lg hover:bg-gray-100 text-gray-600 md:hidden"
|
||||
class="rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100 md:hidden"
|
||||
on:click={handleHamburgerClick}
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
@@ -106,7 +106,7 @@
|
||||
<!-- Logo/Brand -->
|
||||
<a
|
||||
href="/"
|
||||
class="flex items-center text-xl font-bold text-gray-800 hover:text-primary transition-colors"
|
||||
class="flex items-center text-xl font-bold text-slate-800 transition-colors hover:text-primary"
|
||||
>
|
||||
<span class="mr-2 inline-flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-sky-500 via-cyan-500 to-indigo-600 text-white shadow-sm">
|
||||
<Icon name="layers" size={18} strokeWidth={2.1} />
|
||||
@@ -128,10 +128,10 @@
|
||||
</div>
|
||||
|
||||
<!-- Nav Actions -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex items-center gap-3 md:gap-4">
|
||||
<!-- Activity Indicator -->
|
||||
<div
|
||||
class="relative cursor-pointer p-2 rounded-lg hover:bg-gray-100 transition-colors text-slate-600"
|
||||
class="relative cursor-pointer rounded-lg p-2 text-slate-600 transition-colors hover:bg-slate-100"
|
||||
on:click={handleActivityClick}
|
||||
on:keydown={(e) =>
|
||||
(e.key === "Enter" || e.key === " ") && handleActivityClick()}
|
||||
|
||||
@@ -24,11 +24,19 @@
|
||||
const profileLabel = $derived(typeof profile?.label === 'function' ? profile.label() : profile?.label);
|
||||
|
||||
function getStatusClass(status) {
|
||||
if (status === 'success') return 'bg-green-100 text-green-700';
|
||||
if (status === 'failed') return 'bg-red-100 text-red-700';
|
||||
if (status === 'in_progress') return 'bg-blue-100 text-blue-700';
|
||||
if (status === 'partial') return 'bg-amber-100 text-amber-700';
|
||||
return 'bg-slate-100 text-slate-700';
|
||||
if (status === 'success') return 'bg-green-100 text-green-700 ring-1 ring-green-200';
|
||||
if (status === 'failed') return 'bg-red-100 text-red-700 ring-1 ring-red-200';
|
||||
if (status === 'in_progress') return 'bg-blue-100 text-blue-700 ring-1 ring-blue-200';
|
||||
if (status === 'partial') return 'bg-amber-100 text-amber-700 ring-1 ring-amber-200';
|
||||
return 'bg-slate-100 text-slate-700 ring-1 ring-slate-200';
|
||||
}
|
||||
|
||||
function getStatusLabel(status) {
|
||||
if (status === 'success') return $t.reports?.status_success || 'Success';
|
||||
if (status === 'failed') return $t.reports?.status_failed || 'Failed';
|
||||
if (status === 'in_progress') return $t.reports?.status_in_progress || 'In progress';
|
||||
if (status === 'partial') return $t.reports?.status_partial || 'Partial';
|
||||
return status || ($t.reports?.not_provided || 'Not provided');
|
||||
}
|
||||
|
||||
function formatDate(value) {
|
||||
@@ -44,7 +52,7 @@
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="w-full rounded-lg border p-3 text-left transition hover:bg-slate-50 {selected ? 'border-blue-400 bg-blue-50' : 'border-slate-200 bg-white'}"
|
||||
class="w-full rounded-xl border p-4 text-left shadow-sm transition hover:border-slate-300 hover:bg-slate-50 hover:shadow {selected ? 'border-blue-400 bg-blue-50' : 'border-slate-200 bg-white'}"
|
||||
on:click={onSelect}
|
||||
aria-label={`Report ${report?.report_id || ''} type ${profileLabel || ($t.reports?.unknown_type || 'Other / Unknown Type')}`}
|
||||
>
|
||||
@@ -53,7 +61,7 @@
|
||||
{profileLabel || ($t.reports?.unknown_type || 'Other / Unknown Type')}
|
||||
</span>
|
||||
<span class="rounded px-2 py-0.5 text-xs font-semibold {getStatusClass(report?.status)}">
|
||||
{report?.status || ($t.reports?.not_provided || 'Not provided')}
|
||||
{getStatusLabel(report?.status)}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-sm font-medium text-slate-800">{report?.summary || ($t.reports?.not_provided || 'Not provided')}</p>
|
||||
|
||||
@@ -31,13 +31,13 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="rounded-lg border border-slate-200 bg-white p-4">
|
||||
<div class="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
|
||||
<h3 class="mb-3 text-sm font-semibold text-slate-700">{$t.reports?.view_details || 'View details'}</h3>
|
||||
|
||||
{#if !detail || !detail.report}
|
||||
<p class="text-sm text-slate-500">{$t.reports?.not_provided || 'Not provided'}</p>
|
||||
{:else}
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="space-y-2 text-sm text-slate-700">
|
||||
<p><span class="text-slate-500">ID:</span> {notProvided(detail.report.report_id)}</p>
|
||||
<p><span class="text-slate-500">Type:</span> {notProvided(detail.report.task_type)}</p>
|
||||
<p><span class="text-slate-500">Status:</span> {notProvided(detail.report.status)}</p>
|
||||
@@ -46,13 +46,13 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">Diagnostics</p>
|
||||
<pre class="max-h-48 overflow-auto rounded bg-slate-50 p-2 text-xs text-slate-700">{JSON.stringify(detail.diagnostics || { note: $t.reports?.not_provided || 'Not provided' }, null, 2)}</pre>
|
||||
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.diagnostics || 'Diagnostics'}</p>
|
||||
<pre class="max-h-48 overflow-auto rounded-lg border border-slate-200 bg-slate-50 p-2 text-xs text-slate-700">{JSON.stringify(detail.diagnostics || { note: $t.reports?.not_provided || 'Not provided' }, null, 2)}</pre>
|
||||
</div>
|
||||
|
||||
{#if (detail.next_actions && detail.next_actions.length > 0) || (detail.report.error_context && detail.report.error_context.next_actions && detail.report.error_context.next_actions.length > 0)}
|
||||
<div class="mt-4">
|
||||
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">Next actions</p>
|
||||
<p class="mb-1 text-xs font-semibold uppercase text-slate-500">{$t.reports?.next_actions || 'Next actions'}</p>
|
||||
<ul class="list-disc space-y-1 pl-5 text-sm text-slate-700">
|
||||
{#each (detail.next_actions && detail.next_actions.length > 0 ? detail.next_actions : detail.report.error_context.next_actions) as action}
|
||||
<li>{action}</li>
|
||||
|
||||
@@ -89,12 +89,8 @@
|
||||
"storage_repo_pattern": "Repository Directory Pattern",
|
||||
"storage_filename_pattern": "Filename Pattern",
|
||||
"storage_preview": "Path Preview",
|
||||
"environments": "Superset Environments",
|
||||
"env_description": "Configure Superset environments for dashboards and datasets.",
|
||||
"env_add": "Add Environment",
|
||||
"env_actions": "Actions",
|
||||
"env_test": "Test",
|
||||
"env_delete": "Delete",
|
||||
"connections_description": "Configure database connections for data mapping.",
|
||||
"llm_description": "Configure LLM providers for dataset documentation.",
|
||||
"logging": "Logging Configuration",
|
||||
@@ -161,8 +157,6 @@
|
||||
"action_migrate": "Migrate",
|
||||
"action_backup": "Backup",
|
||||
"action_commit": "Commit",
|
||||
"git_status": "Git Status",
|
||||
"last_task": "Last Task",
|
||||
"view_task": "View task",
|
||||
"task_running": "Running...",
|
||||
"task_done": "Done",
|
||||
@@ -170,23 +164,25 @@
|
||||
"task_waiting": "Waiting",
|
||||
"status_synced": "Synced",
|
||||
"status_diff": "Diff",
|
||||
"status_synced": "Synced",
|
||||
"status_diff": "Diff",
|
||||
"status_error": "Error",
|
||||
"task_running": "Running...",
|
||||
"task_done": "Done",
|
||||
"task_failed": "Failed",
|
||||
"task_waiting": "Waiting",
|
||||
"view_task": "View task",
|
||||
"empty": "No dashboards found"
|
||||
},
|
||||
"reports": {
|
||||
"title": "Reports",
|
||||
"empty": "No reports available.",
|
||||
"filtered_empty": "No reports match your filters.",
|
||||
"loading": "Loading reports...",
|
||||
"retry_load": "Retry loading",
|
||||
"clear_filters": "Clear filters",
|
||||
"unknown_type": "Other / Unknown Type",
|
||||
"not_provided": "Not provided",
|
||||
"view_details": "View details"
|
||||
"view_details": "View details",
|
||||
"diagnostics": "Diagnostics",
|
||||
"next_actions": "Next actions",
|
||||
"status_success": "Success",
|
||||
"status_failed": "Failed",
|
||||
"status_in_progress": "In progress",
|
||||
"status_partial": "Partial"
|
||||
},
|
||||
"datasets": {
|
||||
"empty": "No datasets found",
|
||||
|
||||
@@ -89,12 +89,8 @@
|
||||
"storage_repo_pattern": "Шаблон директории репозиториев",
|
||||
"storage_filename_pattern": "Шаблон имени файла",
|
||||
"storage_preview": "Предпросмотр пути",
|
||||
"environments": "Окружения Superset",
|
||||
"env_description": "Настройка окружений Superset для дашбордов и датасетов.",
|
||||
"env_add": "Добавить окружение",
|
||||
"env_actions": "Действия",
|
||||
"env_test": "Тест",
|
||||
"env_delete": "Удалить",
|
||||
"connections_description": "Настройка подключений к базам данных для маппинга.",
|
||||
"llm_description": "Настройка LLM провайдеров для документирования датасетов.",
|
||||
"logging": "Настройка логирования",
|
||||
@@ -160,8 +156,6 @@
|
||||
"action_migrate": "Мигрировать",
|
||||
"action_backup": "Создать бэкап",
|
||||
"action_commit": "Зафиксировать",
|
||||
"git_status": "Статус Git",
|
||||
"last_task": "Последняя задача",
|
||||
"view_task": "Просмотреть задачу",
|
||||
"task_running": "Выполняется...",
|
||||
"task_done": "Готово",
|
||||
@@ -169,23 +163,25 @@
|
||||
"task_waiting": "Ожидание",
|
||||
"status_synced": "Синхронизировано",
|
||||
"status_diff": "Различия",
|
||||
"status_synced": "Синхронизировано",
|
||||
"status_diff": "Различия",
|
||||
"status_error": "Ошибка",
|
||||
"task_running": "Выполняется...",
|
||||
"task_done": "Готово",
|
||||
"task_failed": "Ошибка",
|
||||
"task_waiting": "Ожидание",
|
||||
"view_task": "Просмотреть задачу",
|
||||
"empty": "Дашборды не найдены"
|
||||
},
|
||||
"reports": {
|
||||
"title": "Отчеты",
|
||||
"empty": "Отчеты отсутствуют.",
|
||||
"filtered_empty": "Нет отчетов по выбранным фильтрам.",
|
||||
"loading": "Загрузка отчетов...",
|
||||
"retry_load": "Повторить загрузку",
|
||||
"clear_filters": "Сбросить фильтры",
|
||||
"unknown_type": "Прочее / Неизвестный тип",
|
||||
"not_provided": "Не указано",
|
||||
"view_details": "Подробнее"
|
||||
"view_details": "Подробнее",
|
||||
"diagnostics": "Диагностика",
|
||||
"next_actions": "Следующие действия",
|
||||
"status_success": "Успешно",
|
||||
"status_failed": "Ошибка",
|
||||
"status_in_progress": "В процессе",
|
||||
"status_partial": "Частично"
|
||||
},
|
||||
"datasets": {
|
||||
"empty": "Датасеты не найдены",
|
||||
|
||||
Reference in New Issue
Block a user