This commit is contained in:
2026-02-18 17:29:46 +03:00
parent 026239e3bf
commit 77147dc95b
27 changed files with 5560 additions and 1506 deletions

View File

@@ -0,0 +1,16 @@
<!-- [DEF:StorageIndexPage:Page] -->
<!--
@TIER: TRIVIAL
@PURPOSE: Redirect to the backups page as the default storage view.
@LAYER: Page
@INVARIANT: Always redirects to /storage/backups.
-->
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
onMount(() => {
goto('/storage/backups');
});
</script>
<!-- [/DEF:StorageIndexPage:Page] -->

View File

@@ -0,0 +1,35 @@
<!-- [DEF:StorageBackupsPage:Page] -->
<!--
@TIER: STANDARD
@SEMANTICS: backup, page, tools
@PURPOSE: Entry point for the Backup Management interface (moved from /tools/backups).
@LAYER: Page
@RELATION: USES -> BackupManager
@INVARIANT: BackupManager component is always rendered.
-->
<script lang="ts">
/**
* @UX_STATE: Loading -> (via BackupManager) showing spinner.
* @UX_STATE: Idle -> Showing BackupManager interface.
* @UX_FEEDBACK: Toast -> (via BackupManager) success/error notifications.
*/
// [SECTION: IMPORTS]
import { t } from '$lib/i18n';
import { PageHeader } from '$lib/ui';
import BackupManager from '../../../components/backups/BackupManager.svelte';
// [/SECTION]
</script>
<!-- [SECTION: TEMPLATE] -->
<div class="container mx-auto p-4 max-w-6xl">
<PageHeader title={$t.nav?.backups || "Backups"} />
<div class="mt-6">
<BackupManager />
</div>
</div>
<!-- [/SECTION] -->
<!-- [/DEF:StorageBackupsPage:Page] -->

View File

@@ -0,0 +1,110 @@
<!-- [DEF:frontend.src.routes.storage.repos.+page:Module] -->
<!--
@TIER: STANDARD
@SEMANTICS: git, dashboard, management, ui
@PURPOSE: Dashboard management page for Git integration (moved from /git).
@LAYER: UI (Page)
@RELATION: DEPENDS_ON -> DashboardGrid
@RELATION: DEPENDS_ON -> api
@INVARIANT: Dashboard grid is always shown when an environment is selected.
-->
<!-- [DEF:StorageReposPage:Page] -->
<script lang="ts">
/**
* @UX_STATE: Loading -> Showing spinner while fetching environments/dashboards.
* @UX_STATE: Idle -> Showing dashboard grid with actions.
* @UX_FEEDBACK: Toast -> Error messages on fetch failure.
* @UX_RECOVERY: Environment Selection -> Switch environment to retry loading.
*/
import { onMount } from 'svelte';
import DashboardGrid from '../../../components/DashboardGrid.svelte';
import { addToast as toast } from '$lib/toasts.js';
import { api } from '$lib/api.js';
import type { DashboardMetadata } from '$lib/types/dashboard';
import { t } from '$lib/i18n';
import { Button, Card, PageHeader, Select } from '$lib/ui';
let environments: any[] = [];
let selectedEnvId = "";
let dashboards: DashboardMetadata[] = [];
let loading = true;
let fetchingDashboards = false;
// [DEF:fetchEnvironments:Function]
/**
* @PURPOSE: Fetches the list of available environments.
* @PRE: None.
* @POST: environments array is populated, selectedEnvId is set to first env if available.
*/
async function fetchEnvironments() {
try {
environments = await api.getEnvironmentsList();
if (environments.length > 0) {
selectedEnvId = environments[0].id;
}
} catch (e) {
toast(e.message, 'error');
} finally {
loading = false;
}
}
// [/DEF:fetchEnvironments:Function]
// [DEF:fetchDashboards:Function]
/**
* @PURPOSE: Fetches dashboards for a specific environment.
* @PRE: envId is a valid environment ID.
* @POST: dashboards array is populated with metadata for the selected environment.
*/
async function fetchDashboards(envId: string) {
if (!envId) return;
fetchingDashboards = true;
try {
dashboards = await api.requestApi(`/environments/${envId}/dashboards`);
} catch (e) {
toast(e.message, 'error');
dashboards = [];
} finally {
fetchingDashboards = false;
}
}
// [/DEF:fetchDashboards:Function]
onMount(fetchEnvironments);
$: if (selectedEnvId) {
fetchDashboards(selectedEnvId);
localStorage.setItem('selected_env_id', selectedEnvId);
}
</script>
<div class="max-w-6xl mx-auto p-6">
<PageHeader title={$t.nav?.repositories || "Git Repositories"}>
<div slot="actions" class="flex items-center space-x-4">
<Select
label="Environment"
bind:value={selectedEnvId}
options={environments.map(e => ({ value: e.id, label: e.name }))}
/>
</div>
</PageHeader>
{#if loading}
<div class="flex justify-center py-12">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
{:else}
<Card title="Select Dashboard to Manage">
{#if fetchingDashboards}
<p class="text-gray-500">Loading dashboards...</p>
{:else if dashboards.length > 0}
<DashboardGrid {dashboards} />
{:else}
<p class="text-gray-500 italic">No dashboards found in this environment.</p>
{/if}
</Card>
{/if}
</div>
<!-- [/DEF:StorageReposPage:Page] -->
<!-- [/DEF:frontend.src.routes.storage.repos.+page:Module] -->