@page "/manual-imports" @using Microsoft.AspNetCore.Components.Forms @using Microsoft.EntityFrameworkCore @using TrafagSalesExporter.Data @using TrafagSalesExporter.Models @using TrafagSalesExporter.Services @inject IDbContextFactory DbFactory @inject IStandortePageService StandortePageService @inject ISnackbar Snackbar @inject IUiTextService UiText @T("Manuelle Importe", "Manual imports") @T("Manuelle Importe", "Manual imports") @T("Diese Seite ist fuer Keyuser: Hier werden Excel-/CSV-Dateien fuer manuelle Laender wie DE, UK und ES hinterlegt und aktiviert. Technische Spaltenmappings bleiben in Admin -> Standorte.", "This page is for key users: Excel/CSV files for manual countries such as DE, UK and ES are maintained and activated here. Technical column mappings remain in Admin -> Sites.") @T("Land", "Country") TSC @T("Aktiv", "Active") @T("Datei / SharePoint-Ordner", "File / SharePoint folder") @T("Letzter Upload", "Last upload") @T("Aktionen", "Actions") @context.Land @context.TSC @(context.ManualImportLastUploadedAtUtc?.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") ?? "-") @T("Pfad pruefen", "Check path") @T("Speichern", "Save") @T("Keine manuellen Excel-/CSV-Standorte gefunden.", "No manual Excel/CSV sites found.")
1

@T("Excel bereitstellen", "Provide Excel")

@T("Datei hochladen oder SharePoint-/UNC-Pfad eintragen.", "Upload a file or enter a SharePoint/UNC path.")

2

@T("Speichern und aktivieren", "Save and activate")

@T("Pfad pruefen, Standort aktiv setzen und speichern.", "Check the path, set the site active, and save.")

3

@T("Standort exportieren", "Export site")

@T("Im Export Dashboard den Standort starten. Die Daten landen in CentralSalesRecords.", "Start the site in the export dashboard. Data is written to CentralSalesRecords.")

4

@T("Zentrale Excel erzeugen", "Build final Excel")

@T("Danach `Zentrale Datei neu erzeugen` ausfuehren.", "Then run `Rebuild consolidated file`.")

5

@T("Finance pruefen", "Check finance")

@T("Im Endexcel `Finance | ...` oder im Reiter `Soll/Ist Vergleich` kontrollieren.", "Check the `Finance | ...` columns in the final Excel or the `Actual/reference comparison` tab.")

@T("Richtige Reihenfolge", "Correct order")

@T("Ein Standortexport aktualisiert die Datenbasis. Die zentrale Excel muss danach neu erzeugt werden.", "A site export updates the data basis. The final Excel must be rebuilt afterwards.")

@T("DE bleibt fachlich offen", "DE remains open")

@T("Alphaplan ist technisch importierbar. Kundenlaender und Filter fuer den offiziellen DE-Istwert muessen noch bestaetigt werden.", "Alphaplan is technically importable. Customer countries and filters for the official DE actual still need confirmation.")

@T("Server-Hinweis", "Server note")

@T("Der Server braucht kein Microsoft Excel. XLSX/CSV wird direkt von der Anwendung gelesen.", "The server does not need Microsoft Excel. XLSX/CSV is read directly by the application.")

@code { private List _rows = []; private bool _loading = true; private int? _busySiteId; protected override async Task OnInitializedAsync() { await LoadAsync(); } private async Task LoadAsync() { _loading = true; await using var db = await DbFactory.CreateDbContextAsync(); var manualSourceCodes = await db.SourceSystemDefinitions .Where(x => x.ConnectionKind == SourceSystemConnectionKinds.ManualExcel) .Select(x => x.Code) .ToListAsync(); _rows = await db.Sites .Where(site => manualSourceCodes.Contains(site.SourceSystem)) .OrderBy(site => site.Land) .ThenBy(site => site.TSC) .Select(site => new ManualImportRow { Id = site.Id, Land = site.Land, TSC = site.TSC, IsActive = site.IsActive, ManualImportFilePath = site.ManualImportFilePath, ManualImportLastUploadedAtUtc = site.ManualImportLastUploadedAtUtc }) .ToListAsync(); _loading = false; } private async Task SaveAsync(ManualImportRow row) { _busySiteId = row.Id; try { await using var db = await DbFactory.CreateDbContextAsync(); var site = await db.Sites.FirstAsync(x => x.Id == row.Id); site.IsActive = row.IsActive; site.ManualImportFilePath = row.ManualImportFilePath.Trim(); site.ManualImportLastUploadedAtUtc = row.ManualImportLastUploadedAtUtc; await db.SaveChangesAsync(); Snackbar.Add(T("Import-Einstellungen gespeichert.", "Import settings saved."), Severity.Success); } catch (Exception ex) { Snackbar.Add($"{T("Speichern fehlgeschlagen", "Save failed")}: {ex.Message}", Severity.Error); } finally { _busySiteId = null; } } private async Task ValidatePathAsync(ManualImportRow row) { _busySiteId = row.Id; try { row.ManualImportLastUploadedAtUtc = await StandortePageService.ValidateManualImportPathAsync(row.ManualImportFilePath); Snackbar.Add(T("Datei oder SharePoint-Referenz ist erreichbar.", "File or SharePoint reference is reachable."), Severity.Success); } catch (Exception ex) { Snackbar.Add($"{T("Pfadpruefung fehlgeschlagen", "Path check failed")}: {ex.Message}", Severity.Error); } finally { _busySiteId = null; } } private async Task UploadAsync(ManualImportRow row, InputFileChangeEventArgs args) { var file = args.File; if (file is null) return; _busySiteId = row.Id; try { var extension = Path.GetExtension(file.Name); if (!string.Equals(extension, ".xlsx", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".csv", StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException(T("Bitte eine .xlsx- oder .csv-Datei auswaehlen.", "Please choose a .xlsx or .csv file.")); } var uploadDirectory = Path.Combine(AppContext.BaseDirectory, "manual-imports"); Directory.CreateDirectory(uploadDirectory); var safeBaseName = string.Concat(Path.GetFileNameWithoutExtension(file.Name) .Select(ch => char.IsLetterOrDigit(ch) || ch == '-' || ch == '_' ? ch : '_')); if (string.IsNullOrWhiteSpace(safeBaseName)) safeBaseName = "manual_import"; var targetPath = Path.Combine(uploadDirectory, $"{safeBaseName}_{Guid.NewGuid():N}{extension}"); await using (var sourceStream = file.OpenReadStream(maxAllowedSize: 50 * 1024 * 1024)) await using (var targetStream = File.Create(targetPath)) { await sourceStream.CopyToAsync(targetStream); } row.ManualImportFilePath = targetPath; row.ManualImportLastUploadedAtUtc = DateTime.UtcNow; await SaveAsync(row); Snackbar.Add(T("Datei hochgeladen.", "File uploaded."), Severity.Success); } catch (Exception ex) { Snackbar.Add($"{T("Upload fehlgeschlagen", "Upload failed")}: {ex.Message}", Severity.Error); } finally { _busySiteId = null; } } private string T(string german, string english) => UiText.Text(german, english); private sealed class ManualImportRow { public int Id { get; set; } public string Land { get; set; } = string.Empty; public string TSC { get; set; } = string.Empty; public bool IsActive { get; set; } public string ManualImportFilePath { get; set; } = string.Empty; public DateTime? ManualImportLastUploadedAtUtc { get; set; } } }