AAYMA
RU
Войти

Ветропрогноз

Веб-приложение для почасового прогноза выработки ветряков на 48 часов: управление турбинами, 3D-карта, загрузка CSV с измерениями и автоматическое…

Рабочий MVP
Команда «KMG» 9💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: KMG
Кейс HackAlem.ai: Энергетика — Самрук-Казына
Стадия: Рабочий MVP

Задача кейса: AI-агент, прогнозирующий выработку ветровой электростанции по погодным данным

Суть

Веб-приложение для почасового прогноза выработки ветряков на 48 часов: управление турбинами, 3D-карта, загрузка CSV с измерениями и автоматическое переобучение с новым прогнозом.

Как устроено

История мощности двух турбин объединяется с кэшированными архивными прогнозами погоды ECMWF (parquet по датам выдачи) и проверяется на корреляции часовых поясов. На этих данных обучается общая модель градиентного бустинга и сравнивается с базовыми прогнозами на holdout. Агент оркестрирует доступные прогоны погоды с fallback и защитой от утечки будущего, а прогнозы записываются (upsert) в MongoDB. При каждом импорте CSV воркеры Celery/Redis ставят в очередь переобучение. Для новых площадок модель строится по истории мощности и календарным признакам, без выдуманной погоды. Фронтенд на React/TS.

Ключевые решения

  • gradient boosting
  • ECMWF archived forecast runs
  • leakage guards
  • baseline comparison
  • timezone correlation analysis
  • Celery retraining queue
  • model versioning

Особенности

Прогнозы погоды кэшируются по конкретным прогонам ECMWF, а выборка строится «как было известно на момент прогноза» с защитой от утечки. Проведён анализ сдвига часовых поясов данных до и после марта 2024. Импорт новых измерений автоматически запускает переобучение, а старые версии моделей сохраняются.

Стек

Python · React · TypeScript · Vite · MongoDB · Celery · Redis · Docker Compose · parquet · joblib


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-3d048659-kmg.

README

README.md

Ветропрогноз — агентный почасовой прогноз выработки ВЭС на 48 часов

Windmill management, hourly power forecasting, and an interactive 3D map. React/TypeScript frontend, Python API, MongoDB storage, and Celery workers with Redis transport. Power is normalized to [0, 1]; all displayed times are UTC.

Run the complete application

From the repository root, with Docker Desktop / Docker Compose running:

docker compose up --build -d --wait

Open http://127.0.0.1:8091. MongoDB and Redis stay on the private container network. The first startup imports the bundled two-windmill archive into MongoDB; subsequent starts preserve uploaded data and models.

Windmills and measurements

Open Ветряки и данные to add a windmill with its name and coordinates. It immediately becomes selectable and appears on the 3D map, even without data. Upload hourly CSV measurements or correct an individual hour:

time,power
2026-03-01T00:00:00Z,0.42
2026-03-01T01:00:00Z,0.38

Imports update matching hours and preserve other measurements. Every changed import automatically queues retraining and a new 48-hour forecast. The page shows queued/running/completed/failed jobs, input revisions, and validation metrics. Previous model versions and forecasts remain available.

The new per-windmill model uses power history and calendar features, without inventing weather for new sites. Start with at least five consecutive days of hourly measurements. Insufficient data is reported explicitly and remains saved for the next upload. Forecasts begin relative to the latest measured hour; training on old data does not produce a forecast for today's date.

Bundled archive

The initial archive contains 29 forecast origins (January 30–February 27, 2026), 48 predictions per turbine per origin, and January measurements for two turbines. These original weather-based forecasts are retained. The original offline research/model scripts are documented in backend/README.md.

Frontend development

Run the backend, database, workers, and scheduler in containers while Vite runs on the host:

docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build -d backend worker scheduler
cd frontend
npm ci
npm run dev

Open http://127.0.0.1:5173. The development override publishes the API on loopback port 8000 for Vite's existing proxy. Use Node 22.12+ (Node 24 recommended). Stop any older API using port 8000 before this setup.

Documentation and checks

cd frontend
npm test
npm run build
npm run docker:check

Backend unit tests run with python -m pytest -p no:cacheprovider from backend/ after installing requirements.txt. Real MongoDB integration tests are enabled with MONGODB_TEST_URI; they create and remove isolated windfarm_test_* databases. See deployment instructions for running them inside the stack.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
59
Авторы
4
Файлы
263
Ветки
4

Авторы коммитов

GabYer 42adilzhan 11NeAdisa 5asissemba 1

История коммитов

  • 2026-09-23 17:54Update README.mdNeAdisa
  • 2026-09-23 17:53Update README.mdNeAdisa
  • 2026-09-23 17:52Merge pull request #3 from BAITC-Hacks/dev-adilzhanNeAdisa
  • 2026-09-23 17:51Merge branch 'main' into dev-adilzhanNeAdisa
  • 2026-09-23 17:50add database support adding new windmills and update actual dataadilzhan
  • 2026-09-23 17:45docs: README — запуск без участия команды, метрики, агент, источникиGabYer
  • 2026-09-23 17:36docs: README — запуск без участия команды, метрики, агент, источникиGabYer
  • 2026-09-23 17:28Merge pull request #2 from BAITC-Hacks/dev-adilzhanNeAdisa
Все коммиты (59)
  • 2026-09-23 17:163d map with windmillsadilzhan
  • 2026-09-23 16:52Merge main into dev-adilzhanadilzhan
  • 2026-09-23 16:48Integrate forecast dashboard with layered backend APIadilzhan
  • 2026-09-23 16:30Move backend code and assets into dedicated backend directoryadilzhan
  • 2026-09-23 16:22Merge origin/main and preserve frontend and Python ignore rulesadilzhan
  • 2026-09-23 16:19created frontend directory and docker containeradilzhan
  • 2026-09-23 16:10merge: as-of model, MAE recipe, fallback demoGabYer
  • 2026-09-23 16:10Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 16:10feat(model): as-of model for first origin, December-selected MAE recipe, fallback demo, pinned depsGabYer
  • 2026-09-23 15:43merge: offline reproduction and model evaluation documentationGabYer
  • 2026-09-23 15:43Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:43docs: document offline reproduction leakage controls and January metricsGabYer
  • 2026-09-23 15:40merge: agent fallback and forecast contract testsGabYer
  • 2026-09-23 15:40Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:40test: cover availability fallback upsert and committed forecast contractsGabYer
  • 2026-09-23 15:37merge: timezone correlation analysisGabYer
  • 2026-09-23 15:37Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:37docs: report pre and post March 2024 timezone correlationsGabYer
  • 2026-09-23 15:36merge: January run-aware evaluation and UI actualsGabYer
  • 2026-09-23 15:36Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:36feat(eval): score January forecasts against archived single runsGabYer
  • 2026-09-23 15:34merge: agent tools and cached February forecastsGabYer
  • 2026-09-23 15:33Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:33feat(agent): orchestrate available runs with fallbacks and upsert February forecastsGabYer
  • 2026-09-23 15:30merge: production model through January historyGabYer
  • 2026-09-23 15:30Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:29feat(model): retain validation artifact and fit full-history production modelGabYer
  • 2026-09-23 15:16merge: real 48-hour turbine forecastGabYer
  • 2026-09-23 15:16Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:16feat(agent): generate 48-hour forecast from archived run with leakage guardsGabYer
  • 2026-09-23 15:13merge: shared forecasting model and holdout metricsGabYer
  • 2026-09-23 15:13Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:13feat(model): train shared gradient boosting and compare forecast baselinesGabYer
  • 2026-09-23 15:11merge: weather join and timezone diagnosticsGabYer
  • 2026-09-23 15:11Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:11feat(features): join hourly weather and check timezone correlationsGabYer
  • 2026-09-23 15:10merge: archived weather client and cacheGabYer
  • 2026-09-23 15:10Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:10feat(weather): cache shared historical forecasts and single ECMWF runGabYer
  • 2026-09-23 15:08merge: hourly turbine data loaderGabYer
  • 2026-09-23 15:08Merge remote-tracking branch 'origin/main' into feature/modelGabYer
  • 2026-09-23 15:07feat(data): load turbine history as hourly UTC observationsGabYer
  • 2026-09-23 15:05Removed unused labels in designadilzhan
  • 2026-09-23 14:56chore: configure turbine coordinates and shared weather pointGabYer
  • 2026-09-23 14:42docs: translate README into Russianasissemba
  • 2026-09-23 14:33Merge pull request #1 from BAITC-Hacks/feature/modelGabdulla Yermekbayev
  • 2026-09-23 14:31data: add raw turbine history CSV filesGabYer
  • 2026-09-23 14:26chore: project skeleton, data inspection, output contractGabYer
  • 2026-09-23 14:18gitignoreadilzhan
  • 2026-09-23 14:14design previewadilzhan
  • 2026-09-23 14:10feat: first design versionadilzhan
Структура репозитория (263 файлов)
.gitignore
DATA_ARCHITECTURE.md
DEPLOYMENT.md
README.md
backend/.dockerignore
backend/.env.example
backend/Dockerfile
backend/README.md
backend/app/.gitkeep
backend/config.yaml
backend/data/raw/.gitkeep
backend/data/raw/turbine_1.csv
backend/data/raw/turbine_2.csv
backend/data/weather_cache/.gitkeep
backend/data/weather_cache/history_2023_6abbea01d7704bae.json
backend/data/weather_cache/history_2023_6abbea01d7704bae.parquet
backend/data/weather_cache/history_2024_07c98a35ff7669ea.json
backend/data/weather_cache/history_2024_07c98a35ff7669ea.parquet
backend/data/weather_cache/history_2025_7d2bd870a5cc0d61.json
backend/data/weather_cache/history_2025_7d2bd870a5cc0d61.parquet
backend/data/weather_cache/history_2026_f7ad17fafb4ae79b.json
backend/data/weather_cache/history_2026_f7ad17fafb4ae79b.parquet
backend/data/weather_cache/run_20251231T1200_15631169ddf254bb.json
backend/data/weather_cache/run_20251231T1200_15631169ddf254bb.parquet
backend/data/weather_cache/run_20260101T1200_2bbcd7d3d23d9509.json
backend/data/weather_cache/run_20260101T1200_2bbcd7d3d23d9509.parquet
backend/data/weather_cache/run_20260102T1200_ee5ba2c1b8a68a09.json
backend/data/weather_cache/run_20260102T1200_ee5ba2c1b8a68a09.parquet
backend/data/weather_cache/run_20260103T1200_7ce61f789ed343b3.json
backend/data/weather_cache/run_20260103T1200_7ce61f789ed343b3.parquet
backend/data/weather_cache/run_20260104T1200_eee3225b4ca1069a.json
backend/data/weather_cache/run_20260104T1200_eee3225b4ca1069a.parquet
backend/data/weather_cache/run_20260105T1200_9006569f3f9c458b.json
backend/data/weather_cache/run_20260105T1200_9006569f3f9c458b.parquet
backend/data/weather_cache/run_20260106T1200_8af0fd30dc31f10f.json
backend/data/weather_cache/run_20260106T1200_8af0fd30dc31f10f.parquet
backend/data/weather_cache/run_20260107T1200_65c90bff565a01fe.json
backend/data/weather_cache/run_20260107T1200_65c90bff565a01fe.parquet
backend/data/weather_cache/run_20260108T1200_51b5ce85a5e52e7c.json
backend/data/weather_cache/run_20260108T1200_51b5ce85a5e52e7c.parquet
backend/data/weather_cache/run_20260109T1200_09f0c3d50e4495f2.json
backend/data/weather_cache/run_20260109T1200_09f0c3d50e4495f2.parquet
backend/data/weather_cache/run_20260110T1200_4eb985a425e15a1b.json
backend/data/weather_cache/run_20260110T1200_4eb985a425e15a1b.parquet
backend/data/weather_cache/run_20260111T1200_99e8c4894802b8fe.json
backend/data/weather_cache/run_20260111T1200_99e8c4894802b8fe.parquet
backend/data/weather_cache/run_20260112T1200_37ced339358943c0.json
backend/data/weather_cache/run_20260112T1200_37ced339358943c0.parquet
backend/data/weather_cache/run_20260113T1200_e00bb07e929a6d84.json
backend/data/weather_cache/run_20260113T1200_e00bb07e929a6d84.parquet
backend/data/weather_cache/run_20260114T1200_710283ea97ec5dbf.json
backend/data/weather_cache/run_20260114T1200_710283ea97ec5dbf.parquet
backend/data/weather_cache/run_20260115T1200_2751ca57a7285656.json
backend/data/weather_cache/run_20260115T1200_2751ca57a7285656.parquet
backend/data/weather_cache/run_20260116T1200_fe524f8b274a9a5e.json
backend/data/weather_cache/run_20260116T1200_fe524f8b274a9a5e.parquet
backend/data/weather_cache/run_20260117T1200_f51c5d89a9b45000.json
backend/data/weather_cache/run_20260117T1200_f51c5d89a9b45000.parquet
backend/data/weather_cache/run_20260118T1200_904a12979e089615.json
backend/data/weather_cache/run_20260118T1200_904a12979e089615.parquet
backend/data/weather_cache/run_20260119T1200_5281e5b1393172d0.json
backend/data/weather_cache/run_20260119T1200_5281e5b1393172d0.parquet
backend/data/weather_cache/run_20260120T1200_6d8f4e38a1761d06.json
backend/data/weather_cache/run_20260120T1200_6d8f4e38a1761d06.parquet
backend/data/weather_cache/run_20260121T1200_4b50b735b8700742.json
backend/data/weather_cache/run_20260121T1200_4b50b735b8700742.parquet
backend/data/weather_cache/run_20260122T1200_3140b9444786e692.json
backend/data/weather_cache/run_20260122T1200_3140b9444786e692.parquet
backend/data/weather_cache/run_20260123T1200_a78f64170bb53795.json
backend/data/weather_cache/run_20260123T1200_a78f64170bb53795.parquet
backend/data/weather_cache/run_20260124T1200_6e6d69531e8208e1.json
backend/data/weather_cache/run_20260124T1200_6e6d69531e8208e1.parquet
backend/data/weather_cache/run_20260125T1200_036f6fe23b244515.json
backend/data/weather_cache/run_20260125T1200_036f6fe23b244515.parquet
backend/data/weather_cache/run_20260126T1200_30fc90fe79237fad.json
backend/data/weather_cache/run_20260126T1200_30fc90fe79237fad.parquet
backend/data/weather_cache/run_20260127T1200_152736ce4477d760.json
backend/data/weather_cache/run_20260127T1200_152736ce4477d760.parquet
backend/data/weather_cache/run_20260128T1200_abc6ba6e65bd74cd.json
backend/data/weather_cache/run_20260128T1200_abc6ba6e65bd74cd.parquet
backend/data/weather_cache/run_20260129T1200_78f0776b7543a5c7.json
backend/data/weather_cache/run_20260129T1200_78f0776b7543a5c7.parquet
backend/data/weather_cache/run_20260130T1200_c7fb9abecf8e0881.json
backend/data/weather_cache/run_20260130T1200_c7fb9abecf8e0881.parquet
backend/data/weather_cache/run_20260131T0600_e3f2d2402b7b1d16.json
backend/data/weather_cache/run_20260131T0600_e3f2d2402b7b1d16.parquet
backend/data/weather_cache/run_20260131T1200_de83adf0dcddf735.json
backend/data/weather_cache/run_20260131T1200_de83adf0dcddf735.parquet
backend/data/weather_cache/run_20260201T1200_6f5d417a1e4756da.json
backend/data/weather_cache/run_20260201T1200_6f5d417a1e4756da.parquet
backend/data/weather_cache/run_20260202T1200_4199b8705f8d68f5.json
backend/data/weather_cache/run_20260202T1200_4199b8705f8d68f5.parquet
backend/data/weather_cache/run_20260203T1200_220c05f707529886.json
backend/data/weather_cache/run_20260203T1200_220c05f707529886.parquet
backend/data/weather_cache/run_20260204T1200_39677087733daaf2.json
backend/data/weather_cache/run_20260204T1200_39677087733daaf2.parquet
backend/data/weather_cache/run_20260205T1200_484e439779e82522.json
backend/data/weather_cache/run_20260205T1200_484e439779e82522.parquet
backend/data/weather_cache/run_20260206T1200_000bb767a28af915.json
backend/data/weather_cache/run_20260206T1200_000bb767a28af915.parquet
backend/data/weather_cache/run_20260207T1200_2d8973e0f45fe4f6.json
backend/data/weather_cache/run_20260207T1200_2d8973e0f45fe4f6.parquet
backend/data/weather_cache/run_20260208T1200_4874238d598f02b6.json
backend/data/weather_cache/run_20260208T1200_4874238d598f02b6.parquet
backend/data/weather_cache/run_20260209T1200_adb0865a328e8952.json
backend/data/weather_cache/run_20260209T1200_adb0865a328e8952.parquet
backend/data/weather_cache/run_20260210T1200_4ff4e0525abc2fcd.json
backend/data/weather_cache/run_20260210T1200_4ff4e0525abc2fcd.parquet
backend/data/weather_cache/run_20260211T1200_6a569e365f615411.json
backend/data/weather_cache/run_20260211T1200_6a569e365f615411.parquet
backend/data/weather_cache/run_20260212T1200_dec6c9abab102c68.json
backend/data/weather_cache/run_20260212T1200_dec6c9abab102c68.parquet
backend/data/weather_cache/run_20260213T1200_f7dd729ed288ca0e.json
backend/data/weather_cache/run_20260213T1200_f7dd729ed288ca0e.parquet
backend/data/weather_cache/run_20260214T1200_c6823bf133cefdf4.json
backend/data/weather_cache/run_20260214T1200_c6823bf133cefdf4.parquet
backend/data/weather_cache/run_20260215T1200_c774171e62f13d73.json
backend/data/weather_cache/run_20260215T1200_c774171e62f13d73.parquet
backend/data/weather_cache/run_20260216T1200_53efde1fc5511f4e.json
backend/data/weather_cache/run_20260216T1200_53efde1fc5511f4e.parquet
backend/data/weather_cache/run_20260217T1200_b16455f1c7b81da3.json
backend/data/weather_cache/run_20260217T1200_b16455f1c7b81da3.parquet
backend/data/weather_cache/run_20260218T1200_45fa95eefb095a7d.json
backend/data/weather_cache/run_20260218T1200_45fa95eefb095a7d.parquet
backend/data/weather_cache/run_20260219T1200_6dd2b76ca9e476a4.json
backend/data/weather_cache/run_20260219T1200_6dd2b76ca9e476a4.parquet
backend/data/weather_cache/run_20260220T1200_837a3233aef01fa7.json
backend/data/weather_cache/run_20260220T1200_837a3233aef01fa7.parquet
backend/data/weather_cache/run_20260221T1200_85f7956154523c5e.json
backend/data/weather_cache/run_20260221T1200_85f7956154523c5e.parquet
backend/data/weather_cache/run_20260222T1200_34caf73821fb198a.json
backend/data/weather_cache/run_20260222T1200_34caf73821fb198a.parquet
backend/data/weather_cache/run_20260223T1200_8c5deb8d43756d0d.json
backend/data/weather_cache/run_20260223T1200_8c5deb8d43756d0d.parquet
backend/data/weather_cache/run_20260224T1200_4db4c53d89832794.json
backend/data/weather_cache/run_20260224T1200_4db4c53d89832794.parquet
backend/data/weather_cache/run_20260225T1200_5a0595455910c5ae.json
backend/data/weather_cache/run_20260225T1200_5a0595455910c5ae.parquet
backend/data/weather_cache/run_20260226T1200_81b01e4a144d1f21.json
backend/data/weather_cache/run_20260226T1200_81b01e4a144d1f21.parquet
backend/data/weather_cache/run_20260227T1200_9052df5fd4ae99be.json
backend/data/weather_cache/run_20260227T1200_9052df5fd4ae99be.parquet
backend/outputs/actuals_jan.csv
backend/outputs/agent_runs.jsonl
backend/outputs/asof_model_metadata.json
backend/outputs/backtest_manifest.json
backend/outputs/data_inspection.md
backend/outputs/data_profile.json
backend/outputs/december_experiment.json
backend/outputs/forecast_metadata.json
backend/outputs/forecasts.csv
backend/outputs/forecasts_fallback_demo.csv
backend/outputs/forecasts_jan.csv
backend/outputs/forecasts_mock.csv
backend/outputs/hgb_asof_model.joblib
backend/outputs/hgb_jan_eval_model.joblib
backend/outputs/hgb_model.joblib
backend/outputs/hgb_validation_model.joblib
backend/outputs/jan_evaluation_rows.csv
backend/outputs/jan_model_metadata.json
backend/outputs/metrics.json
backend/outputs/model_metadata.json
backend/outputs/power_curve.csv
backend/outputs/training_frame.parquet
backend/outputs/turbines_hourly.parquet
backend/outputs/validation_metrics.csv
backend/outputs/validation_model_metadata.json
backend/outputs/validation_predictions.parquet
backend/outputs/wind_correlations.csv
backend/requirements.txt
backend/src/__init__.py
backend/src/agent/__init__.py
backend/src/agent/forecast.py
backend/src/agent/run.py
backend/src/agent/tools.py
backend/src/api/__init__.py
backend/src/api/__main__.py
backend/src/api/application.py
backend/src/api/domain.py
backend/src/api/infrastructure.py
backend/src/api/server.py
backend/src/data/__init__.py
backend/src/data/load.py
backend/src/eval/__init__.py
backend/src/eval/backtest_jan.py
backend/src/features/__init__.py
backend/src/features/build.py
backend/src/features/join.py
backend/src/fleet/__init__.py
backend/src/fleet/repository.py
backend/src/fleet/service.py
backend/src/fleet/tasks.py
backend/src/fleet/training.py
backend/src/fleet/validation.py
backend/src/model/__init__.py
backend/src/model/experiment.py
backend/src/model/train.py
backend/src/weather/__init__.py
backend/src/weather/client.py
backend/tests/test_agent_tools.py
backend/tests/test_api.py
backend/tests/test_config.py
backend/tests/test_data_load.py
backend/tests/test_experiment.py
backend/tests/test_fleet_mongo.py
backend/tests/test_fleet_training.py
backend/tests/test_forecast.py
backend/tests/test_january_metrics.py
backend/tests/test_join.py
backend/tests/test_model.py
backend/tests/test_output_contract.py
backend/tests/test_weather_client.py
docker-compose.dev.yml
docker-compose.yml
frontend/.dockerignore
frontend/Dockerfile
frontend/README.md
frontend/docker/nginx.conf
frontend/index.html
frontend/package-lock.json
frontend/package.json
frontend/public/favicon.svg
frontend/public/wind-hero.png
frontend/scripts/check-container.mjs
frontend/src/app/App.tsx
frontend/src/app/navigation.ts
frontend/src/app/useForecastWorkspace.ts
frontend/src/components/ActualComparison.css
frontend/src/components/ActualComparison.tsx
frontend/src/components/ErrorPanel.tsx
frontend/src/components/HorizonControl.tsx
frontend/src/components/Metric.tsx
frontend/src/components/UpdateDialog.tsx
frontend/src/components/charts/DataTooltip.tsx
frontend/src/components/charts/PowerChart.tsx
frontend/src/components/charts/WeatherChart.tsx
frontend/src/components/forecast/AgentSteps.tsx
frontend/src/components/forecast/HourlyTable.tsx
frontend/src/components/forecast/ReleaseComparison.tsx
frontend/src/components/map/TurbineMap.tsx
frontend/src/components/map/turbineLayer.ts
frontend/src/data/actualsCsv.ts
frontend/src/data/api.ts
frontend/src/domain/forecast.ts
frontend/src/domain/map.ts
frontend/src/lib/format.ts
frontend/src/main.tsx
frontend/src/pages/ForecastPage.tsx
frontend/src/pages/HistoryPage.tsx
frontend/src/pages/MapPage.css
frontend/src/pages/MapPage.tsx
frontend/src/pages/TurbinesPage.css
frontend/src/pages/TurbinesPage.tsx
frontend/src/pages/WeatherPage.tsx
frontend/src/styles/global.css
frontend/src/vite-env.d.ts
frontend/tests/api.test.mjs
frontend/tests/fixtures.mjs
frontend/tests/fleet.test.mjs
frontend/tests/map.test.mjs
frontend/tests/navigation.test.mjs
frontend/tsconfig.json
frontend/vite.config.ts
Ветки (4)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.