From 4bb7d214e8e9939d65c233ff6942bdc089ea628d Mon Sep 17 00:00:00 2001 From: ThePetrovich Date: Sun, 6 Jul 2025 19:16:17 +0800 Subject: [PATCH] cleanup --- src/lib/components/ControlPanel.svelte | 324 ++++++++++-------------- src/lib/components/ScenarioPanel.svelte | 2 + src/lib/prediction.ts | 5 + src/lib/stores.ts | 4 +- 4 files changed, 139 insertions(+), 196 deletions(-) diff --git a/src/lib/components/ControlPanel.svelte b/src/lib/components/ControlPanel.svelte index 9309f29..42f56ac 100644 --- a/src/lib/components/ControlPanel.svelte +++ b/src/lib/components/ControlPanel.svelte @@ -10,11 +10,11 @@ 2. **Derived State (`$derived`)**: - Use camelCase. - 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**: - Use camelCase and suffix with `Ref`. - - Example: `let PointEditorRef: PointEditor | null = null;` + - Example: `let pointEditorRef: PointEditor | null = null;` 4. **Event Handlers**: - Use `handle` or `handle` naming. @@ -34,7 +34,7 @@ - Example: `import { SavedPointsStore } from '$lib/stores';` - The reactive Svelte store prefix `$` is used as standard. */ - import { onMount } from "svelte"; + import { onMount, onDestroy } from "svelte"; import { Button, Card, @@ -53,8 +53,14 @@ import PointEditor from "$lib/components/PointEditor.svelte"; import SelectSearchable from "$lib/components/SelectSearchable.svelte"; import { getForecast } from "$lib/prediction"; - import { FlightParametersStore, SavedPointsStore, writeLocalStorage } from "$lib/stores"; - import { PROFILE_MAP, type FlightParameters, type ProfileName, type SavedPoint } from "$lib/types"; + import { + FlightParametersStore, + SavedPointsStore, + writeLocalStorage, + readLocalStorage, + flightParametersDefaults, + } from "$lib/stores"; + import { PROFILE_MAP, type FlightParameters, type SavedPoint } from "$lib/types"; // Props interface Props { @@ -64,37 +70,30 @@ // State let isCollapsed = $state(false); - let isPointDirty = $state(false); - const now = new Date(); - let startDate = $state(now.toISOString().split("T")[0]); - let startTime = $state(now.toISOString().split("T")[1].split(".")[0]); - let selectedPointId = $state($FlightParametersStore.start_point); + let startDate = $state(readLocalStorage("startDate", new Date().toISOString().split("T")[0])); + let startTime = $state(readLocalStorage("startTime", "12:00:00")); + let selectedPointId = $state($FlightParametersStore.start_point || -1); // Component References - let PointEditorRef: PointEditor | null = null; + let pointEditorRef: PointEditor | null = null; // Derived State - let inputLat = $derived( - $FlightParametersStore.start_point === -1 - ? $FlightParametersStore.launch_latitude.toFixed(6) - : $SavedPointsStore.find((point) => point.id === $FlightParametersStore.start_point)?.lat.toFixed(6) || - "0.000000", - ); - let inputLng = $derived( - $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", - ); + let currentPoint = $derived($SavedPointsStore.find((p) => p.id === selectedPointId) || null); + let isPointDirty = $derived(() => { + if (!currentPoint) return false; // Not dirty if no point is selected + const latMatch = $FlightParametersStore.launch_latitude.toFixed(6) === currentPoint.lat.toFixed(6); + const lonMatch = $FlightParametersStore.launch_longitude.toFixed(6) === currentPoint.lon.toFixed(6); + const altMatch = $FlightParametersStore.launch_altitude.toFixed(2) === currentPoint.alt.toFixed(2); + return !(latMatch && lonMatch && altMatch); + }); // Lifecycle Hooks onMount(() => { + // NOTE: Consider moving localStorage logic into the store itself for better encapsulation. + $FlightParametersStore = + readLocalStorage("flightParameters", flightParametersDefaults) || $FlightParametersStore; + selectedPointId = $FlightParametersStore.start_point || -1; + getSavedPoints() .then((points) => SavedPointsStore.set(points)) .catch((error) => { @@ -103,102 +102,91 @@ body: `Failed to load saved points: ${error.message}`, color: "danger", }); - return []; }); - selectedPointId = $FlightParametersStore.start_point; }); - function handleApplySelectedPoint() { - if (selectedPointId && selectedPointId !== -1) { - $FlightParametersStore.start_point = selectedPointId; - isPointDirty = false; + onDestroy(() => { + writeLocalStorage("flightParameters", $FlightParametersStore); + writeLocalStorage("startDate", startDate); + writeLocalStorage("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() { - if (selectedPointId !== -1) { - const point = $SavedPointsStore.find((p) => p.id === selectedPointId); - if (point) { - point.lat = parseFloat(inputLat); - point.lon = parseFloat(inputLng); - point.alt = parseFloat(inputAlt); - updatePoint(point) - .then((updatedPoint) => { - $SavedPointsStore = $SavedPointsStore.map((p) => (p.id === updatedPoint.id ? updatedPoint : p)); - SavedPointsStore.set($SavedPointsStore); - addToast({ - header: "Точка обновлена", - body: `Точка "${updatedPoint.name}" успешно обновлена.`, - color: "success", - }); - isPointDirty = false; - }) - .catch((error) => { - addToast({ - header: "Ошибка обновления точки", - body: `Ошибка при обновлении точки: ${error.message}`, - color: "danger", - }); - console.error("Ошибка при обновлении точки:", error); + if (currentPoint) { + // Update existing point + const updatedPointData = { + ...currentPoint, + lat: $FlightParametersStore.launch_latitude, + lon: $FlightParametersStore.launch_longitude, + alt: $FlightParametersStore.launch_altitude, + }; + updatePoint(updatedPointData) + .then((savedPoint) => { + SavedPointsStore.update((points) => points.map((p) => (p.id === savedPoint.id ? savedPoint : p))); + addToast({ + header: "Point Updated", + body: `Point "${savedPoint.name}" was successfully updated.`, + color: "success", }); - } + }) + .catch((error) => { + addToast({ + header: "Update Error", + body: `Failed to update point: ${error.message}`, + color: "danger", + }); + }); } else { - PointEditorRef?.openModalAndCreate( + // Create new point + pointEditorRef?.openModalAndCreate( null, { id: 0, - name: `Новая точка ${new Date().toLocaleString()}`, - lat: parseFloat(inputLat), - lon: parseFloat(inputLng), - alt: parseFloat(inputAlt), + name: `New Point ${new Date().toLocaleString()}`, + lat: $FlightParametersStore.launch_latitude, + lon: $FlightParametersStore.launch_longitude, + alt: $FlightParametersStore.launch_altitude, }, true, 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() { - $FlightParametersStore.launch_latitude = parseFloat(inputLat); - $FlightParametersStore.launch_longitude = parseFloat(inputLng); - $FlightParametersStore.launch_altitude = parseFloat(inputAlt); + // Persist current parameters before running prediction writeLocalStorage("flightParameters", $FlightParametersStore); - try { const data = await getForecast($FlightParametersStore, `${startDate}T${startTime}Z`); console.log("Forecast request successful:", data); - addToast({ - header: "Forecast Request", - body: "Forecast request successful!", - color: "success", - }); + addToast({ header: "Forecast Request", body: "Forecast request successful!", color: "success" }); } catch (error: any) { console.error("Error getting forecast:", error); - addToast({ - header: "Forecast Error", - body: `Error getting forecast: ${error.message}`, - color: "danger", - }); + addToast({ header: "Forecast Error", body: `Error getting forecast: ${error.message}`, color: "danger" }); } } @@ -210,25 +198,23 @@ export function updateLaunchPosition(lat: number, lng: number) { $FlightParametersStore.launch_latitude = lat; $FlightParametersStore.launch_longitude = lng; - isPointDirty = true; } - export function getSelectedProfile() { - return $FlightParametersStore.profile; + export function loadFlightParameters(params: FlightParameters) { + $FlightParametersStore = params; + selectedPointId = params.start_point || -1; } - export function selectProfile(profile: ProfileName) { - $FlightParametersStore.profile = profile; + export function getFlightParameters(): FlightParameters { + return $FlightParametersStore; } export function collapsePanel() { isCollapsed = true; } - export function expandPanel() { isCollapsed = false; } - export function togglePanel() { isCollapsed = !isCollapsed; } @@ -238,22 +224,12 @@ - - + Условия прогнозирования + {#if !isCollapsed} @@ -273,8 +249,8 @@ - {#each Object.keys(PROFILE_MAP) as profileName} - + {#each Object.entries(PROFILE_MAP) as [name, value]} + {/each} @@ -287,46 +263,37 @@ handlePointSelection(e)} options={$SavedPointsStore.map((point) => ({ value: point.id, - label: - point.name + - `${point.id == $FlightParametersStore.start_point && isPointDirty ? " (изменено)" : ""}`, + label: `${point.name} ${point.id === selectedPointId && isPointDirty() ? "(изменено)" : ""}`, }))} placeholder="Новая точка..." searchPlaceholder="Поиск по точкам..." - on:change={() => { - if (!isPointDirty) { - $FlightParametersStore.start_point = selectedPointId; - } - }} /> - + {#if selectedPointId !== -1} + + {/if} -
+
@@ -379,10 +344,7 @@ type="number" id="cp-start-height" class="form-control-sm" - on:change={() => { - isPointDirty = true; - }} - bind:value={inputAlt} + bind:value={$FlightParametersStore.launch_altitude} /> @@ -418,34 +380,8 @@
{:else} - - - - ({ - // stub, replace with actual profiles - value: PROFILE_MAP[profileName as ProfileName], - label: profileName, - }))} - placeholder="Выберите профиль..." - searchPlaceholder="Поиск профилей..." - on:change={() => { - $FlightParametersStore.profile = $FlightParametersStore.profile; - }} - /> - - - + +

Custom profile editor is not yet implemented.

{/if}
@@ -454,4 +390,4 @@ {/if} - + handlePointSelection(point.id)} /> diff --git a/src/lib/components/ScenarioPanel.svelte b/src/lib/components/ScenarioPanel.svelte index 93399d6..b9acad6 100644 --- a/src/lib/components/ScenarioPanel.svelte +++ b/src/lib/components/ScenarioPanel.svelte @@ -54,6 +54,7 @@ const savedFlightParameters = savedData.flight_parameters; // Compare flight parameters excluding launch_datetime + console.log("Comparing flight parameters:", flightParameters, savedFlightParameters); return JSON.stringify(flightParameters) !== JSON.stringify(savedFlightParameters); } @@ -112,6 +113,7 @@ $FlightParametersStore = selectedScenario.template_data.flight_parameters; scenarioUnsaved = false; writeLocalStorage("scenario", $ScenarioStore); + writeLocalStorage("flightParameters", $FlightParametersStore); if (showToast) { addToast({ header: "Сценарий применен", diff --git a/src/lib/prediction.ts b/src/lib/prediction.ts index 7f91f2e..2dc4da5 100644 --- a/src/lib/prediction.ts +++ b/src/lib/prediction.ts @@ -52,6 +52,11 @@ export const getForecast = async ( flightParameters.dataset = getLatestDataset(); 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); try { diff --git a/src/lib/stores.ts b/src/lib/stores.ts index ff12837..d0a6f90 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -35,7 +35,7 @@ export const clearLocalStorage = (key: string): void => { } } -const flightParametersDefaults: FlightParameters = { +export const flightParametersDefaults: FlightParameters = { ascent_rate: 5.0, burst_altitude: 30000.0, dataset: "", @@ -52,7 +52,7 @@ export const FlightParametersStore = writable( readLocalStorage("flightParameters", flightParametersDefaults) ); -const templateDataDefaults: TemplateData = { +export const templateDataDefaults: TemplateData = { description: "", prediction_mode: "", model: "",