diff --git a/Lagerbestand/Components/Pages/Customers.razor b/Lagerbestand/Components/Pages/Customers.razor index 834ac42..477e3b0 100644 --- a/Lagerbestand/Components/Pages/Customers.razor +++ b/Lagerbestand/Components/Pages/Customers.razor @@ -175,6 +175,8 @@ else @ts.TireSize (@TireStack.TiresPerStack Reifen) @(ts.StorageLocation?.Code ?? "-") + } diff --git a/Lagerbestand/Components/Pages/Customers.razor.css b/Lagerbestand/Components/Pages/Customers.razor.css index 13d8900..f487096 100644 --- a/Lagerbestand/Components/Pages/Customers.razor.css +++ b/Lagerbestand/Components/Pages/Customers.razor.css @@ -512,6 +512,7 @@ .mini-tireset { display: flex; align-items: center; + flex-wrap: wrap; gap: 8px; padding: 8px 10px; background: rgba(0, 0, 0, 0.2); @@ -521,6 +522,11 @@ font-size: 0.85rem; } +.mini-tireset ::deep .insp-panel { + flex-basis: 100%; + margin-top: 4px; +} + @media (min-width: 480px) { .mini-tireset { gap: 10px; diff --git a/Lagerbestand/Components/Pages/Dashboard.razor b/Lagerbestand/Components/Pages/Dashboard.razor index 77901c5..7812916 100644 --- a/Lagerbestand/Components/Pages/Dashboard.razor +++ b/Lagerbestand/Components/Pages/Dashboard.razor @@ -590,6 +590,8 @@ else
@(ts.TireStack.Vehicle?.ShortName ?? "Ohne KFZ") @(ts.TireStack.TireSize ?? "?") | @(ts.TireStack.StorageLocation?.Code ?? "-") +
} diff --git a/Lagerbestand/Components/Pages/TireCheckin.razor b/Lagerbestand/Components/Pages/TireCheckin.razor index abcbd68..c8e335b 100644 --- a/Lagerbestand/Components/Pages/TireCheckin.razor +++ b/Lagerbestand/Components/Pages/TireCheckin.razor @@ -949,6 +949,12 @@ else + @if (createdTireStack != null && !isInventoryMode) + { + + } +
- +
@if (ts.Customer != null) @@ -152,6 +152,8 @@ else Eingelagert: @ts.StoredAt.ToLocalTime().ToString("dd.MM.yyyy") (@ts.DaysStored Tage)
+
} @@ -205,6 +207,8 @@ else } + @* Incoming *@ diff --git a/Lagerbestand/Components/Pages/TireStorage.razor b/Lagerbestand/Components/Pages/TireStorage.razor index cd451cb..be2d06f 100644 --- a/Lagerbestand/Components/Pages/TireStorage.razor +++ b/Lagerbestand/Components/Pages/TireStorage.razor @@ -399,6 +399,8 @@ else + @if (!string.IsNullOrEmpty(editError)) { @@ -536,32 +538,11 @@ else
} - @if (latestInspections.TryGetValue(stack.Id, out var insp) && insp != null) - { -
-
Letzte Messung
-
-
- VL @FmtMm(insp.TreadDepthFL) - VR @FmtMm(insp.TreadDepthFR) - HL @FmtMm(insp.TreadDepthRL) - HR @FmtMm(insp.TreadDepthRR) -
- - @((insp.MeasuredAt ?? insp.CreatedAt).ToLocalTime().ToString("dd.MM.yyyy")) - @if (insp.Mileage.HasValue) { · @insp.Mileage.Value.ToString("N0", System.Globalization.CultureInfo.GetCultureInfo("de-DE")) km } - @if (!string.IsNullOrEmpty(insp.LicensePlate)) { · @insp.LicensePlate } - - @if (!string.IsNullOrEmpty(insp.ReportUrl)) - { - Report öffnen ↗ - } -
-
- } + }; private List allTireStacks = new(); @@ -575,30 +556,6 @@ else private bool? typeFilter; // null = all, false = customer, true = inventory private string selectedCategoryId = ""; private TireStack? selectedStack; - private readonly Dictionary latestInspections = new(); - - private static string RatingClass(TireMeasureRating r) => r switch - { - TireMeasureRating.Ok => "ok", - TireMeasureRating.Warning => "warn", - TireMeasureRating.Critical => "crit", - _ => "none" - }; - - private static string FmtMm(decimal? mm) => mm.HasValue ? mm.Value.ToString("0.0", System.Globalization.CultureInfo.GetCultureInfo("de-DE")) + " mm" : "–"; - - private async Task LoadLatestInspection(TireStack ts) - { - if (latestInspections.ContainsKey(ts.Id)) return; - try - { - latestInspections[ts.Id] = await TireStackService.GetLatestInspectionAsync(ts.Id); - } - catch - { - latestInspections[ts.Id] = null; - } - } private TireStack? editStack; private TireStack? editSource; private bool showEditModal; @@ -744,13 +701,9 @@ else filteredTireStacks = query.OrderBy(ts => ts.StorageLocation?.Code).ToList(); } - private async Task SelectStack(TireStack ts) + private void SelectStack(TireStack ts) { selectedStack = selectedStack?.Id == ts.Id ? null : ts; - if (selectedStack != null) - { - await LoadLatestInspection(selectedStack); - } } private void OpenEditModal() @@ -831,14 +784,10 @@ else return locationLookup.TryGetValue((area, row, pos, slot), out var loc) ? loc : null; } - private async Task SelectLocation(StorageLocation? location) + private void SelectLocation(StorageLocation? location) { if (location == null) return; selectedLocation = selectedLocation?.Id == location.Id ? null : location; - if (selectedLocation?.OccupiedTireStack != null) - { - await LoadLatestInspection(selectedLocation.OccupiedTireStack); - } } private string GetCellTooltip(StorageLocation? loc, AreaType area, int row, ShelfPosition pos, int slot) diff --git a/Lagerbestand/Components/Shared/TireInspectionPanel.razor b/Lagerbestand/Components/Shared/TireInspectionPanel.razor new file mode 100644 index 0000000..52457c6 --- /dev/null +++ b/Lagerbestand/Components/Shared/TireInspectionPanel.razor @@ -0,0 +1,397 @@ +@inject ITireStackService TireStackService +@inject ITireReportService TireReportService +@inject UserSessionService UserSession + +
+ + + @if (isOpen) + { +
+ @if (isLoading) + { +
Messungen werden geladen…
+ } + else + { + @if (!string.IsNullOrEmpty(error)) + { + + } + + @if (AllowEdit) + { +
+ + +
+ } + + @if (editing != null) + { +
+

@(isNew ? "Neue Messung" : "Messung bearbeiten")

+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
mm
+
+
+ +
mm
+
+
+ +
mm
+
+
+ +
mm
+
+
+
+ + +
+ @if (!string.IsNullOrEmpty(editing.ReportUrl)) + { + Original-Report öffnen ↗ + } + +
+ } + + @if (inspections.Count == 0) + { + @if (editing == null) + { +

Noch keine Profiltiefen-Messung fĂĽr diesen Satz vorhanden.

+ } + } + else + { +
    + @foreach (var insp in inspections) + { +
  • +
    + + @((insp.MeasuredAt ?? insp.CreatedAt).ToLocalTime().ToString("dd.MM.yyyy HH:mm")) + @if (insp == latest) { aktuell } + + @insp.Source@(string.IsNullOrEmpty(insp.RecordSn) ? "" : $" · {insp.RecordSn}") + @if (AllowEdit) + { + + + @if (confirmDeleteId == insp.Id) + { + + + } + else + { + + } + + } +
    +
    + VL @Fmt(insp.TreadDepthFL) + VR @Fmt(insp.TreadDepthFR) + HL @Fmt(insp.TreadDepthRL) + HR @Fmt(insp.TreadDepthRR) +
    +
    + @if (!string.IsNullOrEmpty(insp.LicensePlate)) { 🚗 @insp.LicensePlate } + @if (insp.Mileage.HasValue) { @insp.Mileage.Value.ToString("N0", De) km } + @if (!string.IsNullOrEmpty(insp.ReportUrl)) { Report ↗ } +
    + @if (!string.IsNullOrEmpty(insp.Recommendation)) + { +
    đź’ˇ @insp.Recommendation
    + } +
  • + } +
+ } + } +
+ } +
+ +@if (AllowEdit) +{ + +} + +@code { + [Parameter, EditorRequired] public Guid TireStackId { get; set; } + [Parameter] public bool AllowEdit { get; set; } + [Parameter] public bool Compact { get; set; } + [Parameter] public bool InitiallyOpen { get; set; } + [Parameter] public string? ScannerSubtitle { get; set; } + [Parameter] public EventCallback OnLatestChanged { get; set; } + + private static readonly System.Globalization.CultureInfo De = System.Globalization.CultureInfo.GetCultureInfo("de-DE"); + private readonly string panelId = $"insp-{Guid.NewGuid():N}"; + + private Guid loadedFor; + private bool isOpen; + private bool loaded; + private bool isLoading; + private bool isBusy; + private bool isFetching; + private bool showScanner; + private string? error; + private List inspections = new(); + private TireInspection? latest; + private TireInspection? editing; + private bool isNew; + private Guid? confirmDeleteId; + + protected override async Task OnParametersSetAsync() + { + if (loadedFor != TireStackId) + { + loadedFor = TireStackId; + inspections = new(); + latest = null; + loaded = false; + editing = null; + confirmDeleteId = null; + error = null; + isOpen = InitiallyOpen; + await LoadAsync(); + } + } + + private async Task LoadAsync() + { + isLoading = true; + try + { + inspections = await TireStackService.GetInspectionsAsync(TireStackId); + latest = inspections.FirstOrDefault(); + loaded = true; + } + catch (Exception ex) + { + error = $"Messungen konnten nicht geladen werden: {ex.Message}"; + } + finally + { + isLoading = false; + } + } + + private void Toggle() => isOpen = !isOpen; + + private static string RatingClass(TireMeasureRating r) => r switch + { + TireMeasureRating.Ok => "ok", + TireMeasureRating.Warning => "warn", + TireMeasureRating.Critical => "crit", + _ => "none" + }; + + private static string Fmt(decimal? mm) => mm.HasValue ? mm.Value.ToString("0.0", De) + " mm" : "–"; + + private void OpenScanner() + { + error = null; + showScanner = true; + } + + private async Task OnQrScanned(string text) + { + showScanner = false; + + if (!TireReportService.TryParseReportUrl(text, out var recordSn, out _)) + { + error = "Der QR-Code enthält keinen gültigen Reifen-Report-Link."; + return; + } + + isFetching = true; + error = null; + StateHasChanged(); + + try + { + var report = await TireReportService.FetchAsync(recordSn); + report.RecomputeRatings(); + editing = report.ToInspection(TireStackId, UserSession.CurrentUser?.Id); + isNew = true; + } + catch (TireReportException ex) + { + error = ex.Message; + } + catch (Exception ex) + { + error = $"Report konnte nicht verarbeitet werden: {ex.Message}"; + } + finally + { + isFetching = false; + } + } + + private void StartManual() + { + error = null; + confirmDeleteId = null; + isNew = true; + editing = new TireInspection + { + TireStackId = TireStackId, + Source = "Manuell", + MeasuredAt = DateTime.UtcNow, + CreatedByUserId = UserSession.CurrentUser?.Id + }; + } + + private void StartEdit(TireInspection insp) + { + error = null; + confirmDeleteId = null; + isNew = false; + editing = new TireInspection + { + Id = insp.Id, + TireStackId = insp.TireStackId, + Source = insp.Source, + RecordSn = insp.RecordSn, + ReportUrl = insp.ReportUrl, + MeasuredAt = insp.MeasuredAt, + LicensePlate = insp.LicensePlate, + Mileage = insp.Mileage, + TreadDepthFL = insp.TreadDepthFL, + TreadDepthFR = insp.TreadDepthFR, + TreadDepthRL = insp.TreadDepthRL, + TreadDepthRR = insp.TreadDepthRR, + Recommendation = insp.Recommendation, + RawJson = insp.RawJson, + CreatedAt = insp.CreatedAt, + CreatedByUserId = insp.CreatedByUserId + }; + } + + private void OnDateChanged(ChangeEventArgs e) + { + if (editing == null) return; + if (DateTime.TryParse(e.Value?.ToString(), out var d)) + editing.MeasuredAt = DateTime.SpecifyKind(d, DateTimeKind.Local).ToUniversalTime(); + else + editing.MeasuredAt = null; + } + + private void CancelEdit() + { + editing = null; + error = null; + } + + private async Task SaveEdit() + { + if (editing == null) return; + + isBusy = true; + error = null; + StateHasChanged(); + + try + { + editing.RatingFL = TireInspection.RateDepth(editing.TreadDepthFL); + editing.RatingFR = TireInspection.RateDepth(editing.TreadDepthFR); + editing.RatingRL = TireInspection.RateDepth(editing.TreadDepthRL); + editing.RatingRR = TireInspection.RateDepth(editing.TreadDepthRR); + editing.LicensePlate = string.IsNullOrWhiteSpace(editing.LicensePlate) ? null : editing.LicensePlate.Trim(); + editing.Recommendation = string.IsNullOrWhiteSpace(editing.Recommendation) ? null : editing.Recommendation.Trim(); + + if (isNew) + await TireStackService.AddInspectionAsync(editing); + else + await TireStackService.UpdateInspectionAsync(editing); + + editing = null; + await LoadAsync(); + await OnLatestChanged.InvokeAsync(latest); + } + catch (Exception ex) + { + error = $"Speichern fehlgeschlagen: {ex.Message}"; + } + finally + { + isBusy = false; + } + } + + private async Task Delete(TireInspection insp) + { + isBusy = true; + error = null; + try + { + await TireStackService.DeleteInspectionAsync(insp.Id); + confirmDeleteId = null; + await LoadAsync(); + await OnLatestChanged.InvokeAsync(latest); + } + catch (Exception ex) + { + error = $"Löschen fehlgeschlagen: {ex.Message}"; + } + finally + { + isBusy = false; + } + } +} diff --git a/Lagerbestand/Components/Shared/TireInspectionPanel.razor.css b/Lagerbestand/Components/Shared/TireInspectionPanel.razor.css new file mode 100644 index 0000000..b1b3d3a --- /dev/null +++ b/Lagerbestand/Components/Shared/TireInspectionPanel.razor.css @@ -0,0 +1,364 @@ +.insp-panel { + border: 0.5px solid rgba(255, 255, 255, 0.12); + border-radius: 12px; + background: rgba(255, 255, 255, 0.03); + margin-top: 0.75rem; + overflow: hidden; +} + +.insp-toggle { + width: 100%; + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; + padding: 10px 14px; + background: none; + border: none; + color: #ffffff; + cursor: pointer; + text-align: left; + font-family: 'Rajdhani', sans-serif; +} + +.insp-toggle:hover { + background: rgba(255, 255, 255, 0.04); +} + +.insp-toggle-icon { + font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji", sans-serif; + font-size: 1.1rem; + line-height: 1; +} + +.insp-toggle-title { + font-weight: 700; + font-size: 0.95rem; +} + +.insp-toggle-summary { + display: flex; + gap: 4px; + flex-wrap: wrap; + margin-left: auto; +} + +.insp-toggle-summary.muted { + font-size: 0.85rem; +} + +.insp-chevron { + transition: transform 0.15s; + color: rgba(255, 255, 255, 0.6); +} + +.insp-chevron.open { + transform: rotate(180deg); +} + +.insp-body { + padding: 0 14px 14px; + display: flex; + flex-direction: column; + gap: 10px; + border-top: 0.5px solid rgba(255, 255, 255, 0.08); + padding-top: 10px; +} + +.insp-panel.compact .insp-toggle { + padding: 8px 10px; +} + +.insp-panel.compact .insp-body { + padding: 8px 10px 10px; +} + +.insp-status, +.insp-empty { + font-family: 'Rajdhani', sans-serif; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.6); + margin: 0; +} + +.insp-error { + background: rgba(239, 68, 68, 0.12); + border: 0.5px solid rgba(239, 68, 68, 0.4); + color: #fca5a5; + border-radius: 10px; + padding: 8px 12px; + font-family: 'Rajdhani', sans-serif; + font-size: 0.9rem; +} + +.insp-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.insp-actions .btn { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.muted { + color: rgba(255, 255, 255, 0.55); +} + +.insp-chip { + font-family: 'Rajdhani', sans-serif; + font-size: 0.8rem; + font-weight: 600; + padding: 2px 8px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.08); + color: rgba(255, 255, 255, 0.85); + border: 0.5px solid rgba(255, 255, 255, 0.12); + white-space: nowrap; +} + +.insp-chip.rating-ok { background: rgba(34, 197, 94, 0.15); border-color: rgba(34, 197, 94, 0.4); color: #86efac; } +.insp-chip.rating-warn { background: rgba(250, 204, 21, 0.15); border-color: rgba(250, 204, 21, 0.4); color: #fde047; } +.insp-chip.rating-crit { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.4); color: #fca5a5; } + +.insp-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.insp-item { + border-radius: 10px; + padding: 10px 12px; + background: rgba(255, 255, 255, 0.04); + border: 0.5px solid rgba(255, 255, 255, 0.1); + display: flex; + flex-direction: column; + gap: 6px; +} + +.insp-item.latest { + border-color: rgba(96, 165, 250, 0.4); +} + +.insp-item-head { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + font-family: 'Rajdhani', sans-serif; + font-size: 0.85rem; +} + +.insp-date { + font-weight: 700; + color: #ffffff; + display: inline-flex; + align-items: center; + gap: 6px; +} + +.insp-badge { + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 1px 6px; + border-radius: 999px; + background: rgba(96, 165, 250, 0.2); + color: #93c5fd; +} + +.insp-item-actions { + margin-left: auto; + display: flex; + gap: 10px; +} + +.btn-link { + background: none; + border: none; + padding: 0; + color: #60a5fa; + font-family: 'Rajdhani', sans-serif; + font-size: 0.85rem; + cursor: pointer; +} + +.btn-link.danger { + color: #fca5a5; +} + +.btn-link:hover:not(:disabled) { + text-decoration: underline; +} + +.btn-link:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.insp-chips { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.insp-details { + display: flex; + gap: 10px; + flex-wrap: wrap; + font-family: 'Rajdhani', sans-serif; + font-size: 0.85rem; +} + +.insp-link { + font-family: 'Rajdhani', sans-serif; + font-size: 0.85rem; + color: #60a5fa; +} + +.insp-recommendation { + font-family: 'Rajdhani', sans-serif; + font-size: 0.9rem; + color: rgba(255, 255, 255, 0.85); + background: rgba(96, 165, 250, 0.1); + border: 0.5px solid rgba(96, 165, 250, 0.25); + border-radius: 8px; + padding: 6px 10px; +} + +/* Edit form */ +.insp-edit { + border: 0.5px solid rgba(96, 165, 250, 0.35); + border-radius: 10px; + padding: 12px; + display: flex; + flex-direction: column; + gap: 10px; + background: rgba(96, 165, 250, 0.05); +} + +.insp-edit h4 { + margin: 0; + font-family: 'Rajdhani', sans-serif; + font-size: 1rem; + font-weight: 700; + color: #ffffff; +} + +.insp-meta { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 3px; + margin: 0; +} + +.form-group label, +.tread-cell label { + font-family: 'Rajdhani', sans-serif; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.06em; + color: rgba(255, 255, 255, 0.55); +} + +.form-control { + background: rgba(255, 255, 255, 0.06); + border: 0.5px solid rgba(255, 255, 255, 0.15); + border-radius: 8px; + color: #ffffff; + padding: 6px 10px; + font-family: 'Rajdhani', sans-serif; + font-size: 0.95rem; +} + +.form-control:focus { + outline: none; + border-color: #60a5fa; +} + +.tread-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; +} + +.tread-cell { + border-radius: 10px; + padding: 8px 10px; + background: rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.tread-cell.rating-ok { border-color: rgba(34, 197, 94, 0.5); background: rgba(34, 197, 94, 0.08); } +.tread-cell.rating-warn { border-color: rgba(250, 204, 21, 0.5); background: rgba(250, 204, 21, 0.08); } +.tread-cell.rating-crit { border-color: rgba(239, 68, 68, 0.5); background: rgba(239, 68, 68, 0.08); } + +.tread-cell label { + display: block; + margin-bottom: 4px; +} + +.tread-input { + display: flex; + align-items: center; + gap: 6px; +} + +.tread-input .form-control { + max-width: 100px; + font-family: 'Orbitron', monospace; + font-weight: 600; +} + +.tread-input span { + font-family: 'Rajdhani', sans-serif; + color: rgba(255, 255, 255, 0.6); +} + +.insp-edit-footer { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.insp-edit-footer .btn { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.spinner { + display: inline-block; + width: 14px; + height: 14px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-top-color: #ffffff; + border-radius: 50%; + animation: insp-spin 0.7s linear infinite; +} + +@keyframes insp-spin { + to { transform: rotate(360deg); } +} + +@media (max-width: 599px) { + .insp-meta { + grid-template-columns: 1fr; + } + .insp-toggle-summary { + margin-left: 0; + width: 100%; + } +} diff --git a/Lagerbestand/Services/TireStackService.cs b/Lagerbestand/Services/TireStackService.cs index 5cfcd4f..05c6daf 100644 --- a/Lagerbestand/Services/TireStackService.cs +++ b/Lagerbestand/Services/TireStackService.cs @@ -31,6 +31,8 @@ public interface ITireStackService Task AddInspectionAsync(TireInspection inspection); Task> GetInspectionsAsync(Guid tireStackId); Task GetLatestInspectionAsync(Guid tireStackId); + Task UpdateInspectionAsync(TireInspection inspection); + Task DeleteInspectionAsync(Guid inspectionId); } /// @@ -636,6 +638,38 @@ public class TireStackService : ITireStackService return inspection; } + public async Task UpdateInspectionAsync(TireInspection inspection) + { + var existing = await _context.TireInspections.FirstOrDefaultAsync(i => i.Id == inspection.Id) + ?? throw new InvalidOperationException("Messung nicht gefunden."); + + existing.MeasuredAt = inspection.MeasuredAt; + existing.LicensePlate = inspection.LicensePlate; + existing.Mileage = inspection.Mileage; + existing.TreadDepthFL = inspection.TreadDepthFL; + existing.TreadDepthFR = inspection.TreadDepthFR; + existing.TreadDepthRL = inspection.TreadDepthRL; + existing.TreadDepthRR = inspection.TreadDepthRR; + existing.RatingFL = TireInspection.RateDepth(inspection.TreadDepthFL); + existing.RatingFR = TireInspection.RateDepth(inspection.TreadDepthFR); + existing.RatingRL = TireInspection.RateDepth(inspection.TreadDepthRL); + existing.RatingRR = TireInspection.RateDepth(inspection.TreadDepthRR); + existing.Recommendation = inspection.Recommendation; + + await _context.SaveChangesAsync(); + return existing; + } + + public async Task DeleteInspectionAsync(Guid inspectionId) + { + var existing = await _context.TireInspections.FirstOrDefaultAsync(i => i.Id == inspectionId); + if (existing == null) return false; + + _context.TireInspections.Remove(existing); + await _context.SaveChangesAsync(); + return true; + } + public async Task> GetInspectionsAsync(Guid tireStackId) { return await _context.TireInspections