From ffb27c2e0a5e0b21af8eebd039450b782905fb88 Mon Sep 17 00:00:00 2001 From: ThePetrovich Date: Wed, 9 Jul 2025 20:14:47 +0800 Subject: [PATCH] Initial implementation of custom profile editor + formatting --- .prettierrc | 4 +- package-lock.json | 96 ++- package.json | 8 +- src/lib/components/ConfirmationPrompt.svelte | 74 +- src/lib/components/ControlPanel.svelte | 650 ++++++++-------- src/lib/components/CurveChart.svelte | 278 +++++++ src/lib/components/CurveEditor.svelte | 607 +++++++++++++++ src/lib/components/EditableCell.svelte | 45 ++ src/lib/components/Footer.svelte | 67 +- src/lib/components/Map.svelte | 244 +++--- src/lib/components/Navbar.svelte | 203 +++-- src/lib/components/PanelContainer.svelte | 10 +- src/lib/components/PointEditor.svelte | 751 +++++++++---------- src/lib/components/ScenarioEditor.svelte | 473 ++++++------ src/lib/components/ScenarioPanel.svelte | 547 +++++++------- src/lib/components/TabComponent.svelte | 75 +- src/lib/components/TelemetryPanel.svelte | 157 ++-- src/lib/components/Toast.svelte | 158 ++-- src/lib/components/WindVisualisation.svelte | 601 +++++++-------- src/lib/stores.ts | 6 +- src/lib/types.ts | 25 +- 21 files changed, 3045 insertions(+), 2034 deletions(-) create mode 100644 src/lib/components/CurveChart.svelte create mode 100644 src/lib/components/CurveEditor.svelte create mode 100644 src/lib/components/EditableCell.svelte diff --git a/.prettierrc b/.prettierrc index d10f057..f27d159 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,7 @@ "tabWidth": 4, "endOfLine": "lf", "printWidth": 120, - "useTabs": false + "useTabs": true, + "htmlWhitespaceSensitivity": "ignore", + "bracketSameLine": true } diff --git a/package-lock.json b/package-lock.json index 1c6cabe..6de5046 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,17 +11,23 @@ "@sveltestrap/sveltestrap": "^7.1.0", "@types/leaflet": "^1.9.19", "bootstrap-icons": "^1.13.1", + "chart.js": "^4.5.0", + "chartjs-adapter-luxon": "^1.3.1", + "chartjs-plugin-dragdata": "^2.3.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", "leaflet-heatmap": "^1.0.0", "leaflet-timedimension": "^1.1.1", "leaflet-velocity": "^2.1.4", - "leaflet.heat": "^0.2.0" + "leaflet.heat": "^0.2.0", + "luxon": "^3.6.1", + "svelte5-chartjs": "^1.0.0" }, "devDependencies": { "@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@types/luxon": "^3.6.2", "@vincjo/datatables": "^2.5.0", "svelte": "^5.34.8", "svelte-check": "^4.0.0", @@ -484,6 +490,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, "node_modules/@polka/url": { "version": "1.0.0-next.28", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", @@ -884,6 +895,12 @@ "@types/geojson": "*" } }, + "node_modules/@types/luxon": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz", + "integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==", + "dev": true + }, "node_modules/@vincjo/datatables": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@vincjo/datatables/-/datatables-2.5.0.tgz", @@ -935,6 +952,38 @@ } ] }, + "node_modules/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-adapter-luxon": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.3.1.tgz", + "integrity": "sha512-yxHov3X8y+reIibl1o+j18xzrcdddCLqsXhriV2+aQ4hCR66IYFchlRXUvrJVoxglJ380pgytU7YWtoqdIgqhg==", + "peerDependencies": { + "chart.js": ">=3.0.0", + "luxon": ">=1.0.0" + } + }, + "node_modules/chartjs-plugin-dragdata": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chartjs-plugin-dragdata/-/chartjs-plugin-dragdata-2.3.1.tgz", + "integrity": "sha512-CFD1e2d+gyH9EXb92qWu4Zb2zVoY4OtrbJYLMoGWOInE7ftoOD//4B0/k9IvKzQbdVU3JsPqUQI9KHcMpqQVfg==", + "dependencies": { + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0" + }, + "peerDependencies": { + "chart.js": "^3.9.1 || ^4.0.1" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -967,6 +1016,34 @@ "node": ">= 0.6" } }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1163,6 +1240,14 @@ "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" }, + "node_modules/luxon": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz", + "integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -1399,6 +1484,15 @@ "typescript": ">=5.0.0" } }, + "node_modules/svelte5-chartjs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svelte5-chartjs/-/svelte5-chartjs-1.0.0.tgz", + "integrity": "sha512-SMk+D5ECbsoeFurKE/Nr9sqD4H3WqZkQ4eLxwchDSh8gu7YSGN3ASXYCz9kzFhrH2QGQYpebHwLIMHg7FOI/7A==", + "peerDependencies": { + "chart.js": "^3.5.0 || ^4.0.0", + "svelte": "^5.0.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", diff --git a/package.json b/package.json index 9bae6d0..bd83ec5 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@sveltejs/adapter-auto": "^4.0.0", "@sveltejs/kit": "^2.16.0", "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@types/luxon": "^3.6.2", "@vincjo/datatables": "^2.5.0", "svelte": "^5.34.8", "svelte-check": "^4.0.0", @@ -25,11 +26,16 @@ "@sveltestrap/sveltestrap": "^7.1.0", "@types/leaflet": "^1.9.19", "bootstrap-icons": "^1.13.1", + "chart.js": "^4.5.0", + "chartjs-adapter-luxon": "^1.3.1", + "chartjs-plugin-dragdata": "^2.3.1", "js-cookie": "^3.0.5", "leaflet": "^1.9.4", "leaflet-heatmap": "^1.0.0", "leaflet-timedimension": "^1.1.1", "leaflet-velocity": "^2.1.4", - "leaflet.heat": "^0.2.0" + "leaflet.heat": "^0.2.0", + "luxon": "^3.6.1", + "svelte5-chartjs": "^1.0.0" } } diff --git a/src/lib/components/ConfirmationPrompt.svelte b/src/lib/components/ConfirmationPrompt.svelte index 3808413..47fb94d 100644 --- a/src/lib/components/ConfirmationPrompt.svelte +++ b/src/lib/components/ConfirmationPrompt.svelte @@ -1,44 +1,44 @@ - {title} - - {#if children} - {@render children()} - {:else} - Вы действительно хотите продолжить? - {/if} - - - - - - \ No newline at end of file + {title} + + {#if children} + {@render children()} + {:else} + Вы действительно хотите продолжить? + {/if} + + + + + + diff --git a/src/lib/components/ControlPanel.svelte b/src/lib/components/ControlPanel.svelte index 42f56ac..b57e49c 100644 --- a/src/lib/components/ControlPanel.svelte +++ b/src/lib/components/ControlPanel.svelte @@ -1,5 +1,5 @@ - - Условия прогнозирования - - + + Условия прогнозирования + + - {#if !isCollapsed} - -
- - - - - - - - -
+ {#if !isCollapsed} + +
+ + + + + + + + +
- - - - - {#each Object.entries(PROFILE_MAP) as [name, value]} - - {/each} - - - + + + + + {#each Object.entries(PROFILE_MAP) as [name, value]} + + {/each} + + + - - - -
- handlePointSelection(e)} - options={$SavedPointsStore.map((point) => ({ - value: point.id, - label: `${point.name} ${point.id === selectedPointId && isPointDirty() ? "(изменено)" : ""}`, - }))} - placeholder="Новая точка..." - searchPlaceholder="Поиск по точкам..." - /> - {#if selectedPointId !== -1} - - {/if} -
-
-
+ + + +
+ handlePointSelection(e)} + options={$SavedPointsStore.map((point) => ({ + value: point.id, + label: `${point.name} ${point.id === selectedPointId && isPointDirty() ? "(изменено)" : ""}`, + }))} + placeholder="Новая точка..." + searchPlaceholder="Поиск по точкам..." /> + {#if selectedPointId !== -1} + + {/if} +
+
+
-
- +
+ - -
+ +
- - - - - / - - - - + + + + + / + + + + -
- - - - - - - - -
+
+ + + + + + + + +
- {#if $FlightParametersStore.profile !== "custom_profile"} -
- - - - - - - - -
- {:else} - -

Custom profile editor is not yet implemented.

- {/if} + {#if $FlightParametersStore.profile !== "custom_profile"} +
+ + + + + + + + +
+ {:else} + +

Custom profile editor is not yet implemented.

+ + {/if} -
- -
-
- {/if} +
+ +
+
+ {/if}
handlePointSelection(point.id)} /> + diff --git a/src/lib/components/CurveChart.svelte b/src/lib/components/CurveChart.svelte new file mode 100644 index 0000000..70472de --- /dev/null +++ b/src/lib/components/CurveChart.svelte @@ -0,0 +1,278 @@ + + +
+ + {#if !chart} +
+ Loading chart... +
+ {/if} +
\ No newline at end of file diff --git a/src/lib/components/CurveEditor.svelte b/src/lib/components/CurveEditor.svelte new file mode 100644 index 0000000..af5cf80 --- /dev/null +++ b/src/lib/components/CurveEditor.svelte @@ -0,0 +1,607 @@ + + + + + + + + { + isConfirmationVisible = false; + handleDeleteCurve(selectedCurve); + }} + oncancel={() => { + isConfirmationVisible = false; + }}> +

Are you sure you want to delete this curve?

+
diff --git a/src/lib/components/EditableCell.svelte b/src/lib/components/EditableCell.svelte new file mode 100644 index 0000000..ff23816 --- /dev/null +++ b/src/lib/components/EditableCell.svelte @@ -0,0 +1,45 @@ + + + + {#if editing} + + {:else} + {#if value === emptyValue || value === null || value === undefined} + {emptyPlaceholder} + {:else} + {valuePrefix}{value}{valueSuffix} + {/if} + {/if} + diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte index 4cb9911..b5a445b 100644 --- a/src/lib/components/Footer.svelte +++ b/src/lib/components/Footer.svelte @@ -1,36 +1,35 @@ \ No newline at end of file +
+
+
+
+ + ООО «ЯКС» + +
+
+
+
+
+
+
+
+
Copyright © 2024 ООО «Якутские Космические Системы»
+
+ +
+
+ diff --git a/src/lib/components/Map.svelte b/src/lib/components/Map.svelte index 5ce3f55..d47069d 100644 --- a/src/lib/components/Map.svelte +++ b/src/lib/components/Map.svelte @@ -1,154 +1,158 @@
-
-

- Lat: - {mouseLat.toFixed(6)}, - Lon: - {mouseLng.toFixed(6)} -

-
- - {#if map && windData} - - {/if} -
\ No newline at end of file +
+

+ Lat: + {mouseLat.toFixed(6)}, + Lon: + {mouseLng.toFixed(6)} +

+
+ + {#if map && windData} + + {/if} + diff --git a/src/lib/components/Navbar.svelte b/src/lib/components/Navbar.svelte index 658f35d..7a77406 100644 --- a/src/lib/components/Navbar.svelte +++ b/src/lib/components/Navbar.svelte @@ -1,113 +1,112 @@ + - - Logo - - (isOpen = !isOpen)} /> - + + Logo + + (isOpen = !isOpen)} /> + \ No newline at end of file + .custom-tab-label { + font-size: 0.66rem; + font-weight: 500; + } + diff --git a/src/lib/components/TelemetryPanel.svelte b/src/lib/components/TelemetryPanel.svelte index 7a69ca4..6911c61 100644 --- a/src/lib/components/TelemetryPanel.svelte +++ b/src/lib/components/TelemetryPanel.svelte @@ -1,97 +1,82 @@ - - Последние данные телеметрии - - - {#if !isCollapsed} - - - - - - - + + Последние данные телеметрии + + + {#if !isCollapsed} + + + + + + + - - - - - - + + + + + + - - - - - - - - {/if} + + + + + + + + {/if} - diff --git a/src/lib/components/Toast.svelte b/src/lib/components/Toast.svelte index c3c9e99..736fab0 100644 --- a/src/lib/components/Toast.svelte +++ b/src/lib/components/Toast.svelte @@ -1,71 +1,70 @@
- {#each $toasts as toast (toast.id)} - removeToast(toast.id)} - > - removeToast(toast.id)} class={`text-${toast.color || 'text-info'}`}> - - {toast.header} - - - {toast.body} - - - {/each} + {#each $toasts as toast (toast.id)} + removeToast(toast.id)}> + removeToast(toast.id)} class={`text-${toast.color || "text-info"}`}> + + {toast.header} + + + {toast.body} + + + {/each}
\ No newline at end of file + .toast-container { + z-index: 1090; /* High z-index to appear above other elements */ + } + diff --git a/src/lib/components/WindVisualisation.svelte b/src/lib/components/WindVisualisation.svelte index 4950b6a..36eb408 100644 --- a/src/lib/components/WindVisualisation.svelte +++ b/src/lib/components/WindVisualisation.svelte @@ -1,190 +1,191 @@ +
-
- - -
+
+ + +
+ \ No newline at end of file + .layer-controls { + position: absolute; + bottom: 30px; + left: 10px; + z-index: 1000; + background: rgba(255, 255, 255, 0.8); + padding: 10px; + border-radius: 5px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + } + + .control-group { + display: flex; + flex-direction: column; + gap: 5px; + } + + .control-group label { + display: flex; + align-items: center; + gap: 5px; + font-size: 14px; + cursor: pointer; + } + :global(.wind-heat-legend) { + padding: 8px 10px; + background: rgba(255, 255, 255, 0.9); + border-radius: 5px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); + line-height: 1.2; + color: #333; + font-family: Arial, sans-serif; + } + + :global(.wind-heat-legend h4) { + margin: 0 0 5px; + font-size: 14px; + font-weight: bold; + } + + :global(legend-scale) { + display: flex; + margin-bottom: 3px; + } + + :global(legend-color) { + height: 12px; + flex-grow: 1; + } + + :global(.legend-labels) { + display: flex; + justify-content: space-between; + font-size: 11px; + } + diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 2605648..b800b0a 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -1,7 +1,7 @@ import { writable } from "svelte/store"; import type { FlightParameters, RawTelemetry, Telemetry } from "./types"; import type { RawPrediction, Prediction } from "./types"; -import type { SavedPoint, SavedFlightProfile, SavedScenario, TemplateData } from "./types"; +import type { SavedPoint, SavedFlightProfile, SavedScenario } from "./types"; export const readLocalStorage = (key: string, defaultValue: T): T => { const item = localStorage.getItem(key); @@ -52,7 +52,7 @@ export const FlightParametersStore = writable( readLocalStorage("flightParameters", flightParametersDefaults) ); -export const templateDataDefaults: TemplateData = { +export const templateDataDefaults = { description: "", prediction_mode: "", model: "", @@ -63,7 +63,7 @@ export const templateDataDefaults: TemplateData = { export const scenarioDefaults: SavedScenario = { id: -1, name: "Новый сценарий", - template_data: templateDataDefaults, + ...templateDataDefaults, } export const ScenarioStore = writable( diff --git a/src/lib/types.ts b/src/lib/types.ts index 3eaf947..45aeb5b 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -48,15 +48,6 @@ export const PPREDICTION_MODE_NAMES = { ensemble: "Ансамблевый" }; -export interface TemplateData { - description: string; - prediction_mode: string; - model: string; - dataset: string; - flight_parameters: FlightParameters; -} - - export interface Point { latlng: LatLngLiteral & { alt?: number }; datetime: Date; @@ -126,14 +117,26 @@ export interface SavedPoint { alt: number; } +export interface RateCurvePoint { + order: number; // Order in the curve + time_constraint: number; // Time in seconds + alt_constraint: number; // Altitude constraint in meters + rate: number; // Rate in m/s +} + export interface SavedFlightProfile { id: number; name: string; - rate_profile_data: object; + type?: string; + rate_profile_data: RateCurvePoint[]; } export interface SavedScenario { id: number; name: string; - template_data: TemplateData; + description: string; + prediction_mode: string; + model: string; + dataset: string; + flight_parameters: FlightParameters; } \ No newline at end of file