cleanup
This commit is contained in:
parent
19f969c18c
commit
4bb7d214e8
4 changed files with 139 additions and 196 deletions
|
|
@ -10,11 +10,11 @@
|
||||||
2. **Derived State (`$derived`)**:
|
2. **Derived State (`$derived`)**:
|
||||||
- Use camelCase.
|
- Use camelCase.
|
||||||
- No special prefixes are needed as `$derived` already marks them as reactive derived state.
|
- No special prefixes are needed as `$derived` already marks them as reactive derived state.
|
||||||
- Example: `let inputLat = $derived(...)`
|
- Example: `let currentPoint = $derived(...)`
|
||||||
|
|
||||||
3. **Component Instance References**:
|
3. **Component Instance References**:
|
||||||
- Use camelCase and suffix with `Ref`.
|
- Use camelCase and suffix with `Ref`.
|
||||||
- Example: `let PointEditorRef: PointEditor | null = null;`
|
- Example: `let pointEditorRef: PointEditor | null = null;`
|
||||||
|
|
||||||
4. **Event Handlers**:
|
4. **Event Handlers**:
|
||||||
- Use `handle<EventName>` or `handle<Element><Event>` naming.
|
- Use `handle<EventName>` or `handle<Element><Event>` naming.
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
- Example: `import { SavedPointsStore } from '$lib/stores';`
|
- Example: `import { SavedPointsStore } from '$lib/stores';`
|
||||||
- The reactive Svelte store prefix `$` is used as standard.
|
- The reactive Svelte store prefix `$` is used as standard.
|
||||||
*/
|
*/
|
||||||
import { onMount } from "svelte";
|
import { onMount, onDestroy } from "svelte";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
|
@ -53,8 +53,14 @@
|
||||||
import PointEditor from "$lib/components/PointEditor.svelte";
|
import PointEditor from "$lib/components/PointEditor.svelte";
|
||||||
import SelectSearchable from "$lib/components/SelectSearchable.svelte";
|
import SelectSearchable from "$lib/components/SelectSearchable.svelte";
|
||||||
import { getForecast } from "$lib/prediction";
|
import { getForecast } from "$lib/prediction";
|
||||||
import { FlightParametersStore, SavedPointsStore, writeLocalStorage } from "$lib/stores";
|
import {
|
||||||
import { PROFILE_MAP, type FlightParameters, type ProfileName, type SavedPoint } from "$lib/types";
|
FlightParametersStore,
|
||||||
|
SavedPointsStore,
|
||||||
|
writeLocalStorage,
|
||||||
|
readLocalStorage,
|
||||||
|
flightParametersDefaults,
|
||||||
|
} from "$lib/stores";
|
||||||
|
import { PROFILE_MAP, type FlightParameters, type SavedPoint } from "$lib/types";
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -64,37 +70,30 @@
|
||||||
|
|
||||||
// State
|
// State
|
||||||
let isCollapsed = $state(false);
|
let isCollapsed = $state(false);
|
||||||
let isPointDirty = $state(false);
|
let startDate = $state(readLocalStorage<string>("startDate", new Date().toISOString().split("T")[0]));
|
||||||
const now = new Date();
|
let startTime = $state(readLocalStorage<string>("startTime", "12:00:00"));
|
||||||
let startDate = $state(now.toISOString().split("T")[0]);
|
let selectedPointId = $state($FlightParametersStore.start_point || -1);
|
||||||
let startTime = $state(now.toISOString().split("T")[1].split(".")[0]);
|
|
||||||
let selectedPointId = $state($FlightParametersStore.start_point);
|
|
||||||
|
|
||||||
// Component References
|
// Component References
|
||||||
let PointEditorRef: PointEditor | null = null;
|
let pointEditorRef: PointEditor | null = null;
|
||||||
|
|
||||||
// Derived State
|
// Derived State
|
||||||
let inputLat = $derived(
|
let currentPoint = $derived($SavedPointsStore.find((p) => p.id === selectedPointId) || null);
|
||||||
$FlightParametersStore.start_point === -1
|
let isPointDirty = $derived(() => {
|
||||||
? $FlightParametersStore.launch_latitude.toFixed(6)
|
if (!currentPoint) return false; // Not dirty if no point is selected
|
||||||
: $SavedPointsStore.find((point) => point.id === $FlightParametersStore.start_point)?.lat.toFixed(6) ||
|
const latMatch = $FlightParametersStore.launch_latitude.toFixed(6) === currentPoint.lat.toFixed(6);
|
||||||
"0.000000",
|
const lonMatch = $FlightParametersStore.launch_longitude.toFixed(6) === currentPoint.lon.toFixed(6);
|
||||||
);
|
const altMatch = $FlightParametersStore.launch_altitude.toFixed(2) === currentPoint.alt.toFixed(2);
|
||||||
let inputLng = $derived(
|
return !(latMatch && lonMatch && altMatch);
|
||||||
$FlightParametersStore.start_point === -1
|
});
|
||||||
? $FlightParametersStore.launch_longitude.toFixed(6)
|
|
||||||
: $SavedPointsStore.find((point) => point.id === $FlightParametersStore.start_point)?.lon.toFixed(6) ||
|
|
||||||
"0.000000",
|
|
||||||
);
|
|
||||||
let inputAlt = $derived(
|
|
||||||
$FlightParametersStore.start_point === -1
|
|
||||||
? $FlightParametersStore.launch_altitude.toFixed(2)
|
|
||||||
: $SavedPointsStore.find((point) => point.id === $FlightParametersStore.start_point)?.alt.toFixed(2) ||
|
|
||||||
"0.00",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Lifecycle Hooks
|
// Lifecycle Hooks
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
// NOTE: Consider moving localStorage logic into the store itself for better encapsulation.
|
||||||
|
$FlightParametersStore =
|
||||||
|
readLocalStorage<FlightParameters>("flightParameters", flightParametersDefaults) || $FlightParametersStore;
|
||||||
|
selectedPointId = $FlightParametersStore.start_point || -1;
|
||||||
|
|
||||||
getSavedPoints()
|
getSavedPoints()
|
||||||
.then((points) => SavedPointsStore.set(points))
|
.then((points) => SavedPointsStore.set(points))
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
@ -103,102 +102,91 @@
|
||||||
body: `Failed to load saved points: ${error.message}`,
|
body: `Failed to load saved points: ${error.message}`,
|
||||||
color: "danger",
|
color: "danger",
|
||||||
});
|
});
|
||||||
return [];
|
|
||||||
});
|
});
|
||||||
selectedPointId = $FlightParametersStore.start_point;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleApplySelectedPoint() {
|
onDestroy(() => {
|
||||||
if (selectedPointId && selectedPointId !== -1) {
|
writeLocalStorage<FlightParameters>("flightParameters", $FlightParametersStore);
|
||||||
$FlightParametersStore.start_point = selectedPointId;
|
writeLocalStorage<string>("startDate", startDate);
|
||||||
isPointDirty = false;
|
writeLocalStorage<string>("startTime", startTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event Handlers
|
||||||
|
function handlePointSelection(newPointId: number) {
|
||||||
|
console.log("Point selection changed:", newPointId);
|
||||||
|
selectedPointId = newPointId;
|
||||||
|
const point = $SavedPointsStore.find((p) => p.id === newPointId);
|
||||||
|
|
||||||
|
if (point) {
|
||||||
|
console.log("Selected point:", point);
|
||||||
|
$FlightParametersStore.start_point = point.id;
|
||||||
|
$FlightParametersStore.launch_latitude = point.lat;
|
||||||
|
$FlightParametersStore.launch_longitude = point.lon;
|
||||||
|
$FlightParametersStore.launch_altitude = point.alt;
|
||||||
|
} else if (newPointId === -1) {
|
||||||
|
$FlightParametersStore.start_point = -1;
|
||||||
|
// When clearing the selection, we can reset to defaults or leave as is.
|
||||||
|
// For now, we'll just update the ID. The user can manually edit coordinates.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleSelectPointInModal(point: SavedPoint) {
|
|
||||||
console.log("Selected point from modal:", point);
|
|
||||||
selectedPointId = point.id;
|
|
||||||
$FlightParametersStore.start_point = selectedPointId;
|
|
||||||
isPointDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSaveCurrentPoint() {
|
function handleSaveCurrentPoint() {
|
||||||
if (selectedPointId !== -1) {
|
if (currentPoint) {
|
||||||
const point = $SavedPointsStore.find((p) => p.id === selectedPointId);
|
// Update existing point
|
||||||
if (point) {
|
const updatedPointData = {
|
||||||
point.lat = parseFloat(inputLat);
|
...currentPoint,
|
||||||
point.lon = parseFloat(inputLng);
|
lat: $FlightParametersStore.launch_latitude,
|
||||||
point.alt = parseFloat(inputAlt);
|
lon: $FlightParametersStore.launch_longitude,
|
||||||
updatePoint(point)
|
alt: $FlightParametersStore.launch_altitude,
|
||||||
.then((updatedPoint) => {
|
};
|
||||||
$SavedPointsStore = $SavedPointsStore.map((p) => (p.id === updatedPoint.id ? updatedPoint : p));
|
updatePoint(updatedPointData)
|
||||||
SavedPointsStore.set($SavedPointsStore);
|
.then((savedPoint) => {
|
||||||
addToast({
|
SavedPointsStore.update((points) => points.map((p) => (p.id === savedPoint.id ? savedPoint : p)));
|
||||||
header: "Точка обновлена",
|
addToast({
|
||||||
body: `Точка "${updatedPoint.name}" успешно обновлена.`,
|
header: "Point Updated",
|
||||||
color: "success",
|
body: `Point "${savedPoint.name}" was successfully updated.`,
|
||||||
});
|
color: "success",
|
||||||
isPointDirty = false;
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
addToast({
|
|
||||||
header: "Ошибка обновления точки",
|
|
||||||
body: `Ошибка при обновлении точки: ${error.message}`,
|
|
||||||
color: "danger",
|
|
||||||
});
|
|
||||||
console.error("Ошибка при обновлении точки:", error);
|
|
||||||
});
|
});
|
||||||
}
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
addToast({
|
||||||
|
header: "Update Error",
|
||||||
|
body: `Failed to update point: ${error.message}`,
|
||||||
|
color: "danger",
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
PointEditorRef?.openModalAndCreate(
|
// Create new point
|
||||||
|
pointEditorRef?.openModalAndCreate(
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name: `Новая точка ${new Date().toLocaleString()}`,
|
name: `New Point ${new Date().toLocaleString()}`,
|
||||||
lat: parseFloat(inputLat),
|
lat: $FlightParametersStore.launch_latitude,
|
||||||
lon: parseFloat(inputLng),
|
lon: $FlightParametersStore.launch_longitude,
|
||||||
alt: parseFloat(inputAlt),
|
alt: $FlightParametersStore.launch_altitude,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
handleModalSave,
|
(savedPoint) => {
|
||||||
|
if (savedPoint) {
|
||||||
|
handlePointSelection(savedPoint.id);
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleModalSave(savedPoint: SavedPoint) {
|
|
||||||
if (savedPoint) {
|
|
||||||
$FlightParametersStore.start_point = savedPoint.id;
|
|
||||||
selectedPointId = savedPoint.id;
|
|
||||||
isPointDirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePointEditorOpen() {
|
|
||||||
PointEditorRef?.openModal(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handlePredictionRequest() {
|
async function handlePredictionRequest() {
|
||||||
$FlightParametersStore.launch_latitude = parseFloat(inputLat);
|
// Persist current parameters before running prediction
|
||||||
$FlightParametersStore.launch_longitude = parseFloat(inputLng);
|
|
||||||
$FlightParametersStore.launch_altitude = parseFloat(inputAlt);
|
|
||||||
writeLocalStorage<FlightParameters>("flightParameters", $FlightParametersStore);
|
writeLocalStorage<FlightParameters>("flightParameters", $FlightParametersStore);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await getForecast($FlightParametersStore, `${startDate}T${startTime}Z`);
|
const data = await getForecast($FlightParametersStore, `${startDate}T${startTime}Z`);
|
||||||
console.log("Forecast request successful:", data);
|
console.log("Forecast request successful:", data);
|
||||||
addToast({
|
addToast({ header: "Forecast Request", body: "Forecast request successful!", color: "success" });
|
||||||
header: "Forecast Request",
|
|
||||||
body: "Forecast request successful!",
|
|
||||||
color: "success",
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error getting forecast:", error);
|
console.error("Error getting forecast:", error);
|
||||||
addToast({
|
addToast({ header: "Forecast Error", body: `Error getting forecast: ${error.message}`, color: "danger" });
|
||||||
header: "Forecast Error",
|
|
||||||
body: `Error getting forecast: ${error.message}`,
|
|
||||||
color: "danger",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,25 +198,23 @@
|
||||||
export function updateLaunchPosition(lat: number, lng: number) {
|
export function updateLaunchPosition(lat: number, lng: number) {
|
||||||
$FlightParametersStore.launch_latitude = lat;
|
$FlightParametersStore.launch_latitude = lat;
|
||||||
$FlightParametersStore.launch_longitude = lng;
|
$FlightParametersStore.launch_longitude = lng;
|
||||||
isPointDirty = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectedProfile() {
|
export function loadFlightParameters(params: FlightParameters) {
|
||||||
return $FlightParametersStore.profile;
|
$FlightParametersStore = params;
|
||||||
|
selectedPointId = params.start_point || -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectProfile(profile: ProfileName) {
|
export function getFlightParameters(): FlightParameters {
|
||||||
$FlightParametersStore.profile = profile;
|
return $FlightParametersStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function collapsePanel() {
|
export function collapsePanel() {
|
||||||
isCollapsed = true;
|
isCollapsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expandPanel() {
|
export function expandPanel() {
|
||||||
isCollapsed = false;
|
isCollapsed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function togglePanel() {
|
export function togglePanel() {
|
||||||
isCollapsed = !isCollapsed;
|
isCollapsed = !isCollapsed;
|
||||||
}
|
}
|
||||||
|
|
@ -238,22 +224,12 @@
|
||||||
<CardHeader
|
<CardHeader
|
||||||
class="bg-primary text-white d-flex justify-content-between align-items-center card-header p-1 px-3"
|
class="bg-primary text-white d-flex justify-content-between align-items-center card-header p-1 px-3"
|
||||||
style="cursor:pointer;"
|
style="cursor:pointer;"
|
||||||
|
onclick={handleToggleCollapse}
|
||||||
>
|
>
|
||||||
<button
|
<b class="card-title mb-0 text-white p-0">Условия прогнозирования</b>
|
||||||
type="button"
|
<Button class="p-0" size="sm" color="primary" aria-label="Свернуть/развернуть условия прогнозирования">
|
||||||
class="d-flex w-100 justify-content-between align-items-center bg-transparent border-0 p-0"
|
<Icon name={isCollapsed ? "caret-left-fill" : "caret-down-fill"} class="text-white" />
|
||||||
aria-label="Свернуть/развернуть условия прогнозирования"
|
</Button>
|
||||||
onclick={handleToggleCollapse}
|
|
||||||
>
|
|
||||||
<b class="card-title mb-0 text-white p-0">Условия прогнозирования</b>
|
|
||||||
<Button class="p-0" size="sm" color="primary" onclick={handleToggleCollapse}>
|
|
||||||
{#if isCollapsed}
|
|
||||||
<Icon name="caret-left-fill" class="text-white" />
|
|
||||||
{:else}
|
|
||||||
<Icon name="caret-down-fill" class="text-white" />
|
|
||||||
{/if}
|
|
||||||
</Button>
|
|
||||||
</button>
|
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
{#if !isCollapsed}
|
{#if !isCollapsed}
|
||||||
|
|
@ -273,8 +249,8 @@
|
||||||
<Label for="cp-flight-profile" class="form-label">Профиль полета:</Label>
|
<Label for="cp-flight-profile" class="form-label">Профиль полета:</Label>
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input type="select" id="cp-flight-profile" bind:value={$FlightParametersStore.profile}>
|
<Input type="select" id="cp-flight-profile" bind:value={$FlightParametersStore.profile}>
|
||||||
{#each Object.keys(PROFILE_MAP) as profileName}
|
{#each Object.entries(PROFILE_MAP) as [name, value]}
|
||||||
<option value={PROFILE_MAP[profileName as ProfileName]}>{profileName}</option>
|
<option {value}>{name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Input>
|
</Input>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
@ -287,46 +263,37 @@
|
||||||
<SelectSearchable
|
<SelectSearchable
|
||||||
style="min-height: calc(1.5em + .5rem + 2px); padding: .25rem .5rem; font-size: .875rem;"
|
style="min-height: calc(1.5em + .5rem + 2px); padding: .25rem .5rem; font-size: .875rem;"
|
||||||
id="cp-start-point"
|
id="cp-start-point"
|
||||||
bind:selected={selectedPointId}
|
selected={selectedPointId}
|
||||||
|
onChange={(e) => handlePointSelection(e)}
|
||||||
options={$SavedPointsStore.map((point) => ({
|
options={$SavedPointsStore.map((point) => ({
|
||||||
value: point.id,
|
value: point.id,
|
||||||
label:
|
label: `${point.name} ${point.id === selectedPointId && isPointDirty() ? "(изменено)" : ""}`,
|
||||||
point.name +
|
|
||||||
`${point.id == $FlightParametersStore.start_point && isPointDirty ? " (изменено)" : ""}`,
|
|
||||||
}))}
|
}))}
|
||||||
placeholder="Новая точка..."
|
placeholder="Новая точка..."
|
||||||
searchPlaceholder="Поиск по точкам..."
|
searchPlaceholder="Поиск по точкам..."
|
||||||
on:change={() => {
|
|
||||||
if (!isPointDirty) {
|
|
||||||
$FlightParametersStore.start_point = selectedPointId;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Button
|
{#if selectedPointId !== -1}
|
||||||
size="sm"
|
<Button
|
||||||
color="white"
|
size="sm"
|
||||||
class="position-absolute top-50 end-0 translate-middle-y rounded-circle d-flex align-items-center justify-content-center"
|
color="white"
|
||||||
style="width: 16px; height: 16px; border: none; background: var(--bs-secondary); color: var(--bs-white); z-index: 10; margin-right: 2rem;"
|
class="position-absolute top-50 end-0 translate-middle-y rounded-circle d-flex align-items-center justify-content-center"
|
||||||
on:click={() => {
|
style="width: 16px; height: 16px; border: none; background: var(--bs-secondary); color: var(--bs-white); z-index: 10; margin-right: 2rem;"
|
||||||
selectedPointId = -1;
|
on:click={() => handlePointSelection(-1)}
|
||||||
$FlightParametersStore.start_point = -1;
|
title="Clear selection"
|
||||||
}}
|
>
|
||||||
disabled={selectedPointId === -1}
|
<Icon name="x" style="font-size: 16px;" />
|
||||||
>
|
</Button>
|
||||||
<Icon name="x" style="font-size: 16px;" />
|
{/if}
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<Button color="success" size="sm" onclick={handleApplySelectedPoint} title="Apply Coordinates"
|
|
||||||
>✓</Button
|
|
||||||
>
|
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<div class="d-flex gap-2 mb-2">
|
<div class="d-flex gap-2 mb-2">
|
||||||
<Button
|
<Button
|
||||||
color="secondary flex-fill"
|
color="secondary"
|
||||||
|
class="flex-fill"
|
||||||
size="sm"
|
size="sm"
|
||||||
onclick={handlePointEditorOpen}
|
onclick={() => pointEditorRef?.openModal(true)}
|
||||||
title="Открыть список точек"
|
title="Открыть список точек"
|
||||||
>
|
>
|
||||||
Все точки
|
Все точки
|
||||||
|
|
@ -334,7 +301,8 @@
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="primary flex-fill"
|
color="primary"
|
||||||
|
class="flex-fill"
|
||||||
size="sm"
|
size="sm"
|
||||||
onclick={handleSaveCurrentPoint}
|
onclick={handleSaveCurrentPoint}
|
||||||
title="Сохранить текущие координаты"
|
title="Сохранить текущие координаты"
|
||||||
|
|
@ -350,28 +318,25 @@
|
||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
id="cp-latitude"
|
id="cp-latitude"
|
||||||
type="text"
|
type="number"
|
||||||
bind:value={inputLat}
|
step="0.000001"
|
||||||
|
bind:value={$FlightParametersStore.launch_latitude}
|
||||||
placeholder="Latitude"
|
placeholder="Latitude"
|
||||||
on:change={() => {
|
|
||||||
isPointDirty = true;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<InputGroupText>/</InputGroupText>
|
<InputGroupText>/</InputGroupText>
|
||||||
<Input
|
<Input
|
||||||
id="cp-longitude"
|
id="cp-longitude"
|
||||||
type="text"
|
type="number"
|
||||||
bind:value={inputLng}
|
step="0.000001"
|
||||||
|
bind:value={$FlightParametersStore.launch_longitude}
|
||||||
placeholder="Longitude"
|
placeholder="Longitude"
|
||||||
on:change={() => {
|
|
||||||
isPointDirty = true;
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Button color="secondary" size="sm" onclick={onSelectOnMapClick}>
|
<Button color="secondary" size="sm" onclick={onSelectOnMapClick}>
|
||||||
<Icon name="geo-alt-fill" />
|
<Icon name="geo-alt-fill" />
|
||||||
</Button>
|
</Button>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||||
<Label for="cp-start-height" class="form-label">Высота старта (м):</Label>
|
<Label for="cp-start-height" class="form-label">Высота старта (м):</Label>
|
||||||
|
|
@ -379,10 +344,7 @@
|
||||||
type="number"
|
type="number"
|
||||||
id="cp-start-height"
|
id="cp-start-height"
|
||||||
class="form-control-sm"
|
class="form-control-sm"
|
||||||
on:change={() => {
|
bind:value={$FlightParametersStore.launch_altitude}
|
||||||
isPointDirty = true;
|
|
||||||
}}
|
|
||||||
bind:value={inputAlt}
|
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
<FormGroup class="flex-fill w-50" spacing="mb-2">
|
||||||
|
|
@ -418,34 +380,8 @@
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<FormGroup spacing="mb-2">
|
<!-- NOTE: Custom profile UI to be implemented -->
|
||||||
<Label for="cp-flight-profile" class="form-label">Пользовательский профиль:</Label>
|
<p class="text-muted text-center small my-3">Custom profile editor is not yet implemented.</p>
|
||||||
<InputGroup size="sm">
|
|
||||||
<SelectSearchable
|
|
||||||
id="cp-flight-profile"
|
|
||||||
bind:selected={$FlightParametersStore.profile}
|
|
||||||
options={Object.keys(PROFILE_MAP).map((profileName) => ({
|
|
||||||
// stub, replace with actual profiles
|
|
||||||
value: PROFILE_MAP[profileName as ProfileName],
|
|
||||||
label: profileName,
|
|
||||||
}))}
|
|
||||||
placeholder="Выберите профиль..."
|
|
||||||
searchPlaceholder="Поиск профилей..."
|
|
||||||
on:change={() => {
|
|
||||||
$FlightParametersStore.profile = $FlightParametersStore.profile;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
color="secondary"
|
|
||||||
size="sm"
|
|
||||||
title="Edit profile"
|
|
||||||
disabled={$FlightParametersStore.profile !== "custom_profile"}
|
|
||||||
>
|
|
||||||
<span>Редакт.</span>
|
|
||||||
<Icon name="gear-fill" />
|
|
||||||
</Button>
|
|
||||||
</InputGroup>
|
|
||||||
</FormGroup>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="d-grid gap-1">
|
<div class="d-grid gap-1">
|
||||||
|
|
@ -454,4 +390,4 @@
|
||||||
</CardBody>
|
</CardBody>
|
||||||
{/if}
|
{/if}
|
||||||
</Card>
|
</Card>
|
||||||
<PointEditor bind:this={PointEditorRef} onSelectPoint={handleSelectPointInModal} />
|
<PointEditor bind:this={pointEditorRef} onSelectPoint={(point: SavedPoint) => handlePointSelection(point.id)} />
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
const savedFlightParameters = savedData.flight_parameters;
|
const savedFlightParameters = savedData.flight_parameters;
|
||||||
|
|
||||||
// Compare flight parameters excluding launch_datetime
|
// Compare flight parameters excluding launch_datetime
|
||||||
|
console.log("Comparing flight parameters:", flightParameters, savedFlightParameters);
|
||||||
return JSON.stringify(flightParameters) !== JSON.stringify(savedFlightParameters);
|
return JSON.stringify(flightParameters) !== JSON.stringify(savedFlightParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,6 +113,7 @@
|
||||||
$FlightParametersStore = selectedScenario.template_data.flight_parameters;
|
$FlightParametersStore = selectedScenario.template_data.flight_parameters;
|
||||||
scenarioUnsaved = false;
|
scenarioUnsaved = false;
|
||||||
writeLocalStorage("scenario", $ScenarioStore);
|
writeLocalStorage("scenario", $ScenarioStore);
|
||||||
|
writeLocalStorage("flightParameters", $FlightParametersStore);
|
||||||
if (showToast) {
|
if (showToast) {
|
||||||
addToast({
|
addToast({
|
||||||
header: "Сценарий применен",
|
header: "Сценарий применен",
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,11 @@ export const getForecast = async (
|
||||||
flightParameters.dataset = getLatestDataset();
|
flightParameters.dataset = getLatestDataset();
|
||||||
flightParameters.launch_datetime = launchDateTime;
|
flightParameters.launch_datetime = launchDateTime;
|
||||||
|
|
||||||
|
if (flightParameters.start_point === -1) {
|
||||||
|
// remove start_point if it is -1
|
||||||
|
delete flightParameters.start_point;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Sending request:", flightParameters);
|
console.log("Sending request:", flightParameters);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export const clearLocalStorage = (key: string): void => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const flightParametersDefaults: FlightParameters = {
|
export const flightParametersDefaults: FlightParameters = {
|
||||||
ascent_rate: 5.0,
|
ascent_rate: 5.0,
|
||||||
burst_altitude: 30000.0,
|
burst_altitude: 30000.0,
|
||||||
dataset: "",
|
dataset: "",
|
||||||
|
|
@ -52,7 +52,7 @@ export const FlightParametersStore = writable<FlightParameters>(
|
||||||
readLocalStorage<FlightParameters>("flightParameters", flightParametersDefaults)
|
readLocalStorage<FlightParameters>("flightParameters", flightParametersDefaults)
|
||||||
);
|
);
|
||||||
|
|
||||||
const templateDataDefaults: TemplateData = {
|
export const templateDataDefaults: TemplateData = {
|
||||||
description: "",
|
description: "",
|
||||||
prediction_mode: "",
|
prediction_mode: "",
|
||||||
model: "",
|
model: "",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue