diff --git a/FilterCair.Client/Pages/Admin/Customers.razor b/FilterCair.Client/Pages/Admin/Customers.razor index f792527..c97b636 100644 --- a/FilterCair.Client/Pages/Admin/Customers.razor +++ b/FilterCair.Client/Pages/Admin/Customers.razor @@ -40,7 +40,7 @@ Ansprechpartner Telefon Email - + @@ -53,6 +53,10 @@ @c.Telefon @c.Email + @@ -117,6 +126,7 @@ private List customers = new(); private CustomerModel newCustomer = new(); private bool showModal = false; + private bool isEditMode = false; private bool isLoading = true; protected override async Task OnInitializedAsync() @@ -134,6 +144,22 @@ private void ShowCreateModal() { newCustomer = new CustomerModel(); + isEditMode = false; + showModal = true; + } + + private void ShowEditModal(CustomerModel c) + { + newCustomer = new CustomerModel + { + Id = c.Id, + Name = c.Name, + Standort = c.Standort, + Ansprechpartner = c.Ansprechpartner, + Telefon = c.Telefon, + Email = c.Email + }; + isEditMode = true; showModal = true; } @@ -144,16 +170,22 @@ private async Task SaveCustomer() { - var success = await CustomerService.AddCustomerAsync(newCustomer); + bool success; + + if (isEditMode) + success = await CustomerService.UpdateCustomerAsync(newCustomer); + else + success = await CustomerService.AddCustomerAsync(newCustomer); + if (success) { await LoadCustomers(); - await JS.InvokeVoidAsync("alert", "✅ Kunde erfolgreich angelegt!"); CloseModal(); + await JS.InvokeVoidAsync("showToast", "✅ Erfolgreich gespeichert!"); } else { - await JS.InvokeVoidAsync("alert", "❌ Fehler beim Speichern."); + await JS.InvokeVoidAsync("showToast", "❌ Fehler beim Speichern.", "error"); } } @@ -166,11 +198,11 @@ if (success) { customers.Remove(customer); - await JS.InvokeVoidAsync("alert", "🗑️ Kunde gelöscht."); + await JS.InvokeVoidAsync("showToast", "🗑️ Kunde gelöscht"); } else { - await JS.InvokeVoidAsync("alert", "❌ Fehler beim Löschen."); + await JS.InvokeVoidAsync("showToast", "❌ Fehler beim Löschen.", "error"); } } } diff --git a/FilterCair.Client/Pages/CustomerList.razor b/FilterCair.Client/Pages/CustomerList.razor index 26838f7..d95bebc 100644 --- a/FilterCair.Client/Pages/CustomerList.razor +++ b/FilterCair.Client/Pages/CustomerList.razor @@ -76,9 +76,12 @@ customers = await CustomerService.GetCustomersAsync(); } - private async Task SelectCustomer(CustomerModel c) - { - await State.SetCustomerAsync(c.Name); - Nav.NavigateTo("/qrscanner"); - } +private async Task SelectCustomer(CustomerModel c) +{ + + await State.SetCustomerAsync(c.Name); + + Nav.NavigateTo($"/stations/{c.Id}"); +} + } diff --git a/FilterCair.Client/Pages/FilterForm.razor b/FilterCair.Client/Pages/FilterForm.razor index d2893a3..0a6d305 100644 --- a/FilterCair.Client/Pages/FilterForm.razor +++ b/FilterCair.Client/Pages/FilterForm.razor @@ -1,7 +1,9 @@ @page "/filterform" @inject IJSRuntime JS +@inject NavigationManager NavManager @using FilterCair.Shared.Models @using Microsoft.AspNetCore.Components.Forms +@inject FilterCair.Client.Services.API.FilterService FilterService Filterdaten erfassen @@ -18,20 +20,20 @@
+
- - + +
- - - - @foreach (var h in halls) - { - - } - + + +
+ +
+ +
@@ -45,34 +47,20 @@
- - + +
-
- - +
+ +
- -
- - +
+ +
- @if (imagePreviews.Count > 0) - { -
-
- @foreach (var img in imagePreviews) - { -
- Foto -
- } -
-
- }
@@ -100,48 +88,72 @@ @code { private FilterModel filter = new(); - private List halls = new() { "Halle A", "Halle B", "Halle C" }; private bool showToast; - private List imagePreviews = new(); - - private async Task OnFilesSelected(InputFileChangeEventArgs e) - { - imagePreviews.Clear(); - - foreach (var file in e.GetMultipleFiles(3)) // max. 3 Bilder - { - using var stream = file.OpenReadStream(maxAllowedSize: 5_000_000); // 5 MB max - using var ms = new MemoryStream(); - await stream.CopyToAsync(ms); - var base64 = Convert.ToBase64String(ms.ToArray()); - imagePreviews.Add($"data:{file.ContentType};base64,{base64}"); - } - - StateHasChanged(); - } private async Task SaveForm() { - Console.WriteLine($"Gespeichert: {System.Text.Json.JsonSerializer.Serialize(filter)}"); - await JS.InvokeVoidAsync("console.log", "Filter gespeichert:", filter); + bool success; - showToast = true; - StateHasChanged(); + if (filter.Id > 0) + { + // Update vorhandenen Filter + success = await FilterService.UpdateFilterAsync(filter); + } + else + { + // Neuer Filter + success = await FilterService.AddFilterAsync(filter); + } - await Task.Delay(3000); - showToast = false; - StateHasChanged(); + if (success) + { + showToast = true; + StateHasChanged(); + + await Task.Delay(3000); + showToast = false; + StateHasChanged(); + + await JS.InvokeVoidAsync("showToast", "✅ Filter erfolgreich gespeichert!"); + } + else + { + await JS.InvokeVoidAsync("showToast", "❌ Fehler beim Speichern.", "error"); + } } - [Inject] NavigationManager? NavManager { get; set; } protected override void OnInitialized() { - var qrParam = NavManager?.ToAbsoluteUri(NavManager.Uri).Query; - if (!string.IsNullOrEmpty(qrParam)) + var uri = NavManager.ToAbsoluteUri(NavManager.Uri); + var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + var stationIdStr = query.Get("stationId"); + if (int.TryParse(stationIdStr, out int stationId)) { - var parts = System.Web.HttpUtility.ParseQueryString(qrParam); - filter.FilterId = parts.Get("id"); + filter.StationId = stationId; } } + + protected override async Task OnInitializedAsync() +{ + var uri = NavManager.ToAbsoluteUri(NavManager.Uri); + var query = System.Web.HttpUtility.ParseQueryString(uri.Query); + + // StationId setzen + if (int.TryParse(query.Get("stationId"), out int stationId)) + { + filter.StationId = stationId; + } + + // Falls FilterId vorhanden → bestehenden Filter laden + if (int.TryParse(query.Get("filterId"), out int filterId)) + { + var existing = await FilterService.GetFilterByIdAsync(filterId); + if (existing != null) + filter = existing; + } +} + + + } diff --git a/FilterCair.Client/Pages/Filters.razor b/FilterCair.Client/Pages/Filters.razor new file mode 100644 index 0000000..14706ad --- /dev/null +++ b/FilterCair.Client/Pages/Filters.razor @@ -0,0 +1,87 @@ +@page "/stations/{customerId:int}/filters/{stationId:int}" +@using FilterCair.Shared.Models +@inject FilterCair.Client.Services.API.FilterService FilterService +@inject NavigationManager Nav + +Filterübersicht + +
+

+ Filterübersicht +

+ + @if (isLoading) + { +
+
+

Lade Filter...

+
+ } + else if (filters.Count == 0) + { +
+ Keine Filter für diese Station vorhanden. +
+ } + else + { +
+ @foreach (var f in filters) + { +
+
+
+
+ @f.Bezeichnung +
+

+ Typ: @f.Typ +

+

+ Seriennr.: @f.Seriennummer +

+

+ Zustand: @f.Zustand +

+

+ Letzte Wartung: + @(f.LetzteWartung?.ToString("dd.MM.yyyy") ?? "–") +

+
+ +
+
+ } +
+ } +
+ +@code { + [Parameter] public int customerId { get; set; } + [Parameter] public int stationId { get; set; } + + private List filters = new(); + private bool isLoading = true; + + protected override async Task OnInitializedAsync() + { + await LoadFilters(); + } + + private async Task LoadFilters() + { + isLoading = true; + filters = await FilterService.GetFiltersByStationAsync(stationId); + isLoading = false; + } + + private void OpenFilterForm(FilterModel filter) + { + Nav.NavigateTo($"/filterform?filterId={filter.Id}&stationId={stationId}&customerId={customerId}"); + } +} diff --git a/FilterCair.Client/Pages/Stations.razor b/FilterCair.Client/Pages/Stations.razor new file mode 100644 index 0000000..5ee7753 --- /dev/null +++ b/FilterCair.Client/Pages/Stations.razor @@ -0,0 +1,126 @@ +@page "/stations/{customerId:int}" +@using FilterCair.Shared.Models +@inject FilterCair.Client.Services.API.StationService StationService +@inject FilterCair.Client.Services.API.FilterService FilterService +@inject NavigationManager Nav +@inject IJSRuntime JS + +Stationen + +
+

+ Stationen der Fabrik +

+ + +
+ + + @if (!string.IsNullOrEmpty(scanResult)) + { +
+ QR erkannt: @scanResult +
+ } + +
+ + +
+
+ +
+ + + @if (isLoading) + { +
+
+

Lade Stationen...

+
+ } + else if (stations.Count == 0) + { +
+ Keine Stationen gefunden. +
+ } + else + { +
+ @foreach (var s in stations) + { +
+
+
+
+ @s.Name +
+

+ @s.Standort +

+

@s.Beschreibung

+
+
+
+ } +
+ } +
+ +@code { + [Parameter] public int customerId { get; set; } + + private List stations = new(); + private string? scanResult; + private bool isLoading = true; + + protected override async Task OnInitializedAsync() + { + await LoadStations(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JS.InvokeVoidAsync("QRScanner.init", DotNetObjectReference.Create(this)); + } + } + + private async Task LoadStations() + { + try + { + isLoading = true; + stations = await StationService.GetStationsByCustomerAsync(customerId); + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Laden der Stationen: {ex.Message}"); + } + finally + { + isLoading = false; + } + } + + private void SelectStation(StationModel s) + { + Nav.NavigateTo($"/stations/{customerId}/filters/{s.Id}"); + } + + private async Task StartScan() => await JS.InvokeVoidAsync("QRScanner.start"); + private async Task StopScan() => await JS.InvokeVoidAsync("QRScanner.stop"); + + [JSInvokable] + public void OnQrDetected(string code) + { + scanResult = code; + // TODO: später Filter anhand QR-Code öffnen + // Nav.NavigateTo($"/stations/{customerId}/filterdetail/{code}"); + StateHasChanged(); + } +} diff --git a/FilterCair.Client/Program.cs b/FilterCair.Client/Program.cs index 16f51d9..24790f9 100644 --- a/FilterCair.Client/Program.cs +++ b/FilterCair.Client/Program.cs @@ -1,8 +1,9 @@ using FilterCair.Client; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using FilterCair.Client.Services; +using FilterCair.Client.Services.API; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); @@ -26,6 +27,8 @@ builder.Services.AddMsalAuthentication(options => builder.Services.AddScoped(); //API Services -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); await builder.Build().RunAsync(); diff --git a/FilterCair.Client/Services/API/CustomerService.cs b/FilterCair.Client/Services/API/CustomerService.cs index f74c35b..816f3bb 100644 --- a/FilterCair.Client/Services/API/CustomerService.cs +++ b/FilterCair.Client/Services/API/CustomerService.cs @@ -57,5 +57,19 @@ namespace FilterCair.Client.Services.API return false; } } + + public async Task UpdateCustomerAsync(CustomerModel customer) + { + try + { + var response = await _http.PutAsJsonAsync($"{BaseUrl}/{customer.Id}", customer); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Aktualisieren: {ex.Message}"); + return false; + } + } } } diff --git a/FilterCair.Client/Services/API/FilterService.cs b/FilterCair.Client/Services/API/FilterService.cs new file mode 100644 index 0000000..7806320 --- /dev/null +++ b/FilterCair.Client/Services/API/FilterService.cs @@ -0,0 +1,96 @@ +using System.Net.Http.Json; +using FilterCair.Shared.Models; + +namespace FilterCair.Client.Services.API +{ + public class FilterService + { + private readonly HttpClient _http; + private readonly IConfiguration _config; + private string BaseUrl => _config["Api:BaseUrl"] + "/api/Filter"; + + public FilterService(HttpClient http, IConfiguration config) + { + _http = http; + _config = config; + } + + // Alle Filter einer Station laden + public async Task> GetFiltersByStationAsync(int stationId) + { + try + { + var filters = await _http.GetFromJsonAsync>($"{BaseUrl}/byStation/{stationId}"); + return filters ?? new(); + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Laden der Filter: {ex.Message}"); + return new(); + } + } + + // Filter anlegen + public async Task AddFilterAsync(FilterModel filter) + { + try + { + var response = await _http.PostAsJsonAsync(BaseUrl, filter); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Erstellen: {ex.Message}"); + return false; + } + } + + // Filter per QR-Code abrufen + public async Task GetFilterByQRCodeAsync(string qrCode) + { + try + { + var result = await _http.GetFromJsonAsync($"{BaseUrl}/byQr/{qrCode}"); + return result; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Laden per QR-Code: {ex.Message}"); + return null; + } + } + + // Einzelnen Filter per ID laden + public async Task GetFilterByIdAsync(int id) + { + try + { + var result = await _http.GetFromJsonAsync($"{BaseUrl}/{id}"); + return result; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Laden des Filters: {ex.Message}"); + return null; + } + } + + // Filter aktualisieren + public async Task UpdateFilterAsync(FilterModel filter) + { + try + { + var response = await _http.PutAsJsonAsync($"{BaseUrl}/{filter.Id}", filter); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Aktualisieren: {ex.Message}"); + return false; + } + } + + + + } +} diff --git a/FilterCair.Client/Services/API/StationService.cs b/FilterCair.Client/Services/API/StationService.cs new file mode 100644 index 0000000..bfce7f1 --- /dev/null +++ b/FilterCair.Client/Services/API/StationService.cs @@ -0,0 +1,48 @@ +using System.Net.Http.Json; +using FilterCair.Shared.Models; + +namespace FilterCair.Client.Services.API +{ + public class StationService + { + private readonly HttpClient _http; + private readonly IConfiguration _config; + private string BaseUrl => _config["Api:BaseUrl"] + "/api/Station"; + + public StationService(HttpClient http, IConfiguration config) + { + _http = http; + _config = config; + } + + // 🔹 Alle Stationen eines Kunden laden + public async Task> GetStationsByCustomerAsync(int customerId) + { + try + { + var stations = await _http.GetFromJsonAsync>($"{BaseUrl}/byCustomer/{customerId}"); + return stations ?? new(); + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Laden der Stationen: {ex.Message}"); + return new(); + } + } + + // 🔹 Neue Station anlegen + public async Task AddStationAsync(StationModel station) + { + try + { + var response = await _http.PostAsJsonAsync(BaseUrl, station); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Fehler beim Erstellen: {ex.Message}"); + return false; + } + } + } +} diff --git a/FilterCair.Client/wwwroot/index.html b/FilterCair.Client/wwwroot/index.html index 21cedf7..95c32b1 100644 --- a/FilterCair.Client/wwwroot/index.html +++ b/FilterCair.Client/wwwroot/index.html @@ -43,6 +43,7 @@ + diff --git a/FilterCair.Client/wwwroot/js/toastcontainer.js b/FilterCair.Client/wwwroot/js/toastcontainer.js new file mode 100644 index 0000000..66e6d8b --- /dev/null +++ b/FilterCair.Client/wwwroot/js/toastcontainer.js @@ -0,0 +1,30 @@ +window.showToast = (message, type = "success") => { + const container = document.querySelector(".toast-container") || createToastContainer(); + const toastEl = document.createElement("div"); + + toastEl.className = `toast align-items-center text-white border-0 mb-2 bg-${type === "error" ? "danger" : + type === "warning" ? "warning" : + type === "info" ? "info" : "success" + }`; + + toastEl.role = "alert"; + toastEl.innerHTML = ` +
+
${message}
+ +
+ `; + + container.appendChild(toastEl); + const toast = new bootstrap.Toast(toastEl, { delay: 3000 }); + toast.show(); + + toastEl.addEventListener("hidden.bs.toast", () => toastEl.remove()); +}; + +function createToastContainer() { + const container = document.createElement("div"); + container.className = "toast-container position-fixed bottom-0 end-0 p-3"; + document.body.appendChild(container); + return container; +} diff --git a/FilterCair.Server/Program.cs b/FilterCair.Server/Program.cs index acd8ad1..d6e7d2c 100644 --- a/FilterCair.Server/Program.cs +++ b/FilterCair.Server/Program.cs @@ -19,7 +19,7 @@ builder.Services.AddCors(options => { options.AddPolicy("AllowClient", policy => { - policy.WithOrigins("https://filtercair-client-efava4bfgvamhkfu.westeurope-01.azurewebsites.net", "https://localhost:5001") + policy.WithOrigins("https://filtercair-client-efava4bfgvamhkfu.westeurope-01.azurewebsites.net", "https://localhost:5186", "http://localhost:5186") .AllowAnyHeader() .AllowAnyMethod(); }); diff --git a/FilterCair.Shared/Models/FilterModel.cs b/FilterCair.Shared/Models/FilterModel.cs index 686a039..5eace89 100644 --- a/FilterCair.Shared/Models/FilterModel.cs +++ b/FilterCair.Shared/Models/FilterModel.cs @@ -9,14 +9,14 @@ namespace FilterCair.Shared.Models { public class FilterModel { - [Required] - public string? FilterId { get; set; } - - [Required] - public string? Halle { get; set; } - - public string? Zustand { get; set; } - public double? Luftdruck { get; set; } - public string? Bemerkung { get; set; } + public int Id { get; set; } + public int StationId { get; set; } + public string Bezeichnung { get; set; } = string.Empty; + public string Typ { get; set; } = string.Empty; + public string Seriennummer { get; set; } = string.Empty; + public DateTime? Einbaudatum { get; set; } + public DateTime? LetzteWartung { get; set; } + public string Zustand { get; set; } = string.Empty; + public string QRCode { get; set; } = string.Empty; } }