Compare commits
No commits in common. "ac4af66cd53521b0d6c17c328eef1b3caadb8a05" and "cb67c5d93d4a60ae032ab26a15819e5537857a9b" have entirely different histories.
ac4af66cd5
...
cb67c5d93d
7 changed files with 25 additions and 288 deletions
|
|
@ -32,16 +32,11 @@
|
||||||
user = await whoami();
|
user = await whoami();
|
||||||
} else {
|
} else {
|
||||||
user = null;
|
user = null;
|
||||||
|
|
||||||
if ($page.url.pathname !== '/')
|
|
||||||
goto('/login'); // Redirect to login if not authenticated
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Authentication check failed:', error);
|
console.error('Authentication check failed:', error);
|
||||||
isAuthenticated = false;
|
isAuthenticated = false;
|
||||||
user = null;
|
user = null;
|
||||||
if ($page.url.pathname !== '/')
|
|
||||||
goto('/login'); // Redirect to login if not authenticated
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,197 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { TableHandler } from "@vincjo/datatables";
|
import { Datatable, Search, RowsPerPage, RowCount, Pagination } from '@vincjo/datatables'
|
||||||
import {
|
import { Modal } from '@sveltestrap/sveltestrap';
|
||||||
Modal,
|
import { onMount } from 'svelte';
|
||||||
Button,
|
|
||||||
FormGroup,
|
|
||||||
Label,
|
|
||||||
Input,
|
|
||||||
Alert,
|
|
||||||
Icon,
|
|
||||||
Pagination,
|
|
||||||
PaginationItem,
|
|
||||||
PaginationLink,
|
|
||||||
} from "@sveltestrap/sveltestrap";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import { addToast } from "$lib/components/Toast.svelte";
|
|
||||||
import type { SavedPoint } from "$lib/types";
|
|
||||||
import { SavedPointsStore } from "$lib/stores";
|
|
||||||
import ConfirmationPrompt from "./ConfirmationPrompt.svelte";
|
|
||||||
import { getSavedPoints, savePoint, updatePoint, deletePoint } from "$lib/api/points";
|
|
||||||
|
|
||||||
// Props
|
|
||||||
let { isOpen = $bindable(false), onClose = () => {}, onChange = () => {}, point} = $props();
|
|
||||||
|
|
||||||
// Runes
|
|
||||||
let selectedPoint = $derived<SavedPoint>(point);
|
|
||||||
let isAlertVisible = $state(false);
|
|
||||||
let isConfirmationVisible = $state(false);
|
|
||||||
let alertText = $state("");
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
onChange();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Modal controls
|
|
||||||
export function openModal() {
|
|
||||||
isOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
isOpen = false;
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDeletePoint(point: SavedPoint) {
|
|
||||||
deletePoint(point.id)
|
|
||||||
.then(() => {
|
|
||||||
$SavedPointsStore = $SavedPointsStore.filter((p) => p.id !== point.id);
|
|
||||||
SavedPointsStore.set($SavedPointsStore);
|
|
||||||
resetForm();
|
|
||||||
addToast({
|
|
||||||
header: "Точка удалена",
|
|
||||||
body: `Точка "${point.name}" успешно удалена.`,
|
|
||||||
color: "success",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
showAlert(`Ошибка при удалении точки: ${error.message}`);
|
|
||||||
console.error("Ошибка при удалении точки:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSavePoint() {
|
|
||||||
updatePoint(selectedPoint)
|
|
||||||
.then((updatedPoint) => {
|
|
||||||
$SavedPointsStore = $SavedPointsStore.map((p) => (p.id === updatedPoint.id ? updatedPoint : p));
|
|
||||||
SavedPointsStore.set($SavedPointsStore);
|
|
||||||
resetForm();
|
|
||||||
addToast({
|
|
||||||
header: "Точка обновлена",
|
|
||||||
body: `Точка "${updatedPoint.name}" успешно обновлена.`,
|
|
||||||
color: "success",
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
showAlert(`Ошибка при обновлении точки: ${error.message}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function showAlert(message: string) {
|
|
||||||
isAlertVisible = true;
|
|
||||||
alertText = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hideAlert() {
|
|
||||||
isAlertVisible = false;
|
|
||||||
alertText = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resetForm() {
|
|
||||||
hideAlert();
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal {isOpen} toggle={closeModal} size="lg" fade={false} backdrop={true} scrollable class={ isConfirmationVisible ? "modal-tinted" : ""}>
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Редактирование точки</h5>
|
|
||||||
<button type="button" class="btn-close" onclick={closeModal} aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div>
|
|
||||||
<h5>{"Редактирование точки"}</h5>
|
|
||||||
<Alert
|
|
||||||
color="danger"
|
|
||||||
isOpen={isAlertVisible}
|
|
||||||
toggle={() => (isAlertVisible = false)}
|
|
||||||
fade={false}
|
|
||||||
class="mb-2"
|
|
||||||
>
|
|
||||||
<Icon name="exclamation-triangle" class="me-2" />
|
|
||||||
{alertText}
|
|
||||||
</Alert>
|
|
||||||
<form
|
|
||||||
onsubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
handleSavePoint();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="mb-2">
|
|
||||||
<Label for="name" class="small">Название точки:</Label>
|
|
||||||
<Input class="form-control-sm" type="text" id="name" bind:value={selectedPoint.name} required />
|
|
||||||
</div>
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<FormGroup class="flex-grow-1">
|
|
||||||
<Label for="lat" class="small">Широта:</Label>
|
|
||||||
<Input
|
|
||||||
class="form-control-sm"
|
|
||||||
type="number"
|
|
||||||
step="any"
|
|
||||||
id="lat"
|
|
||||||
bind:value={selectedPoint.lat}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<span class="form-text">Градусы</span>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup class="flex-grow-1">
|
|
||||||
<Label for="lon" class="small">Долгота:</Label>
|
|
||||||
<Input
|
|
||||||
class="form-control-sm"
|
|
||||||
type="number"
|
|
||||||
step="any"
|
|
||||||
id="lon"
|
|
||||||
bind:value={selectedPoint.lon}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<span class="form-text">Градусы</span>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup class="flex-grow-1">
|
|
||||||
<Label for="alt" class="small">Высота:</Label>
|
|
||||||
<Input
|
|
||||||
class="form-control-sm"
|
|
||||||
type="number"
|
|
||||||
step="any"
|
|
||||||
id="alt"
|
|
||||||
bind:value={selectedPoint.alt}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<span class="form-text">Метры над ур. моря</span>
|
|
||||||
</FormGroup>
|
|
||||||
</div>
|
|
||||||
<div class="d-grid gap-2 d-md-flex">
|
|
||||||
<Button type="submit" color="success" size="sm">
|
|
||||||
Обновить точку
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" type="button" color="secondary" onclick={resetForm}>Отмена</Button>
|
|
||||||
<span class="flex-grow-1"></span>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
color="danger"
|
|
||||||
size="sm"
|
|
||||||
onclick={() => isConfirmationVisible = true}
|
|
||||||
>
|
|
||||||
Удалить точку
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<ConfirmationPrompt
|
|
||||||
isOpen={isConfirmationVisible}
|
|
||||||
title="Подтвердите удаление"
|
|
||||||
confirmText="Удалить"
|
|
||||||
cancelText="Отмена"
|
|
||||||
confirmVariant="danger"
|
|
||||||
onconfirm={() => {
|
|
||||||
isConfirmationVisible = false;
|
|
||||||
handleDeletePoint(selectedPoint);
|
|
||||||
}}
|
|
||||||
oncancel={() => {
|
|
||||||
isConfirmationVisible = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>Вы уверены, что хотите удалить эту точку?</p>
|
|
||||||
</ConfirmationPrompt>
|
|
||||||
|
|
@ -16,24 +16,16 @@
|
||||||
import { addToast } from "$lib/components/Toast.svelte";
|
import { addToast } from "$lib/components/Toast.svelte";
|
||||||
import type { SavedPoint } from "$lib/types";
|
import type { SavedPoint } from "$lib/types";
|
||||||
import { SavedPointsStore } from "$lib/stores";
|
import { SavedPointsStore } from "$lib/stores";
|
||||||
import ConfirmationPrompt from "./ConfirmationPrompt.svelte";
|
|
||||||
import { getSavedPoints, savePoint, updatePoint, deletePoint } from "$lib/api/points";
|
import { getSavedPoints, savePoint, updatePoint, deletePoint } from "$lib/api/points";
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
let {
|
let { isOpen = $bindable(false), onClose = () => {}, onChange = () => {} } = $props();
|
||||||
isOpen = $bindable(false),
|
|
||||||
onClose = () => {},
|
|
||||||
onChange = () => {},
|
|
||||||
point = null,
|
|
||||||
coordinates = { id: 0, name: "", lat: 0, lon: 0, alt: 0 },
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
// Runes
|
// Runes
|
||||||
let selectedPoint = $state<SavedPoint | null>(point);
|
let selectedPoint = $state<SavedPoint | null>(null);
|
||||||
let newPoint = $state<SavedPoint>(coordinates as SavedPoint);
|
let newPoint = $state<SavedPoint>({ id: 0, name: "", lat: 0, lon: 0, alt: 0 });
|
||||||
let isEditing = $state(false);
|
let isEditing = $state(false);
|
||||||
let isAlertVisible = $state(false);
|
let isAlertVisible = $state(false);
|
||||||
let isConfirmationVisible = $state(false);
|
|
||||||
let alertText = $state("");
|
let alertText = $state("");
|
||||||
|
|
||||||
// Table handler
|
// Table handler
|
||||||
|
|
@ -67,13 +59,7 @@
|
||||||
isEditing = true;
|
isEditing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmDeletePoint(point: SavedPoint) {
|
function handleDeletePoint(point: SavedPoint) {
|
||||||
selectedPoint = point;
|
|
||||||
isConfirmationVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDeletePoint(point: SavedPoint | null) {
|
|
||||||
if (!point) return;
|
|
||||||
deletePoint(point.id)
|
deletePoint(point.id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
$SavedPointsStore = $SavedPointsStore.filter((p) => p.id !== point.id);
|
$SavedPointsStore = $SavedPointsStore.filter((p) => p.id !== point.id);
|
||||||
|
|
@ -143,7 +129,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal {isOpen} toggle={closeModal} size="lg" fade={false} backdrop={true} scrollable class={isConfirmationVisible ? "modal-tinted" : ""}>
|
<Modal {isOpen} toggle={closeModal} size="lg" fade={false} backdrop={true} scrollable>
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">Сохраненные точки</h5>
|
<h5 class="modal-title">Сохраненные точки</h5>
|
||||||
<button type="button" class="btn-close" onclick={closeModal} aria-label="Close"></button>
|
<button type="button" class="btn-close" onclick={closeModal} aria-label="Close"></button>
|
||||||
|
|
@ -193,7 +179,7 @@
|
||||||
<Button color="primary" size="sm" onclick={() => handleEditPoint(row)}>
|
<Button color="primary" size="sm" onclick={() => handleEditPoint(row)}>
|
||||||
<Icon name="pencil" />
|
<Icon name="pencil" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="danger" size="sm" onclick={() => confirmDeletePoint(row)}>
|
<Button color="danger" size="sm" onclick={() => handleDeletePoint(row)}>
|
||||||
<Icon name="trash" />
|
<Icon name="trash" />
|
||||||
</Button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -289,20 +275,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<ConfirmationPrompt
|
|
||||||
isOpen={isConfirmationVisible}
|
|
||||||
title="Подтвердите удаление"
|
|
||||||
confirmText="Удалить"
|
|
||||||
cancelText="Отмена"
|
|
||||||
confirmVariant="danger"
|
|
||||||
onconfirm={() => {
|
|
||||||
isConfirmationVisible = false;
|
|
||||||
handleDeletePoint(selectedPoint);
|
|
||||||
}}
|
|
||||||
oncancel={() => {
|
|
||||||
isConfirmationVisible = false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p>Вы уверены, что хотите удалить эту точку?</p>
|
|
||||||
</ConfirmationPrompt>
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
Войти
|
Войти
|
||||||
{/if}
|
{/if}
|
||||||
</button>
|
</button>
|
||||||
<a href="/" class="btn btn-secondary mt-3 w-100">Назад</a>
|
<a href="/predict" class="btn btn-secondary mt-3 w-100">Назад</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
<div class="container my-4">
|
<div class="container my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Side Navigation -->
|
<!-- Side Navigation -->
|
||||||
<div class="col-md-3 col-lg-2 mb-4">
|
<div class="col-md-3 col-lg-2">
|
||||||
<nav class="nav nav-pills flex-column">
|
<nav class="nav nav-pills flex-column">
|
||||||
<a class="nav-link active" href="/user/account">Учетная запись</a>
|
<a class="nav-link active" href="/user/account">Учетная запись</a>
|
||||||
<a class="nav-link" href="/user/templates">Сохраненные сценарии</a>
|
<a class="nav-link" href="/user/templates">Сохраненные сценарии</a>
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,6 @@
|
||||||
import Navbar from "$lib/components/Navbar.svelte";
|
import Navbar from "$lib/components/Navbar.svelte";
|
||||||
import Footer from "$lib/components/Footer.svelte";
|
import Footer from "$lib/components/Footer.svelte";
|
||||||
import ConfirmationPrompt from "$lib/components/ConfirmationPrompt.svelte";
|
import ConfirmationPrompt from "$lib/components/ConfirmationPrompt.svelte";
|
||||||
import PointEditor from "$lib/components/PointEditor.svelte";
|
|
||||||
import ToastContainer from "$lib/components/Toast.svelte";
|
|
||||||
import { addToast } from "$lib/components/Toast.svelte";
|
import { addToast } from "$lib/components/Toast.svelte";
|
||||||
|
|
||||||
// TODO: Implement these imports
|
// TODO: Implement these imports
|
||||||
|
|
@ -37,14 +35,12 @@
|
||||||
let templatesTable = $derived(new TableHandler($SavedScenarioTemplatesStore, { rowsPerPage: 5 }));
|
let templatesTable = $derived(new TableHandler($SavedScenarioTemplatesStore, { rowsPerPage: 5 }));
|
||||||
let templatesSearch = $derived(templatesTable.createSearch(["name"]));
|
let templatesSearch = $derived(templatesTable.createSearch(["name"]));
|
||||||
|
|
||||||
let editPoint: SavedPoint | null = $state(null);
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// Mock data for demonstration. Replace with API calls.
|
// Mock data for demonstration. Replace with API calls.
|
||||||
const pts = await getSavedPoints();
|
$SavedPointsStore = [
|
||||||
$SavedPointsStore = pts;
|
{ id: 1, name: "Baikonur Cosmodrome", lat: 45.96, lon: 63.3, alt: 90 },
|
||||||
SavedPointsStore.set($SavedPointsStore);
|
{ id: 2, name: "Kennedy Space Center", lat: 28.57, lon: -80.64, alt: 3 },
|
||||||
|
];
|
||||||
$SavedFlightProfilesStore = [
|
$SavedFlightProfilesStore = [
|
||||||
{ id: 1, name: "Standard Weather Balloon", rate_profile_data: {ascent_rate: 5, descent_rate: 8, burst_altitude: 30000} },
|
{ id: 1, name: "Standard Weather Balloon", rate_profile_data: {ascent_rate: 5, descent_rate: 8, burst_altitude: 30000} },
|
||||||
{ id: 2, name: "High Altitude Probe", rate_profile_data: {ascent_rate: 6, descent_rate: 10, burst_altitude: 40000} },
|
{ id: 2, name: "High Altitude Probe", rate_profile_data: {ascent_rate: 6, descent_rate: 10, burst_altitude: 40000} },
|
||||||
|
|
@ -119,10 +115,6 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditPoint(point: SavedPoint) {
|
|
||||||
editPoint = point;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="force-page-height">
|
<main class="force-page-height">
|
||||||
|
|
@ -132,7 +124,7 @@
|
||||||
<div class="container my-4">
|
<div class="container my-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Side Navigation -->
|
<!-- Side Navigation -->
|
||||||
<div class="col-md-3 col-lg-2 mb-4">
|
<div class="col-md-3 col-lg-2">
|
||||||
<nav class="nav nav-pills flex-column">
|
<nav class="nav nav-pills flex-column">
|
||||||
<a class="nav-link" href="/user/account">Учетная запись</a>
|
<a class="nav-link" href="/user/account">Учетная запись</a>
|
||||||
<a class="nav-link active" href="/user/templates">Сохраненные сценарии</a>
|
<a class="nav-link active" href="/user/templates">Сохраненные сценарии</a>
|
||||||
|
|
@ -149,28 +141,13 @@
|
||||||
<h5 class="mb-0">Точки запуска</h5>
|
<h5 class="mb-0">Точки запуска</h5>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
<div class="position-relative mb-2">
|
<Input
|
||||||
<Input
|
type="text"
|
||||||
type="text"
|
class="form-control-sm mb-2"
|
||||||
class="form-control-sm pe-5"
|
placeholder="Поиск по названию..."
|
||||||
placeholder="Поиск по названию..."
|
bind:value={pointsSearch.value}
|
||||||
bind:value={pointsSearch.value}
|
oninput={() => pointsSearch.set()}
|
||||||
oninput={() => pointsSearch.set()}
|
/>
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
color="white"
|
|
||||||
class="position-absolute top-50 end-0 translate-middle-y me-2 rounded-circle d-flex align-items-center justify-content-center"
|
|
||||||
style="width: 16px; height: 16px; border: none; background: var(--bs-secondary); color: var(--bs-white);"
|
|
||||||
onclick={() => {
|
|
||||||
pointsSearch.value = "";
|
|
||||||
pointsSearch.set();
|
|
||||||
}}
|
|
||||||
disabled={!pointsSearch.value}
|
|
||||||
>
|
|
||||||
<Icon name="x" style="font-size: 16px;" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-sm">
|
<table class="table table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -190,9 +167,6 @@
|
||||||
<td>{row.lon.toFixed(4)} °</td>
|
<td>{row.lon.toFixed(4)} °</td>
|
||||||
<td>{row.alt} м</td>
|
<td>{row.alt} м</td>
|
||||||
<td class="fit">
|
<td class="fit">
|
||||||
<Button color="primary" size="sm" onclick={() => handleEditPoint(row)}>
|
|
||||||
<Icon name="pencil" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
color="danger"
|
color="danger"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
@ -369,11 +343,3 @@
|
||||||
>
|
>
|
||||||
<p>{confirmConfig.body}</p>
|
<p>{confirmConfig.body}</p>
|
||||||
</ConfirmationPrompt>
|
</ConfirmationPrompt>
|
||||||
|
|
||||||
<PointEditor
|
|
||||||
point={editPoint}
|
|
||||||
isOpen={editPoint !== null}
|
|
||||||
onClose={() => { editPoint = null; pointsTable.setRows($SavedPointsStore) }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ToastContainer />
|
|
||||||
|
|
@ -128,10 +128,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-tinted {
|
|
||||||
filter: brightness(0.6);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767.98px)
|
@media (max-width: 767.98px)
|
||||||
{
|
{
|
||||||
.coordinates-display {
|
.coordinates-display {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue