diff --git a/BattSim Logo.png b/BattSim Logo.png new file mode 100644 index 0000000..4ebec29 Binary files /dev/null and b/BattSim Logo.png differ diff --git a/Layout/MainLayout.razor b/Layout/MainLayout.razor index e1a9a75..724fc91 100644 --- a/Layout/MainLayout.razor +++ b/Layout/MainLayout.razor @@ -1,3 +1,5 @@ -@inherits LayoutComponentBase +@inherits LayoutComponentBase -@Body +
+ @Body +
diff --git a/Pages/Home.razor b/Pages/Home.razor index cd397e3..31c7094 100644 --- a/Pages/Home.razor +++ b/Pages/Home.razor @@ -1,79 +1,543 @@ -@page "/" +@page "/" @using Radzen @using Radzen.Blazor @using BattSim.Models @using BattSim.Services -Energie Simulator +Thuisbatterij Simulator -

Energie Simulator

+

Thuisbatterij Simulator

-

Je Fluvius Energie Data

-
-

Ga naar de website van Fluvius en download je historische elektriciteitsverbruiksgegevens met kwartier nauwkeurigheid. Voor een optimale simulatie is het raadzaam om een volledig jaar te downloaden. Dit geeft je inzicht in seizoensgebonden patronen en helpt om realistische uitkomsten te genereren.

- - @if (_isLoadingFile) {

Data aan het inladen...

} - else - { - @if (FluviusDataRaw.Length != 0) - { -

@(FluviusDataRaw.Length) kwartieren ingeladen.

- } +
+ +
+
+ + +
+
+
1
+
Fluvius Data
+
+
+
+
+

Upload je Fluvius kwartierdata om te beginnen

+ +
+
+ + + @if (FluviusDataRaw.Length > 0 && !StepData[0].isProcessing) + { +

@_uploadedFileName

+ } +
+
+ + @if (StepData[0].Completed) + { +
+ Data succesvol ingeladen en verwerkt! +
+ } + @if (_uploadError != null) + { +
+ @_uploadError +
+ } + +
+

Tip: Download een volledig jaar aan kwartierdata voor de meest accurate resultaten.

+
+
+
+
+ + +
+
+
2
+
Batterij Simulatie
+
+
+
+
+

Test verschillende batterijconfiguraties

+ + @if (FluviusDataRaw.Length == 0) + { +
+

Upload eerst je Fluvius data (stap 1) om deze stap te kunnen uitvoeren.

+
+ } + else + { +
+
+
+ + +
+
+ + +
+
+

+ Efficiëntie is het percentage van opgeslagen energie dat terug kan worden gebruikt. +

+
+ +
+ +
+ + @if (StepData[1].Completed) + { +
+ Simulatie voltooid! @SimulationData.Length kwartieren gesimuleerd. +
+ } + } +
+
+
+ + +
+
+
3
+
Kosten Berekenen
+
+
+
+
+

Vergelijk kosten met en zonder batterij

+ + +
+

+ Vaste Kosten +

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ Energiekosten +

+
+
+ + +
+
+ + +
+
+
+ + +
+

+ Heffingen Groene Stroom +

+
+
+ + +
+
+ + +
+
+
+ + +
+

+ Net- en distributiekosten +

+
+
+ + +
+
+ + +
+
+
+ + +
+

+ Heffingen en Toeslagen +

+
+
+ + +
+
+ + +
+
+
+ +
+ +
+ + @if (StepData[2].Completed) + { +
+

Originele kosten: €@normalCost.ToString("0.00")

+

Gesimuleerde kosten: €@simulatedCost.ToString("0.00")

+

Besparing: €@((normalCost - simulatedCost).ToString("0.00"))

+ @if (normalCost > 0) + { +

Besparingspercent: @(((normalCost - simulatedCost) / normalCost * 100).ToString("0.00"))%

+ } +
+ } +
+
+
+
- @if (FluviusDataDaily.Length != 0) - { - - - - - - - - - - - - - - - - - } - }
-@code{ + + +@if (FluviusDataDaily.Length > 0) +{ +
+

Overzicht: Energie Data & Simulatie

+ + +
+ + + + + +
+ + + + + + + + + + @if (SimulationDataDaily.Length > 0) + { + + + + + + + } + + + + + + + + + + +
+} + +@code { EnergyData[] FluviusDataRaw = []; EnergyData[] FluviusDataDaily = []; - bool _isLoadingFile = false; + + SimulatedBatteryEnergyData[] SimulationData = []; + SimulatedBatteryEnergyData[] SimulationDataDaily = []; + + EnergyData[] FilteredFluviusData = []; + SimulatedBatteryEnergyData[] FilteredSimulationData = []; + + EnergyCostCalculator calculator = new(); + double normalCost = 0.0; + double simulatedCost = 0.0; + + struct PipelineStep{ + public bool Expanded; + public bool Completed; + public bool isProcessing; + } + + PipelineStep[] StepData = [ + new PipelineStep(), // Step 1 + new PipelineStep(), // Step 2 + new PipelineStep(), // Step 3 + ]; + + string _uploadedFileName = string.Empty; + string? _uploadError = null; + + string _timePeriod = "all"; + + double BatteryCapacity = 7.5; + double Efficiency = 90; + + private string GetPipelineStepClasses(int step){ + var classes = new List(); + if (StepData[step].Expanded) classes.Add("expanded"); + if (StepData[step].Completed) classes.Add("completed"); + if (StepData[step].isProcessing) classes.Add("running"); + return string.Join(" ", classes); + } + + private string GetTimeButtonClass(string period) + { + var baseClass = "time-toggle-button"; + if (_timePeriod == period) + { + return $"{baseClass} active"; + } + return baseClass; + } + + private void SetTimePeriodAll() => SetTimePeriod("all"); + private void SetTimePeriodYear() => SetTimePeriod("year"); + private void SetTimePeriodMonth() => SetTimePeriod("month"); + private void SetTimePeriodWeek() => SetTimePeriod("week"); + private void SetTimePeriodDay() => SetTimePeriod("day"); + + private void ToggleBox(int step) + { + if(step == 1 && FluviusDataRaw.Length == 0) return; + if(step == 2 && SimulationData.Length == 0) return; + StepData[step].Expanded = !StepData[step].Expanded; + StateHasChanged(); + } private async Task OnFileUploaded(InputFileChangeEventArgs e) { + _uploadError = null; var file = e.File; + _uploadedFileName = file.Name; + if (file.ContentType != "text/csv") { - Console.WriteLine("Only CSV files are allowed!"); + _uploadError = "Alleen CSV bestanden zijn toegestaan!"; + StateHasChanged(); return; } try { - Console.WriteLine("Reading csv file..."); - _isLoadingFile = true; + StepData[0].isProcessing = true; StateHasChanged(); + FluviusDataRaw = await FluviusDataHandler.LoadAndProcessFile(file); FluviusDataDaily = FluviusDataHandler.GenerateDailyData(FluviusDataRaw); - _isLoadingFile = false; + FilteredFluviusData = FluviusDataDaily; + FilteredSimulationData = []; + + StepData[0].isProcessing = false; + StepData[0].Completed = true; + StepData[1].Completed = false; + StepData[2].Completed = false; + SimulationData = []; + SimulationDataDaily = []; + StateHasChanged(); - Console.WriteLine("Done reading csv file!"); } catch (Exception ex) { - Console.WriteLine($"Error loading file: {ex.Message}"); + StepData[0].isProcessing = false; + _uploadError = ex.Message; + StateHasChanged(); } } - private void OnSeriesClick(){} + private void SimulateBattery() + { + if (FluviusDataRaw.Length == 0) return; + + StepData[1].isProcessing = true; + StateHasChanged(); + + try + { + SimulationData = BatterySimulator.SimulateBattery(FluviusDataRaw, BatteryCapacity, Efficiency/100); + SimulationDataDaily = BatterySimulator.GenerateDailyData(SimulationData); + FilteredSimulationData = SimulationDataDaily; + + StepData[1].isProcessing = false; + StepData[1].Completed = true; + StepData[2].Completed = false; + normalCost = 0; + simulatedCost = 0; + + StateHasChanged(); + } + catch (Exception ex) + { + StepData[1].isProcessing = false; + Console.WriteLine($"Error: {ex.Message}"); + } + } + + private void Calculate() + { + if (SimulationData.Length == 0) return; + + StepData[2].isProcessing = true; + StateHasChanged(); + + try + { + normalCost = calculator.CalculateCostOfEnergyUsage(FluviusDataRaw); + simulatedCost = calculator.CalculateCostOfEnergyUsage(SimulationData); + + StepData[2].isProcessing = false; + StepData[2].Completed = true; + StateHasChanged(); + } + catch (Exception ex) + { + StepData[2].isProcessing = false; + Console.WriteLine($"Error: {ex.Message}"); + } + } + + private void SetTimePeriod(string period) + { + _timePeriod = period; + + // Filter data based on selected period + FilteredFluviusData = FilterFluviusDataByPeriod(period); + FilteredSimulationData = FilterSimulationDataByPeriod(period); + + StateHasChanged(); + } + + private EnergyData[] FilterFluviusDataByPeriod(string period) + { + if (period == "all" || FluviusDataDaily.Length == 0) + return FluviusDataDaily; + + var result = new List(); + + foreach (var item in FluviusDataDaily) + { + // The Time property is DateTime but for daily data it represents the day + var date = DateOnly.FromDateTime(item.Time); + bool include = false; + switch (period) + { + case "day": include = true; break; // Show all daily data + case "week": include = date.DayOfWeek == DayOfWeek.Monday; break; + case "month": include = date.Day == 1; break; + case "year": include = date.Month == 1 && date.Day == 1; break; + } + if (include) result.Add(item); + } + + return result.ToArray(); + } + + private SimulatedBatteryEnergyData[] FilterSimulationDataByPeriod(string period) + { + if (period == "all" || SimulationDataDaily.Length == 0) + return SimulationDataDaily; + + var result = new List(); + + foreach (var item in SimulationDataDaily) + { + var date = DateOnly.FromDateTime(item.Time); + bool include = false; + switch (period) + { + case "day": include = true; break; + case "week": include = date.DayOfWeek == DayOfWeek.Monday; break; + case "month": include = date.Day == 1; break; + case "year": include = date.Month == 1 && date.Day == 1; break; + } + if (include) result.Add(item); + } + + return result.ToArray(); + } + private string FormatObject(object value) { if(value is double d) return $"{d:0.##} kWh"; if(value is DateTime time) return time.ToString("dd/MM/yyyy"); @@ -81,156 +545,3 @@ return string.Empty; } } - -

Batterij Simulatie

-
-

- Met je Fluvius-kwartierdata simuleren we een batterij die elk kwartier de energie die normaal op het elektriciteitsnet wordt teruggeleverd, opvangt en de stroom die normaal uit het net wordt gehaald vervangt—mits de batterij nog voldoende capaciteit heeft. - Op deze manier kun je verschillende batterijcapaciteiten testen om te zien welke installatie het beste past bij jouw verbruiksprofiel en besparingsdoelen. Daarnaast kun je onderzoeken hoe verschillende batterijtypes, met uiteenlopende efficiëntieniveaus, de uitkomsten beïnvloeden. Onder efficiëntie verstaan we hier het aandeel van de opgeslagen energie dat uiteindelijk ook daadwerkelijk weer kan worden gebruikt. -

-
    -
  • -

    Batterijcapaciteit: kWh

    -
  • -
  • -

    Round-trip Efficiëntie: %

    -
  • -
- - @if (_isSimulating) {

Simulating...

} - else - { - @if (SimulationDataDaily.Length != 0) - { - - - - - - - - - - - - - - - - - - - - - - - - - - } - } -
- -@code { - double BatteryCapacity = 7.5; - double Efficiency = 90; - bool _isSimulating = false; - - SimulatedBatteryEnergyData[] SimulationData = []; - SimulatedBatteryEnergyData[] SimulationDataDaily = []; - - private void SimulateBattery(){ - Console.WriteLine("Simulating..."); - _isSimulating = true; - SimulationData = BatterySimulator.SimulateBattery(FluviusDataRaw, BatteryCapacity, Efficiency/100); - SimulationDataDaily = BatterySimulator.GenerateDailyData(SimulationData); - _isSimulating = false; - Console.WriteLine("Done simulating!"); - } -} - -

Bereken kosten

-
-

- Uitleg -

-

Vaste Vergoeding

-
    -
  • -

    Vaste vergoeding: €/jaar

    -
  • -
- -

Energiekosten

-
    -
  • -

    Energiekost: c€/kWh

    -
  • -
  • -

    Terugleveringsvergoeding: c€/kWh

    -
  • -
- -

Heffingen Groene Stroom

-
    -
  • -

    Groene stroomcertificaten: c€/kWh

    -
  • -
  • -

    Warmtekracht certificaten: c€/kWh

    -
  • -
- - -

Net- en distributiekosten

-
    -
  • -

    Tarief databeheer: €/jaar

    -
  • -
  • -

    Capaciteitstarief: €/kWh/jaar

    -
  • -
  • -

    Afnametarief: c€/kWh

    -
  • -
- - -

Heffingen en Toeslagen

-
    -
  • -

    Energiebijdrage: c€/kWh

    -
  • -
  • -

    Bijzondere accijns elektriciteit: c€/kWh

    -
  • -
  • -

    Vlaamse energieheffing elektriciteit: €/jaar

    -
  • -
- - @if (_isCalculating) {

Calculating...

} - else - { -

The original price is: €@normalCost

-

The simulated price is: €@simulatedCost

-

The costsaving is: €@(normalCost - simulatedCost)

- } -
- -@code { - EnergyCostCalculator calculator = new(); - double normalCost = 0.0; - double simulatedCost = 0.0; - bool _isCalculating = false; - - private void Calculate() - { - Console.WriteLine("Calculating..."); - _isCalculating = true; - normalCost = calculator.CalculateCostOfEnergyUsage(FluviusDataRaw); - simulatedCost = calculator.CalculateCostOfEnergyUsage(SimulationData); - _isCalculating = false; - Console.WriteLine("Done Calculating!"); - } -} \ No newline at end of file diff --git a/Services/FluviusDataHandler.cs b/Services/FluviusDataHandler.cs index 4e9ad61..ebe68a1 100644 --- a/Services/FluviusDataHandler.cs +++ b/Services/FluviusDataHandler.cs @@ -85,7 +85,7 @@ namespace BattSim.Services await reader.ReadLineAsync(); // Read all lines into memory var lines = new List(); - string line; + string? line; while ((line = await reader.ReadLineAsync()) is not null) { lines.Add(line); diff --git a/wwwroot/css/site.css b/wwwroot/css/site.css new file mode 100644 index 0000000..004d441 --- /dev/null +++ b/wwwroot/css/site.css @@ -0,0 +1,708 @@ +/* ===== Douwco.be Theme ===== */ + +@font-face { + font-family: 'Righteous'; + src: url('/fonts/Righteous.ttf') format('truetype'); +} + +@font-face { + font-family: 'Montserrat'; + src: url('/fonts/Montserrat.ttf') format('truetype'); + font-weight: normal; +} + +@font-face { + font-family: 'Montserrat-Bold'; + src: url('/fonts/Montserrat.ttf') format('truetype'); + font-weight: bold; +} + +:root { + --background-clr: #283e3e; + --background-accent-clr: #324f4f; + --blue-clr: #47bcdf; + --green-clr: #6ede9a; + --purple-clr: #a48da; + --orange-clr: #e2a661; + --white-clr: #ffffff; + --text-clr: #e0e0e0; + --border-clr: #47bcdf; + --success-clr: #2ecc71; + --warning-clr: #f39c12; + --error-clr: #e74c3c; +} + +/* ===== Base Styles ===== */ + +html, body { + height: 100%; + margin: 0; + padding: 0; + font-family: 'Montserrat', sans-serif; +} + +body { + background-color: var(--background-clr); + color: var(--text-clr); + display: flex; + flex-direction: column; + min-height: 100vh; +} + +main { + flex: 1; + margin: 20px; + overflow-y: auto; + overflow-x: hidden; + scroll-behavior: smooth; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Righteous', sans-serif; + color: var(--white-clr); +} + +h1 { + font-size: 2.5rem; + margin-bottom: 2rem; + text-align: center; +} + +h2 { + font-size: 1.8rem; + margin: 1.5rem 0 1rem 0; + color: var(--blue-clr); +} + +h3 { + font-size: 1.3rem; + margin: 1rem 0 0.5rem 0; + color: var(--green-clr); +} + +p { + line-height: 1.6; + margin: 0.5rem 0; +} + +a { + color: var(--blue-clr); + text-decoration: none; +} + +a:hover { + color: var(--green-clr); +} + +/* ===== Pipeline Layout ===== */ + +.pipeline-container { + display: flex; + flex-direction: column; + gap: 2rem; + margin: 2rem 0; +} + +.pipeline-row { + display: flex; + justify-content: center; + align-items: stretch; + gap: 1rem; + position: relative; + margin-bottom: 2rem; +} + +.pipeline-connector { + position: absolute; + top: 25px; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(to right, var(--blue-clr), var(--green-clr)); + z-index: 0; +} + +.pipeline-connector::before, +.pipeline-connector::after { + content: ''; + position: absolute; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--blue-clr); + top: -4px; +} + +.pipeline-connector::before { + left: -6px; +} + +.pipeline-connector::after { + right: -6px; + background: var(--green-clr); +} + +/* ===== Pipeline Boxes ===== */ + +.pipeline-box { + background-color: var(--background-accent-clr); + border-radius: 15px; + padding: 1.5rem; + width: 300px; + position: relative; + z-index: 1; + transition: border-color 0.3s, box-shadow 0.3s; + border: 2px solid var(--background-accent-clr); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + height: 100px; + overflow: hidden; +} + +.pipeline-box:hover { + border-color: var(--blue-clr); + box-shadow: 0 6px 20px rgba(71, 188, 223, 0.3); +} + +.pipeline-box.expanded { + width: 360px; + border-color: var(--green-clr); + box-shadow: 0 8px 25px rgba(110, 222, 154, 0.4); + height: auto; + overflow: visible; +} + +.pipeline-box:not(.expanded) .box-content { + display: none; +} + +.pipeline-box.completed { + border-color: var(--success-clr); +} + +.pipeline-box.completed .box-header { + border-bottom-color: var(--success-clr); +} + +.pipeline-box.completed .status-indicator { + background-color: var(--success-clr); + box-shadow: 0 0 15px var(--success-clr); +} + +.pipeline-box.completed .step-number { + background-color: var(--success-clr); +} + +/* Box Header */ +.pipeline-box .box-header { + display: flex; + cursor: pointer; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding-bottom: 1rem; + border-bottom: 2px solid var(--border-clr); + transition: all 0.3s ease; +} + +.pipeline-box.expanded .box-header { + border-bottom-color: var(--green-clr); +} + +.pipeline-box .step-number { + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, var(--blue-clr), var(--green-clr)); + display: flex; + align-items: center; + justify-content: center; + font-family: 'Montserrat-Bold', sans-serif; + font-size: 1.2rem; + color: white; + font-weight: bold; +} + +.pipeline-box .step-title { + font-size: 1.3rem; + font-family: 'Montserrat-Bold', sans-serif; + color: var(--white-clr); + flex: 1; + text-align: center; +} + +.pipeline-box .status-indicator { + width: 12px; + height: 12px; + border-radius: 50%; + background-color: var(--background-clr); + border: 2px solid var(--blue-clr); + transition: all 0.3s ease; +} + +.pipeline-box.running .status-indicator { + background-color: var(--orange-clr); + animation: pulse 1.5s infinite; +} + +/* Box Content */ +.pipeline-box .box-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +.pipeline-box.expanded .box-content { + max-height: 3000px; +} + +.pipeline-box:not(.expanded) .box-content { + display: none; +} + +.pipeline-box .box-content-inner { + padding: 0.5rem 0; +} + +.pipeline-box .box-description { + font-size: 0.95rem; + color: var(--text-clr); + margin-bottom: 1.5rem; + text-align: center; +} + +/* Form Elements */ +.pipeline-box .form-section { + margin: 1rem 0; +} + +.pipeline-box label { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; + color: var(--text-clr); +} + +.pipeline-box .input-group { + margin-bottom: 1rem; +} + + +.pipeline-box .input-group input[type="number"] { + margin-bottom: 1rem; +} + +.pipeline-box input[type="number"], +.pipeline-box input[type="file"] { + padding: 0.75rem; + border: 2px solid var(--background-clr); + border-radius: 8px; + background-color: var(--background-clr); + color: var(--white-clr); + font-family: 'Montserrat', sans-serif; + font-size: 1rem; + transition: all 0.3s ease; +} + +.pipeline-box input[type="number"]:focus, +.pipeline-box input[type="file"]:focus { + outline: none; + border-color: var(--blue-clr); + box-shadow: 0 0 10px rgba(71, 188, 223, 0.5); +} + +.pipeline-box input[type="number"] { + -moz-appearance: textfield; + width: 75px; +} + +.pipeline-box input[type="number"]::-webkit-outer-spin-button, +.pipeline-box input[type="number"]::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +.pipeline-box input[type="file"] { + width: 100%; +} + +.pipeline-box .file-input-wrapper { + border: 2px dashed var(--border-clr); + border-radius: 8px; + padding: 1.5rem; + text-align: center; + transition: all 0.3s ease; +} + +.pipeline-box .file-input-wrapper:hover { + border-color: var(--blue-clr); +} + +.pipeline-box .file-input-wrapper input[type="file"] { + display: none; +} + +.pipeline-box .file-input-label { + color: var(--blue-clr); + cursor: pointer; + font-weight: 600; +} + +.pipeline-box .file-name { + margin-top: 0.5rem; + font-size: 0.9rem; + color: var(--green-clr); + word-break: break-all; +} + +/* Buttons */ +.pipeline-box button, +.btn { + display: inline-block; + padding: 0.75rem 1.5rem; + border: none; + border-radius: 8px; + background: linear-gradient(135deg, var(--blue-clr), var(--green-clr)); + color: white; + font-family: 'Montserrat-Bold', sans-serif; + font-size: 1rem; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 1rem; +} + +.pipeline-box button:hover, +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 5px 15px rgba(71, 188, 223, 0.4); +} + +.pipeline-box button:active, +.btn:active { + transform: translateY(0); +} + +.pipeline-box button:disabled, +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + +.pipeline-box.button-container { + display: flex; + justify-content: center; + margin-top: 1rem; +} + +/* Results Display */ +.pipeline-box .results { + margin-top: 1.5rem; + padding: 1rem; + background-color: rgba(71, 188, 223, 0.1); + border-radius: 8px; + border-left: 4px solid var(--blue-clr); +} + +.pipeline-box .results p { + margin: 0.5rem 0; + font-size: 1rem; +} + +.pipeline-box .results .result-value { + font-family: 'Montserrat-Bold', sans-serif; + color: var(--green-clr); + font-size: 1.2rem; +} + +/* ===== Chart Display ===== */ + +.chart-container { + width: 100%; + margin: 2rem 0 0 0; + padding: 0; +} + +.chart-container h2 { + text-align: center; + margin-bottom: 1.5rem; + color: var(--white-clr); +} + +.unified-chart { + min-height: 500px; + background-color: var(--background-accent-clr); + border-radius: 15px; + padding: 1.5rem; + margin: 0 auto; + max-width: calc(100vw - 4rem); + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); + border: 1px solid var(--border-clr); +} + +/* Time Period Toggle */ +.time-toggle-container { + display: flex; + justify-content: center; + gap: 0.5rem; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.time-toggle-button { + padding: 0.5rem 1rem; + border: 2px solid var(--border-clr); + background-color: var(--background-accent-clr); + color: var(--text-clr); + border-radius: 8px; + cursor: pointer; + transition: all 0.3s ease; + font-family: 'Montserrat', sans-serif; + font-size: 0.9rem; +} + +.time-toggle-button:hover { + border-color: var(--blue-clr); + background-color: rgba(71, 188, 223, 0.1); +} + +.time-toggle-button.active { + background: linear-gradient(135deg, var(--blue-clr), var(--green-clr)); + color: white; + border-color: transparent; + box-shadow: 0 4px 12px rgba(71, 188, 223, 0.4); +} + +.time-toggle-button.active:hover { + transform: translateY(-2px); +} + +/* For active button state via data attribute */ +.time-toggle-button[data-active="true"] { + background: linear-gradient(135deg, var(--blue-clr), var(--green-clr)); + color: white; + border-color: transparent; + box-shadow: 0 4px 12px rgba(71, 188, 223, 0.4); +} + +.time-toggle-button[data-active="true"]:hover { + transform: translateY(-2px); +} + +/* ===== Loading States ===== */ +.loading-indicator { + display: flex; + align-items: center; + justify-content: center; + padding: 1rem; +} + +.spinner { + width: 40px; + height: 40px; + border: 4px solid var(--background-clr); + border-top-color: var(--blue-clr); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.2); } +} + +/* ===== Chart Styling ===== */ + +.chart-container { + background-color: var(--background-clr); + border-radius: 8px; + padding: 1rem; + margin: 1rem 0; +} + +/* Radzen Chart Customization */ +:root { + --rz-color-primary: var(--blue-clr); + --rz-color-secondary: var(--green-clr); + --rz-color-text: var(--white-clr); + --rz-color-surface: var(--background-accent-clr); + --rz-color-border: var(--border-clr); +} + +.rz-chart { + background-color: var(--background-clr) !important; +} + +.rz-chart text { + fill: var(--white-clr) !important; + font-family: 'Montserrat', sans-serif !important; +} + +.rz-series-0 path { + stroke: var(--blue-clr) !important; + fill: rgba(71, 188, 223, 0.3) !important; +} + +.rz-series-1 path { + stroke: var(--green-clr) !important; + fill: rgba(110, 222, 154, 0.3) !important; +} + +.rz-series-2 path { + stroke: var(--purple-clr) !important; + fill: rgba(164, 141, 170, 0.3) !important; +} + +.rz-series-3 path { + stroke: var(--orange-clr) !important; + fill: rgba(226, 166, 97, 0.3) !important; +} + +.rz-series-4 path { + stroke: #e74c3c !important; + fill: rgba(231, 76, 60, 0.3) !important; +} + +.rz-gridline line { + stroke: var(--background-clr) !important; +} + +/* ===== Cost Calculation Grid ===== */ +.cost-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin: 1rem 0; +} + +.cost-grid .input-group { + margin-bottom: 0.5rem; +} + +.cost-grid label { + font-size: 0.85rem; +} + +.cost-grid input { + font-size: 0.85rem; + padding: 0.5rem; +} + +/* ===== Responsive Design ===== */ + +@media (max-width: 1200px) { + .pipeline-row { + flex-wrap: wrap; + justify-content: center; + } + + .pipeline-connector { + display: none; + } + + .pipeline-box { + width: 280px; + } + + .pipeline-box.expanded { + width: 340px; + } + + .unified-chart-container { + width: calc(100% - 2rem); + } +} + +@media (max-width: 900px) { + .pipeline-box { + width: 100%; + min-height: 150px; + } + + .pipeline-box.expanded { + width: 100%; + } + + .pipeline-row { + flex-direction: column; + align-items: center; + } + + .pipeline-connector { + display: none; + } + + .unified-chart-container { + width: calc(100% - 2rem); + margin: 1rem; + } +} + +@media (max-width: 768px) { + main { + margin: 10px; + } + + h1 { + font-size: 1.8rem; + } + + h2 { + font-size: 1.4rem; + } + + .pipeline-box { + min-width: 100%; + } + + .pipeline-box.expanded { + min-width: 100%; + transform: none; + } + + .unified-chart-container { + padding: 1rem; + margin: 1rem; + } +} + +/* ===== Info Text ===== */ +.info-text { + background-color: rgba(71, 188, 223, 0.1); + border-left: 4px solid var(--blue-clr); + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; + font-size: 0.95rem; +} + +.info-text p { + margin: 0; +} + +/* ===== Success Message ===== */ +.success-message { + background-color: rgba(46, 204, 113, 0.2); + border-left: 4px solid var(--success-clr); + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; + font-size: 1rem; + color: var(--success-clr); +} + +/* ===== Error Message ===== */ +.error-message { + background-color: rgba(231, 76, 60, 0.2); + border-left: 4px solid var(--error-clr); + padding: 1rem; + border-radius: 8px; + margin: 1rem 0; + font-size: 1rem; + color: var(--error-clr); +} diff --git a/wwwroot/fonts/Montserrat.ttf b/wwwroot/fonts/Montserrat.ttf new file mode 100644 index 0000000..dfbcfe4 Binary files /dev/null and b/wwwroot/fonts/Montserrat.ttf differ diff --git a/wwwroot/fonts/Righteous.ttf b/wwwroot/fonts/Righteous.ttf new file mode 100644 index 0000000..07fc0b4 Binary files /dev/null and b/wwwroot/fonts/Righteous.ttf differ diff --git a/wwwroot/index.html b/wwwroot/index.html index 8d1d176..d1f47a9 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -4,16 +4,23 @@ - BattSim + BattSim - Energie Simulator + + + - + + + + + + + - @@ -31,6 +38,7 @@ Reload 🗙
+