Implement basic saved point editor
This commit is contained in:
parent
bb390d50dc
commit
0f79cefdac
12 changed files with 414 additions and 41 deletions
64
src/lib/api/base.ts
Normal file
64
src/lib/api/base.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { getCsrfToken } from "$lib/auth";
|
||||
|
||||
export const API_BASE_URL = "http://localhost:8000/api";
|
||||
|
||||
export async function fetchAPI<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
||||
let csrfToken = await getCsrfToken();
|
||||
if (!csrfToken) {
|
||||
console.warn("CSRF token not found, using empty string.");
|
||||
csrfToken = "";
|
||||
}
|
||||
const url = `${API_BASE_URL}${endpoint}`;
|
||||
options.credentials = "include"; // Include cookies in the request
|
||||
options.headers = {
|
||||
...options.headers,
|
||||
"Content-Type": "application/json",
|
||||
"X-CSRFToken": csrfToken,
|
||||
};
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
if (response.status === 204) {
|
||||
// No content response
|
||||
return {} as T; // Return an empty object for 204 responses
|
||||
}
|
||||
return await response.json() as T;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching ${url}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function postAPI<T>(endpoint: string, data: any): Promise<T> {
|
||||
return fetchAPI<T>(endpoint, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function getAPI<T>(endpoint: string): Promise<T> {
|
||||
return fetchAPI<T>(endpoint, {
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
export function putAPI<T>(endpoint: string, data: any): Promise<T> {
|
||||
return fetchAPI<T>(endpoint, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAPI<T>(endpoint: string): Promise<T> {
|
||||
return fetchAPI<T>(endpoint, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
19
src/lib/api/points.ts
Normal file
19
src/lib/api/points.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* API functions for Saved Points */
|
||||
import type { SavedPoint } from "$lib/types";
|
||||
import { getAPI, postAPI, putAPI, deleteAPI } from "./base";
|
||||
|
||||
export function getSavedPoints(): Promise<SavedPoint[]> {
|
||||
return getAPI<SavedPoint[]>("/saved-points/");
|
||||
}
|
||||
|
||||
export function savePoint(point: SavedPoint): Promise<SavedPoint> {
|
||||
return postAPI<SavedPoint>("/saved-points/", point);
|
||||
}
|
||||
|
||||
export function updatePoint(point: SavedPoint): Promise<SavedPoint> {
|
||||
return putAPI<SavedPoint>(`/saved-points/${point.id}/`, point);
|
||||
}
|
||||
|
||||
export function deletePoint(id: number): Promise<void> {
|
||||
return deleteAPI<void>(`/saved-points/${id}/`);
|
||||
}
|
||||
|
|
@ -15,7 +15,10 @@
|
|||
import { getForecast } from "$lib/prediction";
|
||||
import type { FlightParameters, ProfileName } from "$lib/types";
|
||||
import { PROFILE_MAP } from "$lib/types";
|
||||
import { FlightParametersStore, writeLocalStorage } from "$lib/stores";
|
||||
import { SavedPointsStore, FlightParametersStore, writeLocalStorage } from "$lib/stores";
|
||||
import { getSavedPoints } from "$lib/api/points";
|
||||
import type { SavedPoint } from "$lib/types";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let isCollapsed = false;
|
||||
let selectedProfile: ProfileName = "Normal";
|
||||
|
|
@ -27,14 +30,55 @@
|
|||
let startDate = now.toISOString().split("T")[0]; // YYYY-MM-DD
|
||||
let startTime = now.toISOString().split("T")[1].split(".")[0]; // HH:MM:SS
|
||||
|
||||
let inputLat = $FlightParametersStore.launch_latitude.toString();
|
||||
let inputLng = $FlightParametersStore.launch_longitude.toString();
|
||||
let inputLat = $FlightParametersStore.launch_latitude.toFixed(6).toString();
|
||||
let inputLng = $FlightParametersStore.launch_longitude.toFixed(6).toString();
|
||||
|
||||
$: $FlightParametersStore = {
|
||||
...$FlightParametersStore,
|
||||
profile: PROFILE_MAP[selectedProfile],
|
||||
};
|
||||
|
||||
$: $SavedPointsStore, setCoordinatesFromSavedPoint();
|
||||
|
||||
$: inputLat, inputLng, setToCustomOnChange();
|
||||
|
||||
function setCoordinatesFromSavedPoint() {
|
||||
console.log("Start point changed:", startPoint);
|
||||
|
||||
if (startPoint === "Custom") {
|
||||
$FlightParametersStore.launch_latitude = parseFloat(inputLat);
|
||||
$FlightParametersStore.launch_longitude = parseFloat(inputLng);
|
||||
} else {
|
||||
const selectedOption = document.querySelector(
|
||||
`#startPoint option[value="${startPoint}"]`
|
||||
) as HTMLOptionElement;
|
||||
if (selectedOption) {
|
||||
const lat = parseFloat(selectedOption.getAttribute("data-lat") || "0");
|
||||
const lng = parseFloat(selectedOption.getAttribute("data-lng") || "0");
|
||||
const alt = parseFloat(selectedOption.getAttribute("data-alt") || "0");
|
||||
inputLat = lat.toFixed(6).toString();
|
||||
inputLng = lng.toFixed(6).toString();
|
||||
$FlightParametersStore.launch_latitude = lat;
|
||||
$FlightParametersStore.launch_longitude = lng;
|
||||
$FlightParametersStore.launch_altitude = alt;
|
||||
console.log("Updated position from saved point:", lat, lng, alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setToCustomOnChange() {
|
||||
if (startPoint !== "Custom") {
|
||||
startPoint = "Custom";
|
||||
console.log("Switched to Custom point");
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
// Load saved points from the server or local storage
|
||||
const savedPoints = await getSavedPoints();
|
||||
SavedPointsStore.set(savedPoints);
|
||||
});
|
||||
|
||||
const handleGetPrediction = async () => {
|
||||
console.log("Fetching prediction with parameters:", $FlightParametersStore);
|
||||
console.log(startDate, startTime);
|
||||
|
|
@ -58,6 +102,10 @@
|
|||
console.log("Select on map clicked");
|
||||
}
|
||||
|
||||
export let handleClickPointListModal = () => {
|
||||
console.log("Open Point List Modal");
|
||||
};
|
||||
|
||||
const applyCoordinatesFromInput = () => {
|
||||
const lat = parseFloat(inputLat);
|
||||
const lng = parseFloat(inputLng);
|
||||
|
|
@ -179,15 +227,22 @@
|
|||
<FormGroup spacing="mb-2">
|
||||
<Label for="startPoint" class="form-label">Точка старта:</Label>
|
||||
<InputGroup size="sm">
|
||||
<Input type="select" id="startPoint" bind:value={startPoint}>
|
||||
<option>Custom</option>
|
||||
<option>Preset 1</option>
|
||||
<option>Preset 2</option>
|
||||
<Input type="select" id="startPoint" bind:value={startPoint} on:change={setCoordinatesFromSavedPoint}>
|
||||
<optgroup label="Сохраненные точки">
|
||||
{#each $SavedPointsStore as point}
|
||||
<option value={point.name} data-lat={point.lat} data-lng={point.lon} data-alt={point.alt}>
|
||||
{point.name}
|
||||
</option>
|
||||
{/each}
|
||||
</optgroup>
|
||||
<optgroup label="Задать вручную">
|
||||
<option value="Custom">Custom</option>
|
||||
</optgroup>
|
||||
</Input>
|
||||
<Button
|
||||
color="secondary"
|
||||
title="Edit Saved Locations"
|
||||
on:click={() => console.log("Not implemented yet")}
|
||||
on:click={handleClickPointListModal}
|
||||
>
|
||||
<span>Редакт.</span>
|
||||
<Icon name="journal-bookmark-fill" />
|
||||
|
|
@ -236,27 +291,29 @@
|
|||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-flex gap-2">
|
||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||
<Label for="ascentRate" class="form-label">Скорость подъема (м/с):</Label>
|
||||
<Input
|
||||
type="number"
|
||||
id="ascentRate"
|
||||
class="form-control-sm"
|
||||
bind:value={$FlightParametersStore.ascent_rate}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||
<Label for="descentRate" class="form-label">Скорость спуска (м/с):</Label>
|
||||
<Input
|
||||
type="number"
|
||||
id="descentRate"
|
||||
class="form-control-sm"
|
||||
bind:value={$FlightParametersStore.descent_rate}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
{#if selectedProfile != "Custom"}
|
||||
<div class="mb-2 d-flex gap-2">
|
||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||
<Label for="ascentRate" class="form-label">Скорость подъема (м/с):</Label>
|
||||
<Input
|
||||
type="number"
|
||||
id="ascentRate"
|
||||
class="form-control-sm"
|
||||
bind:value={$FlightParametersStore.ascent_rate}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||
<Label for="descentRate" class="form-label">Скорость спуска (м/с):</Label>
|
||||
<Input
|
||||
type="number"
|
||||
id="descentRate"
|
||||
class="form-control-sm"
|
||||
bind:value={$FlightParametersStore.descent_rate}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="d-grid gap-1">
|
||||
<Button color="outline-secondary" size="sm">Показать график высоты</Button>
|
||||
|
|
|
|||
8
src/lib/components/PointEditor.svelte
Normal file
8
src/lib/components/PointEditor.svelte
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { Datatable, Search, RowsPerPage, RowCount, Pagination } from '@vincjo/datatables'
|
||||
import { Modal } from '@sveltestrap/sveltestrap';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
181
src/lib/components/PointListModal.svelte
Normal file
181
src/lib/components/PointListModal.svelte
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
<script lang="ts">
|
||||
import { TableHandler, Datatable } from '@vincjo/datatables'
|
||||
import { Modal,
|
||||
Button,
|
||||
FormGroup,
|
||||
Label,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputGroupText,
|
||||
Icon,
|
||||
Pagination,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
} from '@sveltestrap/sveltestrap';
|
||||
import { onMount } from 'svelte';
|
||||
import type { SavedPoint } from '$lib/types';
|
||||
import { SavedPointsStore } from '$lib/stores';
|
||||
import { getSavedPoints, savePoint, updatePoint, deletePoint } from '$lib/api/points';
|
||||
|
||||
export let isOpen: boolean = false;
|
||||
export let onClose: () => void = () => {};
|
||||
|
||||
let points: SavedPoint[] = [];
|
||||
const table = new TableHandler($SavedPointsStore, { rowsPerPage: 10 })
|
||||
|
||||
let selectedPoint: SavedPoint | null = null;
|
||||
let newPoint: SavedPoint = { id: 0, name: '', lat: 0, lon: 0, alt: 0 };
|
||||
let isEditing: boolean = false;
|
||||
let modalTitle: string = 'Сохраненные точки';
|
||||
|
||||
|
||||
onMount(async () => {
|
||||
points = await getSavedPoints();
|
||||
SavedPointsStore.set(points);
|
||||
});
|
||||
|
||||
export function openModal() {
|
||||
isOpen = true;
|
||||
modalTitle = 'Сохраненные точки';
|
||||
}
|
||||
|
||||
export function closeModal() {
|
||||
isOpen = false;
|
||||
onClose();
|
||||
}
|
||||
|
||||
function handleEditPoint(point: SavedPoint) {
|
||||
selectedPoint = point;
|
||||
newPoint = { ...point };
|
||||
isEditing = true;
|
||||
modalTitle = 'Редактирование точки';
|
||||
}
|
||||
|
||||
function handleDeletePoint(point: SavedPoint) {
|
||||
deletePoint(point.id).then(() => {
|
||||
points = points.filter(p => p.id !== point.id);
|
||||
SavedPointsStore.set(points);
|
||||
});
|
||||
}
|
||||
|
||||
function handleSavePoint() {
|
||||
if (isEditing && selectedPoint) {
|
||||
updatePoint(newPoint).then(updatedPoint => {
|
||||
points = points.map(p => (p.id === updatedPoint.id ? updatedPoint : p));
|
||||
SavedPointsStore.set(points);
|
||||
table.setRows(points);
|
||||
resetForm();
|
||||
});
|
||||
} else {
|
||||
savePoint(newPoint).then(savedPoint => {
|
||||
points.push(savedPoint);
|
||||
points = [...points]; // Trigger reactivity
|
||||
SavedPointsStore.set(points);
|
||||
table.setRows(points);
|
||||
resetForm();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function resetForm() {
|
||||
selectedPoint = null;
|
||||
newPoint = { id: 0, name: '', lat: 0, lon: 0, alt: 0 };
|
||||
isEditing = false;
|
||||
modalTitle = 'Сохраненные точки';
|
||||
}
|
||||
|
||||
$: if (isOpen) {
|
||||
SavedPointsStore.set(points);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal isOpen={isOpen} toggle={closeModal} size="lg" fade={false} backdrop={true} scrollable>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{modalTitle}</h5>
|
||||
<button type="button" class="btn-close" on:click={closeModal} aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" >
|
||||
<Datatable {table}>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Имя</th>
|
||||
<th>Широта</th>
|
||||
<th>Долгота</th>
|
||||
<th>Высота</th>
|
||||
<th class="fit"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each points as point (point.id)}
|
||||
<tr>
|
||||
<td>{point.name}</td>
|
||||
<td>{point.lat}</td>
|
||||
<td>{point.lon}</td>
|
||||
<td>{point.alt}</td>
|
||||
<td class="fit">
|
||||
<Button color="primary" size="sm" on:click={() => handleEditPoint(point)}>
|
||||
<Icon name="pencil" />
|
||||
</Button>
|
||||
<Button color="danger" size="sm" on:click={() => handleDeletePoint(point)}>
|
||||
<Icon name="trash" />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</Datatable>
|
||||
<Pagination aria-label="Page navigation">
|
||||
<PaginationItem>
|
||||
<PaginationLink previous on:click={() => table.setPage("previous")} />
|
||||
</PaginationItem>
|
||||
{#each table.pagesWithEllipsis as page}
|
||||
<PaginationItem active={table.currentPage === page}>
|
||||
<PaginationLink on:click={() => table.setPage(page)}>{page}</PaginationLink>
|
||||
</PaginationItem>
|
||||
{/each}
|
||||
<PaginationItem>
|
||||
<PaginationLink next on:click={() => table.setPage("next")} />
|
||||
</PaginationItem>
|
||||
</Pagination>
|
||||
|
||||
<!-- Form for adding/editing points -->
|
||||
<div class="mt-2">
|
||||
<h5>{isEditing ? 'Редактирование точки' : 'Добавить новую точку'}</h5>
|
||||
<form on:submit|preventDefault={handleSavePoint}>
|
||||
<div class="mb-2">
|
||||
<Label for="name" class="small">Имя:</Label>
|
||||
<Input class="form-control-sm" type="text" id="name" bind:value={newPoint.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" id="lat" bind:value={newPoint.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" id="lon" bind:value={newPoint.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" id="alt" bind:value={newPoint.alt} required />
|
||||
<span class="form-text">Метры над ур. моря</span>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<Button type="submit" color="success">
|
||||
{isEditing ? 'Обновить точку' : 'Сохранить точку'}
|
||||
</Button>
|
||||
{#if isEditing}
|
||||
<Button type="button" color="secondary" on:click={resetForm} class="ms-2">Отмена</Button>
|
||||
{/if}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<Button color="secondary" on:click={closeModal}>Закрыть</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ import type { LatLngExpression } from "leaflet";
|
|||
import L from "leaflet";
|
||||
|
||||
import { getCsrfToken } from "./auth";
|
||||
import type { PredictionStage, RawPrediction, Prediction } from "./types";
|
||||
import type { PredictionStage, RawPrediction, Prediction, Point } from "./types";
|
||||
import { PredictionStore, RawPredictionStore, writeLocalStorage } from "./stores";
|
||||
|
||||
function getLatestDataset() {
|
||||
|
|
@ -90,10 +90,10 @@ export const getForecast = async (
|
|||
};
|
||||
|
||||
export function parsePrediction(prediction: PredictionStage[]): Prediction {
|
||||
const flight_path: [number, number, number][] = [];
|
||||
const launch: { latlng: LatLngExpression; datetime: Date } = {} as any;
|
||||
const burst: { latlng: LatLngExpression; datetime: Date } = {} as any;
|
||||
const landing: { latlng: LatLngExpression; datetime: Date } = {} as any;
|
||||
const flight_path: LatLngExpression[] = [];
|
||||
const launch: Point = {} as any;
|
||||
const burst: Point = {} as any;
|
||||
const landing: Point = {} as any;
|
||||
|
||||
const ascent = prediction[0].trajectory;
|
||||
const descent = prediction[1].trajectory;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { writable } from "svelte/store";
|
||||
import type { FlightParameters, RawTelemetry, Telemetry } from "./types";
|
||||
import type { RawPrediction, Prediction } from "./types";
|
||||
import type { SavedPoint } from "./types";
|
||||
|
||||
export const readLocalStorage = <T>(key: string, defaultValue: T): T => {
|
||||
const item = localStorage.getItem(key);
|
||||
|
|
@ -67,3 +68,5 @@ export const RawPredictionStore = writable<RawPrediction>(
|
|||
export const PredictionStore = writable<Prediction>(
|
||||
readLocalStorage<Prediction>("prediction", {} as Prediction)
|
||||
);
|
||||
|
||||
export const SavedPointsStore = writable<SavedPoint[]>([]);
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ export interface FlightParameters {
|
|||
launch_longitude: number;
|
||||
profile: (typeof PROFILE_MAP)[ProfileName];
|
||||
version: number;
|
||||
start_point?: string; // Optional, used for saved points
|
||||
}
|
||||
|
||||
export interface Point {
|
||||
latlng: LatLngLiteral & { alt: number };
|
||||
latlng: LatLngLiteral & { alt?: number };
|
||||
datetime: Date;
|
||||
}
|
||||
|
||||
|
|
@ -83,3 +84,11 @@ export interface Prediction {
|
|||
profile: string;
|
||||
flight_time: number;
|
||||
}
|
||||
|
||||
export interface SavedPoint {
|
||||
id: number;
|
||||
name: string;
|
||||
lat: number;
|
||||
lon: number;
|
||||
alt: number;
|
||||
}
|
||||
|
|
@ -3,15 +3,14 @@
|
|||
import ControlPanel from "$lib/components/ControlPanel.svelte";
|
||||
import Navbar from "$lib/components/Navbar.svelte";
|
||||
import PanelContainer from "$lib/components/PanelContainer.svelte";
|
||||
import TelemetryPanel from '$lib/components/TelemetryPanel.svelte';
|
||||
import ScenarioPanel from "$lib/components/ScenarioPanel.svelte";
|
||||
import TabComponent from "$lib/components/TabComponent.svelte";
|
||||
import PointListModal from "$lib/components/PointListModal.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { PredictionStore } from "$lib/stores";
|
||||
import { Modal, Icon } from "@sveltestrap/sveltestrap";
|
||||
import Toast, { addToast, removeToast } from "$lib/components/Toast.svelte";
|
||||
import { addToast, removeToast } from "$lib/components/Toast.svelte";
|
||||
import ToastContainer from '$lib/components/Toast.svelte';
|
||||
import L from "leaflet";
|
||||
import L, { point } from "leaflet";
|
||||
|
||||
let map: Map | null = null;
|
||||
let panelContainer: PanelContainer | null = null;
|
||||
|
|
@ -19,6 +18,8 @@
|
|||
let selectionToastId: string | null = null;
|
||||
let activeTab: 'control' | 'scenario' | 'settings' | 'about' = 'scenario';
|
||||
|
||||
let pointListModal: PointListModal | null = null;
|
||||
|
||||
onMount(() => {
|
||||
PredictionStore.subscribe((data) => {
|
||||
if (data) {
|
||||
|
|
@ -65,6 +66,16 @@
|
|||
selectionToastId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickPointListModal() {
|
||||
if (map) {
|
||||
map.stopSelection();
|
||||
console.log("Selection mode disabled");
|
||||
}
|
||||
pointListModal?.openModal();
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<main>
|
||||
|
|
@ -73,7 +84,7 @@
|
|||
<PanelContainer bind:this={panelContainer} >
|
||||
<TabComponent
|
||||
tabs={[
|
||||
{ id: 'scenario', icon: 'activity', label: 'Сценарий' },
|
||||
{ id: 'scenario', icon: 'file-earmark-play', label: 'Сценарий' },
|
||||
{ id: 'control', icon: 'sliders', label: 'Условия' },
|
||||
{ id: 'settings', icon: 'gear', label: 'Настройки' },
|
||||
{ id: 'about', icon: 'info-circle', label: 'О проекте' }
|
||||
|
|
@ -83,7 +94,7 @@
|
|||
|
||||
<div>
|
||||
{#if activeTab === 'control'}
|
||||
<ControlPanel {handleClickSelectOnMap} bind:this={controlPanel} />
|
||||
<ControlPanel {handleClickSelectOnMap} {handleClickPointListModal} bind:this={controlPanel} />
|
||||
{:else if activeTab === 'scenario'}
|
||||
<ScenarioPanel />
|
||||
{:else if activeTab === 'settings'}
|
||||
|
|
@ -94,5 +105,6 @@
|
|||
</div>
|
||||
</PanelContainer>
|
||||
<ToastContainer />
|
||||
<PointListModal bind:this={pointListModal} />
|
||||
</Map>
|
||||
</main>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue