@page "/tire-storage" @implements IAsyncDisposable @inject ITireStackService TireStackService @inject IStorageLocationService StorageLocationService @inject ICatalogService CatalogService @inject UserSessionService UserSession @inject IJSRuntime JS @using Lagerbestand.Components.Shared Lagerübersicht - Reifen Späth Lagerverwaltung @if (!UserSession.IsWorkerOrAdmin) { } else {
@* Compact Toolbar: Stats as clickable filter chips + controls *@
@stats.AvailableLocations Frei
Nach Saison filtern
@if (typeFilter == true && categories.Count > 0) { }
@if (HasActiveFilters) { }
@* Tire Stacks List *@ @if (isLoading) {
Lade Daten...
} else if (showGridView) {
@foreach (var area in new[] { AreaType.Werkstatt, AreaType.Lager }) {

@area.GetBuildingLabel()

@for (int row = StorageLocation.MinRow; row <= area.GetMaxRow(); row++) { var currentRow = row;

Reihe @currentRow

@foreach (var side in new[] { "Links", "Rechts" }) {
@side @foreach (var pos in ShelfPositionExtensions.All.Where(p => p.IsLeft() == (side == "Links")).OrderByDescending(p => p.IsTop())) {
@(pos.IsTop() ? "Oben" : "Unten")
@for (int slot = StorageLocation.MinSlot; slot <= StorageLocation.MaxSlot; slot++) { var loc = GetLocation(area, currentRow, pos, slot); var cellClass = loc == null ? "missing" : !loc.IsAvailable ? "blocked" : loc.IsOccupied ? "occupied" : "free"; }
}
}
}
} @if (selectedLocation != null) { }
} else if (filteredTireStacks.Count == 0) {

Keine Einträge

@(string.IsNullOrEmpty(searchTerm) ? "Noch keine Reifenstapel eingelagert." : "Keine Ergebnisse für Ihre Suche.")

@if (string.IsNullOrEmpty(searchTerm)) { Reifenstapel einlagern }
} else { @if (selectedStack != null) { }
@foreach (var ts in filteredTireStacks) {
@if (ts.IsInventory) { @(ts.CatalogItem?.Name ?? "Lagerbestand") Lager } else { @ts.Customer?.FullName @if (ts.Vehicle != null) { @ts.Vehicle.Make @ts.Vehicle.Model } }
@if (ts.StorageLocation != null) { @ts.StorageLocation.Area.GetDisplayName() Regal @ts.StorageLocation.Row @ts.StorageLocation.Position.GetShortName() P@(ts.StorageLocation.SlotNumber) } else { - }
@(ts.Season == TireSeason.Sommer ? "☀️" : ts.Season == TireSeason.Winter ? "❄️" : "🔄") @ts.ItemCount Reifen @(ts.TireSize != null ? $"| {ts.TireSize}" : "") @(ts.TireModel != null ? $"| {ts.TireModel}" : "") @(ts.WithRims ? "| mit Felgen" : "")
}
} @if (showEditModal && editStack != null) { } @* Expiring Pickups Warning (only for customer stacks) *@ @if (expiringPickups.Count > 0) { }
} @code { private static string CheckinUrl(TireStack stack) => $"/tire-checkin?customerId={stack.CustomerId}"; private static string CheckoutUrl(TireStack stack) => $"/tire-checkout?stackId={stack.Id}"; private RenderFragment StackDetailLists => stack => __builder => {

@(stack.IsInventory ? "Bestand & Lagerplatz" : "Kunde & Lagerplatz")

Ticket-Nr.
@stack.TicketNumber
@if (stack.IsInventory) {
Kategorie
@(stack.CatalogItem?.Name ?? "Lagerbestand")
} else {
Kunde
@(stack.Customer?.FullName ?? "-")
Kundennr.
@(stack.Customer?.CustomerNumber ?? "-")
Telefon
@(stack.Customer?.Phone ?? "-")
Fahrzeug
@(stack.Vehicle != null ? $"{stack.Vehicle.Make} {stack.Vehicle.Model}".Trim() : "-")
}
Lagerplatz
@if (stack.StorageLocation != null) { @stack.StorageLocation.Area.GetDisplayName() Regal @stack.StorageLocation.Row @stack.StorageLocation.Position.GetShortName() P@(stack.StorageLocation.SlotNumber) } else { - }
Code
@(stack.StorageLocation?.Code ?? "-")

Reifen & Lagerung

Anzahl
@stack.ItemCount Reifen
Größe
@(stack.TireSize ?? "-")
Marke / Modell
@(string.Join(" ", new[] { stack.TireBrand, stack.TireModel }.Where(s => !string.IsNullOrWhiteSpace(s))) is { Length: > 0 } bm ? bm : "-")
Saison
@(stack.Season == TireSeason.Sommer ? "☀️ Sommer" : stack.Season == TireSeason.Winter ? "❄️ Winter" : "🔄 Allrad")
Felgen
@(stack.WithRims ? "Mit Felgen" : "Ohne Felgen")
Zustand
@stack.ConditionText
Eingelagert
(@stack.DaysStored Tage)
@if (stack.ExpectedPickupDate.HasValue) {
Abholung erwartet
}
}; private List allTireStacks = new(); private List filteredTireStacks = new(); private List expiringPickups = new(); private List categories = new(); private StorageStatistics stats = new(); private string searchTerm = ""; private TireSeason? seasonFilter; private bool? typeFilter; // null = all, false = customer, true = inventory private string selectedCategoryId = ""; private TireStack? selectedStack; private TireStack? editStack; private TireStack? editSource; private bool showEditModal; private bool isSaving; private string? editError; private bool isLoading = true; private bool showGridView; private List allLocations = new(); private Dictionary<(AreaType, int, ShelfPosition, int), StorageLocation> locationLookup = new(); private StorageLocation? selectedLocation; protected override async Task OnInitializedAsync() { if (UserSession.IsWorkerOrAdmin) { await LoadData(); } } private DotNetObjectReference? _visibilityRef; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && UserSession.IsWorkerOrAdmin) { _visibilityRef = DotNetObjectReference.Create(this); await VisibilityRefresh.RegisterAsync(JS, _visibilityRef, nameof(OnVisibilityReturn), 120); } } [JSInvokable] public async Task OnVisibilityReturn(int hiddenSeconds) { if (!UserSession.IsWorkerOrAdmin) return; await LoadData(); await InvokeAsync(StateHasChanged); } public async ValueTask DisposeAsync() { await VisibilityRefresh.UnregisterAsync(JS); _visibilityRef?.Dispose(); } private async Task LoadData() { isLoading = true; StateHasChanged(); try { allTireStacks = await TireStackService.GetAllAsync(TireStackStatus.Stored); stats = await TireStackService.GetStatisticsAsync(); expiringPickups = await TireStackService.GetExpiringPickupsAsync(); categories = await CatalogService.GetAllAsync(); allLocations = await StorageLocationService.GetAllAsync(); locationLookup = allLocations.ToDictionary(l => (l.Area, l.Row, l.Position, l.SlotNumber)); if (selectedLocation != null) { selectedLocation = allLocations.FirstOrDefault(l => l.Id == selectedLocation.Id); } ApplyFilters(); } finally { isLoading = false; } } private void FilterTireStacks() { ApplyFilters(); } private void SetSeasonFilter(TireSeason? season) { seasonFilter = season; ApplyFilters(); } private void SetTypeFilter(bool? isInventory) { typeFilter = isInventory; selectedCategoryId = ""; // Reset category filter when changing type ApplyFilters(); } private bool HasActiveFilters => typeFilter != null || seasonFilter != null || !string.IsNullOrEmpty(selectedCategoryId) || !string.IsNullOrWhiteSpace(searchTerm); private void ResetFilters() { typeFilter = null; seasonFilter = null; selectedCategoryId = ""; searchTerm = ""; ApplyFilters(); } private void ApplyFilters() { var query = allTireStacks.AsEnumerable(); // Type filter (customer vs inventory) if (typeFilter.HasValue) { if (typeFilter.Value) query = query.Where(ts => ts.CustomerId == null); else query = query.Where(ts => ts.CustomerId != null); } // Season filter if (seasonFilter.HasValue) { query = query.Where(ts => ts.Season == seasonFilter.Value); } // Category filter (for inventory) if (!string.IsNullOrEmpty(selectedCategoryId) && Guid.TryParse(selectedCategoryId, out var catId)) { query = query.Where(ts => ts.CatalogItemId == catId); } // Search term if (!string.IsNullOrWhiteSpace(searchTerm)) { var term = searchTerm.ToLower(); query = query.Where(ts => (ts.Customer?.FullName?.ToLower().Contains(term) ?? false) || (ts.Vehicle?.Make?.ToLower().Contains(term) ?? false) || (ts.Vehicle?.Model?.ToLower().Contains(term) ?? false) || (ts.Customer?.CustomerNumber?.ToLower().Contains(term) ?? false) || (ts.CatalogItem?.Name?.ToLower().Contains(term) ?? false) || (ts.StorageLocation?.Code?.ToLower().Contains(term) ?? false) || ts.TicketNumber.ToLower().Contains(term) || (ts.TireBrand?.ToLower().Contains(term) ?? false) || (ts.TireSize?.ToLower().Contains(term) ?? false) ); } filteredTireStacks = query.OrderBy(ts => ts.StorageLocation?.Code).ToList(); } private void SelectStack(TireStack ts) { selectedStack = selectedStack?.Id == ts.Id ? null : ts; } private void OpenEditModal() { if (selectedStack == null) return; OpenEditModalFor(selectedStack); } private void OpenEditModalFor(TireStack source) { editSource = source; editStack = new TireStack { Id = source.Id, TireSize = source.TireSize, TireBrand = source.TireBrand, TireModel = source.TireModel, ItemCount = source.ItemCount, Season = source.Season, Condition = source.Condition, WithRims = source.WithRims, Notes = source.Notes, TicketNumber = source.TicketNumber }; editError = null; showEditModal = true; } private void CloseEditModal() { if (isSaving) return; showEditModal = false; editStack = null; editSource = null; editError = null; } private async Task SaveEdit() { if (editStack == null || editSource == null) return; isSaving = true; editError = null; try { editSource.TireSize = editStack.TireSize; editSource.TireBrand = editStack.TireBrand; editSource.TireModel = editStack.TireModel; editSource.ItemCount = editStack.ItemCount; editSource.Season = editStack.Season; editSource.Condition = editStack.Condition; editSource.WithRims = editStack.WithRims; editSource.Notes = editStack.Notes; await TireStackService.UpdateAsync(editSource); showEditModal = false; editStack = null; editSource = null; var selectedId = selectedStack?.Id; await LoadData(); if (selectedId.HasValue) { selectedStack = allTireStacks.FirstOrDefault(ts => ts.Id == selectedId.Value); } } catch (Exception ex) { editError = $"Fehler beim Speichern: {ex.Message}"; } finally { isSaving = false; } } private StorageLocation? GetLocation(AreaType area, int row, ShelfPosition pos, int slot) { return locationLookup.TryGetValue((area, row, pos, slot), out var loc) ? loc : null; } private void SelectLocation(StorageLocation? location) { if (location == null) return; selectedLocation = selectedLocation?.Id == location.Id ? null : location; } private string GetCellTooltip(StorageLocation? loc, AreaType area, int row, ShelfPosition pos, int slot) { var baseText = $"{area.GetDisplayName()}, Reihe {row}, {pos.GetDisplayName()}, Platz {slot}"; if (loc == null) return $"{baseText} – nicht angelegt"; if (!loc.IsAvailable) return $"{baseText} – gesperrt"; if (loc.IsOccupied) { var stack = loc.OccupiedTireStack; var who = stack == null ? "belegt" : stack.IsInventory ? stack.CatalogItem?.Name ?? "Lagerbestand" : stack.Customer?.FullName ?? "belegt"; return $"{baseText} – {who}"; } return $"{baseText} – frei"; } }