diff --git a/EtchBendLines b/EtchBendLines index da4d322..89d987f 160000 --- a/EtchBendLines +++ b/EtchBendLines @@ -1 +1 @@ -Subproject commit da4d3228b0eede73a665b0a814f7b8ac818bcc94 +Subproject commit 89d987f6c6923b458217017300789ea956114972 diff --git a/ExportDXF.sln b/ExportDXF.sln index e194613..76f48c0 100644 --- a/ExportDXF.sln +++ b/ExportDXF.sln @@ -9,12 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EtchBendLines", "EtchBendLi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "netDxf", "EtchBendLines\netDxf\netDxf\netDxf.csproj", "{785380E0-CEB9-4C34-82E5-60D0E33E848E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FabWorks.Core", "FabWorks.Core\FabWorks.Core.csproj", "{24547EE4-2EAA-4A6C-AD94-1117C038D8CD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FabWorks.Tests", "FabWorks.Tests\FabWorks.Tests.csproj", "{6DD89774-D86B-47E9-B982-2794BD95616A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FabWorks.Api", "FabWorks.Api\FabWorks.Api.csproj", "{9BD571FA-52D8-430D-8843-FEB6EABD421C}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -61,42 +55,6 @@ Global {785380E0-CEB9-4C34-82E5-60D0E33E848E}.Release|x64.Build.0 = Release|Any CPU {785380E0-CEB9-4C34-82E5-60D0E33E848E}.Release|x86.ActiveCfg = Release|Any CPU {785380E0-CEB9-4C34-82E5-60D0E33E848E}.Release|x86.Build.0 = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|x64.ActiveCfg = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|x64.Build.0 = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|x86.ActiveCfg = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Debug|x86.Build.0 = Debug|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|Any CPU.Build.0 = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|x64.ActiveCfg = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|x64.Build.0 = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|x86.ActiveCfg = Release|Any CPU - {24547EE4-2EAA-4A6C-AD94-1117C038D8CD}.Release|x86.Build.0 = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|x64.ActiveCfg = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|x64.Build.0 = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|x86.ActiveCfg = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Debug|x86.Build.0 = Debug|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|Any CPU.Build.0 = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|x64.ActiveCfg = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|x64.Build.0 = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|x86.ActiveCfg = Release|Any CPU - {6DD89774-D86B-47E9-B982-2794BD95616A}.Release|x86.Build.0 = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|x64.ActiveCfg = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|x64.Build.0 = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|x86.ActiveCfg = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Debug|x86.Build.0 = Debug|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|Any CPU.Build.0 = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|x64.ActiveCfg = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|x64.Build.0 = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|x86.ActiveCfg = Release|Any CPU - {9BD571FA-52D8-430D-8843-FEB6EABD421C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FabWorks.Api/Configuration/FileStorageOptions.cs b/FabWorks.Api/Configuration/FileStorageOptions.cs deleted file mode 100644 index 8204f99..0000000 --- a/FabWorks.Api/Configuration/FileStorageOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FabWorks.Api.Configuration -{ - public class FileStorageOptions - { - public const string SectionName = "FileStorage"; - - public string OutputFolder { get; set; } = @"C:\ExportDXF\Output"; - } -} diff --git a/FabWorks.Api/Controllers/BomItemsController.cs b/FabWorks.Api/Controllers/BomItemsController.cs deleted file mode 100644 index f6027d1..0000000 --- a/FabWorks.Api/Controllers/BomItemsController.cs +++ /dev/null @@ -1,259 +0,0 @@ -using FabWorks.Api.DTOs; -using FabWorks.Core.Data; -using FabWorks.Core.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace FabWorks.Api.Controllers -{ - [ApiController] - [Route("api/exports/{exportId}/bom-items")] - public class BomItemsController : ControllerBase - { - private readonly FabWorksDbContext _db; - - public BomItemsController(FabWorksDbContext db) => _db = db; - - [HttpGet("find")] - public async Task> FindExisting(int exportId, [FromQuery] string partName, [FromQuery] string configurationName) - { - var export = await _db.ExportRecords.FindAsync(exportId); - if (export == null) return NotFound(); - - var existing = await _db.BomItems - .Include(b => b.CutTemplate) - .Include(b => b.FormProgram) - .Include(b => b.ExportRecord) - .Where(b => b.ExportRecord.DrawingNumber == export.DrawingNumber - && b.PartName == (partName ?? "") - && b.ConfigurationName == (configurationName ?? "")) - .OrderByDescending(b => b.ID) - .FirstOrDefaultAsync(); - - if (existing == null) return NotFound(); - return MapToDto(existing); - } - - [HttpGet] - public async Task>> GetByExport(int exportId) - { - var items = await _db.BomItems - .Include(b => b.CutTemplate) - .Include(b => b.FormProgram) - .Where(b => b.ExportRecordId == exportId) - .OrderBy(b => b.SortOrder) - .ToListAsync(); - - return items.Select(MapToDto).ToList(); - } - - [HttpPost] - public async Task> Create(int exportId, BomItemDto dto) - { - var export = await _db.ExportRecords.FindAsync(exportId); - if (export == null) return NotFound("Export record not found"); - - // Look up the latest CutTemplate for this drawing+item across all previous exports - // to determine the revision number - var newContentHash = dto.CutTemplate?.ContentHash; - int revision = await ResolveRevisionAsync(export.DrawingNumber, dto.ItemNo, newContentHash); - - // Look for existing BomItem with same PartName + ConfigurationName within this export record - var existing = await _db.BomItems - .Include(b => b.CutTemplate) - .Include(b => b.FormProgram) - .Where(b => b.ExportRecordId == exportId - && b.PartName == (dto.PartName ?? "") - && b.ConfigurationName == (dto.ConfigurationName ?? "")) - .OrderByDescending(b => b.ID) - .FirstOrDefaultAsync(); - - if (existing != null) - { - // Update existing fields - existing.PartNo = dto.PartNo ?? ""; - existing.SortOrder = dto.SortOrder; - existing.Qty = dto.Qty; - existing.TotalQty = dto.TotalQty; - existing.Description = dto.Description ?? ""; - existing.Material = dto.Material ?? ""; - - if (dto.CutTemplate != null) - { - if (existing.CutTemplate != null) - { - existing.CutTemplate.DxfFilePath = dto.CutTemplate.DxfFilePath ?? ""; - existing.CutTemplate.ContentHash = dto.CutTemplate.ContentHash; - existing.CutTemplate.Revision = revision; - existing.CutTemplate.Thickness = dto.CutTemplate.Thickness; - existing.CutTemplate.KFactor = dto.CutTemplate.KFactor; - existing.CutTemplate.DefaultBendRadius = dto.CutTemplate.DefaultBendRadius; - } - else - { - existing.CutTemplate = new CutTemplate - { - DxfFilePath = dto.CutTemplate.DxfFilePath ?? "", - ContentHash = dto.CutTemplate.ContentHash, - Revision = revision, - Thickness = dto.CutTemplate.Thickness, - KFactor = dto.CutTemplate.KFactor, - DefaultBendRadius = dto.CutTemplate.DefaultBendRadius - }; - } - } - - if (dto.FormProgram != null) - { - if (existing.FormProgram != null) - { - existing.FormProgram.ProgramFilePath = dto.FormProgram.ProgramFilePath ?? ""; - existing.FormProgram.ContentHash = dto.FormProgram.ContentHash; - existing.FormProgram.ProgramName = dto.FormProgram.ProgramName ?? ""; - existing.FormProgram.Thickness = dto.FormProgram.Thickness; - existing.FormProgram.MaterialType = dto.FormProgram.MaterialType ?? ""; - existing.FormProgram.KFactor = dto.FormProgram.KFactor; - existing.FormProgram.BendCount = dto.FormProgram.BendCount; - existing.FormProgram.UpperToolNames = dto.FormProgram.UpperToolNames ?? ""; - existing.FormProgram.LowerToolNames = dto.FormProgram.LowerToolNames ?? ""; - existing.FormProgram.SetupNotes = dto.FormProgram.SetupNotes ?? ""; - } - else - { - existing.FormProgram = new FormProgram - { - ProgramFilePath = dto.FormProgram.ProgramFilePath ?? "", - ContentHash = dto.FormProgram.ContentHash, - ProgramName = dto.FormProgram.ProgramName ?? "", - Thickness = dto.FormProgram.Thickness, - MaterialType = dto.FormProgram.MaterialType ?? "", - KFactor = dto.FormProgram.KFactor, - BendCount = dto.FormProgram.BendCount, - UpperToolNames = dto.FormProgram.UpperToolNames ?? "", - LowerToolNames = dto.FormProgram.LowerToolNames ?? "", - SetupNotes = dto.FormProgram.SetupNotes ?? "" - }; - } - } - - await _db.SaveChangesAsync(); - return Ok(MapToDto(existing)); - } - - // No existing match — create new - var item = new BomItem - { - ExportRecordId = exportId, - ItemNo = dto.ItemNo ?? "", - PartNo = dto.PartNo ?? "", - SortOrder = dto.SortOrder, - Qty = dto.Qty, - TotalQty = dto.TotalQty, - Description = dto.Description ?? "", - PartName = dto.PartName ?? "", - ConfigurationName = dto.ConfigurationName ?? "", - Material = dto.Material ?? "" - }; - - if (dto.CutTemplate != null) - { - item.CutTemplate = new CutTemplate - { - DxfFilePath = dto.CutTemplate.DxfFilePath ?? "", - ContentHash = dto.CutTemplate.ContentHash, - Revision = revision, - Thickness = dto.CutTemplate.Thickness, - KFactor = dto.CutTemplate.KFactor, - DefaultBendRadius = dto.CutTemplate.DefaultBendRadius - }; - } - - if (dto.FormProgram != null) - { - item.FormProgram = new FormProgram - { - ProgramFilePath = dto.FormProgram.ProgramFilePath ?? "", - ContentHash = dto.FormProgram.ContentHash, - ProgramName = dto.FormProgram.ProgramName ?? "", - Thickness = dto.FormProgram.Thickness, - MaterialType = dto.FormProgram.MaterialType ?? "", - KFactor = dto.FormProgram.KFactor, - BendCount = dto.FormProgram.BendCount, - UpperToolNames = dto.FormProgram.UpperToolNames ?? "", - LowerToolNames = dto.FormProgram.LowerToolNames ?? "", - SetupNotes = dto.FormProgram.SetupNotes ?? "" - }; - } - - _db.BomItems.Add(item); - await _db.SaveChangesAsync(); - - return CreatedAtAction(nameof(GetByExport), new { exportId }, MapToDto(item)); - } - - /// - /// Determines the revision number for a CutTemplate by looking at the most recent - /// CutTemplate for the same drawing number and item number across all exports. - /// Returns 1 if no previous version exists, the same revision if the hash matches, - /// or previous revision + 1 if the hash changed. - /// - private async Task ResolveRevisionAsync(string drawingNumber, string itemNo, string contentHash) - { - if (string.IsNullOrEmpty(drawingNumber) || string.IsNullOrEmpty(itemNo) || string.IsNullOrEmpty(contentHash)) - return 1; - - var previous = await _db.CutTemplates - .Where(c => c.BomItem.ExportRecord.DrawingNumber == drawingNumber - && c.BomItem.ItemNo == itemNo - && c.ContentHash != null) - .OrderByDescending(c => c.Id) - .Select(c => new { c.ContentHash, c.Revision }) - .FirstOrDefaultAsync(); - - if (previous == null) - return 1; - - return previous.ContentHash == contentHash - ? previous.Revision - : previous.Revision + 1; - } - - private static BomItemDto MapToDto(BomItem b) => new() - { - ID = b.ID, - ItemNo = b.ItemNo, - PartNo = b.PartNo, - SortOrder = b.SortOrder, - Qty = b.Qty, - TotalQty = b.TotalQty, - Description = b.Description, - PartName = b.PartName, - ConfigurationName = b.ConfigurationName, - Material = b.Material, - CutTemplate = b.CutTemplate == null ? null : new CutTemplateDto - { - Id = b.CutTemplate.Id, - DxfFilePath = b.CutTemplate.DxfFilePath, - ContentHash = b.CutTemplate.ContentHash, - Revision = b.CutTemplate.Revision, - Thickness = b.CutTemplate.Thickness, - KFactor = b.CutTemplate.KFactor, - DefaultBendRadius = b.CutTemplate.DefaultBendRadius - }, - FormProgram = b.FormProgram == null ? null : new FormProgramDto - { - Id = b.FormProgram.Id, - ProgramFilePath = b.FormProgram.ProgramFilePath, - ContentHash = b.FormProgram.ContentHash, - ProgramName = b.FormProgram.ProgramName, - Thickness = b.FormProgram.Thickness, - MaterialType = b.FormProgram.MaterialType, - KFactor = b.FormProgram.KFactor, - BendCount = b.FormProgram.BendCount, - UpperToolNames = b.FormProgram.UpperToolNames, - LowerToolNames = b.FormProgram.LowerToolNames, - SetupNotes = b.FormProgram.SetupNotes - } - }; - } -} diff --git a/FabWorks.Api/Controllers/ExportsController.cs b/FabWorks.Api/Controllers/ExportsController.cs deleted file mode 100644 index 81cb8ea..0000000 --- a/FabWorks.Api/Controllers/ExportsController.cs +++ /dev/null @@ -1,366 +0,0 @@ -using System.IO.Compression; -using FabWorks.Api.DTOs; -using FabWorks.Api.Services; -using FabWorks.Core.Data; -using FabWorks.Core.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace FabWorks.Api.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class ExportsController : ControllerBase - { - private readonly FabWorksDbContext _db; - private readonly IFileStorageService _fileStorage; - - public ExportsController(FabWorksDbContext db, IFileStorageService fileStorage) - { - _db = db; - _fileStorage = fileStorage; - } - - [HttpGet] - public async Task> List( - [FromQuery] string search = null, - [FromQuery] int skip = 0, - [FromQuery] int take = 50) - { - var query = _db.ExportRecords - .Include(r => r.BomItems) - .AsQueryable(); - - if (!string.IsNullOrWhiteSpace(search)) - { - var term = search.Trim().ToLower(); - query = query.Where(r => - r.DrawingNumber.ToLower().Contains(term) || - (r.Title != null && r.Title.ToLower().Contains(term)) || - r.ExportedBy.ToLower().Contains(term) || - r.BomItems.Any(b => b.PartName.ToLower().Contains(term) || - b.Description.ToLower().Contains(term))); - } - - var total = await query.CountAsync(); - - var records = await query - .OrderByDescending(r => r.ExportedAt) - .Skip(skip) - .Take(take) - .ToListAsync(); - - return new - { - total, - items = records.Select(r => new - { - r.Id, - r.DrawingNumber, - r.Title, - r.SourceFilePath, - r.ExportedAt, - r.ExportedBy, - BomItemCount = r.BomItems?.Count ?? 0 - }) - }; - } - - [HttpPost] - public async Task> Create(CreateExportRequest request) - { - var record = new ExportRecord - { - DrawingNumber = request.DrawingNumber, - Title = request.Title, - EquipmentNo = request.EquipmentNo, - DrawingNo = request.DrawingNo, - SourceFilePath = request.SourceFilePath, - OutputFolder = request.OutputFolder, - ExportedAt = DateTime.Now, - ExportedBy = Environment.UserName - }; - - _db.ExportRecords.Add(record); - await _db.SaveChangesAsync(); - - return CreatedAtAction(nameof(GetById), new { id = record.Id }, MapToDto(record)); - } - - [HttpGet("{id}")] - public async Task> GetById(int id) - { - var record = await _db.ExportRecords - .Include(r => r.BomItems).ThenInclude(b => b.CutTemplate) - .Include(r => r.BomItems).ThenInclude(b => b.FormProgram) - .FirstOrDefaultAsync(r => r.Id == id); - - if (record == null) return NotFound(); - return MapToDto(record); - } - - [HttpGet("by-source")] - public async Task> GetBySourceFile([FromQuery] string path) - { - var record = await _db.ExportRecords - .Where(r => r.SourceFilePath.ToLower() == path.ToLower() - && !string.IsNullOrEmpty(r.DrawingNumber)) - .OrderByDescending(r => r.Id) - .FirstOrDefaultAsync(); - - if (record == null) return NotFound(); - return MapToDto(record); - } - - [HttpGet("by-drawing")] - public async Task>> GetByDrawing([FromQuery] string drawingNumber) - { - var records = await _db.ExportRecords - .Include(r => r.BomItems).ThenInclude(b => b.CutTemplate) - .Include(r => r.BomItems).ThenInclude(b => b.FormProgram) - .Where(r => r.DrawingNumber == drawingNumber) - .OrderByDescending(r => r.ExportedAt) - .ToListAsync(); - - return records.Select(MapToDto).ToList(); - } - - [HttpGet("next-item-number")] - public async Task> GetNextItemNumber([FromQuery] string drawingNumber) - { - if (string.IsNullOrEmpty(drawingNumber)) return "1"; - - var existingItems = await _db.ExportRecords - .Where(r => r.DrawingNumber == drawingNumber) - .SelectMany(r => r.BomItems) - .Select(b => b.ItemNo) - .ToListAsync(); - - int maxNum = 0; - foreach (var itemNo in existingItems) - { - if (int.TryParse(itemNo, out var num) && num > maxNum) - maxNum = num; - } - return (maxNum + 1).ToString(); - } - - [HttpGet("drawing-numbers")] - public async Task>> GetDrawingNumbers() - { - var numbers = await _db.ExportRecords - .Select(r => r.DrawingNumber) - .Where(d => !string.IsNullOrEmpty(d)) - .Distinct() - .ToListAsync(); - - return numbers; - } - - [HttpGet("equipment-numbers")] - public async Task>> GetEquipmentNumbers() - { - var numbers = await _db.ExportRecords - .Select(r => r.EquipmentNo) - .Where(e => !string.IsNullOrEmpty(e)) - .Distinct() - .OrderBy(e => e) - .ToListAsync(); - - return numbers; - } - - [HttpGet("drawing-numbers-by-equipment")] - public async Task>> GetDrawingNumbersByEquipment([FromQuery] string equipmentNo) - { - var query = _db.ExportRecords - .Where(r => !string.IsNullOrEmpty(r.DrawingNo)); - - if (!string.IsNullOrEmpty(equipmentNo)) - query = query.Where(r => r.EquipmentNo == equipmentNo); - - var numbers = await query - .Select(r => r.DrawingNo) - .Distinct() - .OrderBy(d => d) - .ToListAsync(); - - return numbers; - } - - [HttpGet("previous-pdf-hash")] - public async Task> GetPreviousPdfHash( - [FromQuery] string drawingNumber, - [FromQuery] int? excludeId = null) - { - var hash = await _db.ExportRecords - .Where(r => r.DrawingNumber == drawingNumber - && r.PdfContentHash != null - && (excludeId == null || r.Id != excludeId)) - .OrderByDescending(r => r.Id) - .Select(r => r.PdfContentHash) - .FirstOrDefaultAsync(); - - if (hash == null) return NotFound(); - return hash; - } - - [HttpPatch("{id}/pdf-hash")] - public async Task UpdatePdfHash(int id, [FromBody] UpdatePdfHashRequest request) - { - var record = await _db.ExportRecords.FindAsync(id); - if (record == null) return NotFound(); - - record.PdfContentHash = request.PdfContentHash; - await _db.SaveChangesAsync(); - - return NoContent(); - } - - [HttpGet("previous-cut-template")] - public async Task> GetPreviousCutTemplate( - [FromQuery] string drawingNumber, - [FromQuery] string itemNo) - { - if (string.IsNullOrEmpty(drawingNumber) || string.IsNullOrEmpty(itemNo)) - return BadRequest("drawingNumber and itemNo are required."); - - var ct = await _db.CutTemplates - .Where(c => c.BomItem.ExportRecord.DrawingNumber == drawingNumber - && c.BomItem.ItemNo == itemNo - && c.ContentHash != null) - .OrderByDescending(c => c.Id) - .FirstOrDefaultAsync(); - - if (ct == null) return NotFound(); - - return new CutTemplateDto - { - Id = ct.Id, - DxfFilePath = ct.DxfFilePath, - ContentHash = ct.ContentHash, - Revision = ct.Revision, - Thickness = ct.Thickness, - KFactor = ct.KFactor, - DefaultBendRadius = ct.DefaultBendRadius - }; - } - - [HttpDelete("{id}")] - public async Task Delete(int id) - { - var record = await _db.ExportRecords - .Include(r => r.BomItems).ThenInclude(b => b.CutTemplate) - .Include(r => r.BomItems).ThenInclude(b => b.FormProgram) - .FirstOrDefaultAsync(r => r.Id == id); - - if (record == null) return NotFound(); - - _db.ExportRecords.Remove(record); - await _db.SaveChangesAsync(); - - return NoContent(); - } - - [HttpGet("{id}/download-dxfs")] - public async Task DownloadAllDxfs(int id) - { - var record = await _db.ExportRecords - .Include(r => r.BomItems).ThenInclude(b => b.CutTemplate) - .FirstOrDefaultAsync(r => r.Id == id); - - if (record == null) return NotFound(); - - var dxfItems = record.BomItems - .Where(b => b.CutTemplate?.ContentHash != null) - .ToList(); - - if (dxfItems.Count == 0) return NotFound("No DXF files for this export."); - - var ms = new MemoryStream(); - using (var zip = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true)) - { - var usedNames = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var b in dxfItems) - { - var ct = b.CutTemplate; - var fileName = ct.DxfFilePath?.Split(new[] { '/', '\\' }).LastOrDefault() - ?? $"PT{(b.ItemNo ?? "").PadLeft(2, '0')}.dxf"; - - // Ensure unique names in zip - if (!usedNames.Add(fileName)) - { - var baseName = Path.GetFileNameWithoutExtension(fileName); - var ext = Path.GetExtension(fileName); - var counter = 2; - do { fileName = $"{baseName}_{counter++}{ext}"; } - while (!usedNames.Add(fileName)); - } - - var blobStream = _fileStorage.OpenBlob(ct.ContentHash, "dxf"); - if (blobStream == null) continue; - - var entry = zip.CreateEntry(fileName, CompressionLevel.Fastest); - using var entryStream = entry.Open(); - await blobStream.CopyToAsync(entryStream); - blobStream.Dispose(); - } - } - - ms.Position = 0; - var zipName = $"{record.DrawingNumber ?? $"Export-{id}"} DXFs.zip"; - return File(ms, "application/zip", zipName); - } - - private static ExportDetailDto MapToDto(ExportRecord r) => new() - { - Id = r.Id, - DrawingNumber = r.DrawingNumber, - Title = r.Title, - EquipmentNo = r.EquipmentNo, - DrawingNo = r.DrawingNo, - SourceFilePath = r.SourceFilePath, - OutputFolder = r.OutputFolder, - ExportedAt = r.ExportedAt, - ExportedBy = r.ExportedBy, - PdfContentHash = r.PdfContentHash, - BomItems = r.BomItems?.Select(b => new BomItemDto - { - ID = b.ID, - ItemNo = b.ItemNo, - PartNo = b.PartNo, - SortOrder = b.SortOrder, - Qty = b.Qty, - TotalQty = b.TotalQty, - Description = b.Description, - PartName = b.PartName, - ConfigurationName = b.ConfigurationName, - Material = b.Material, - CutTemplate = b.CutTemplate == null ? null : new CutTemplateDto - { - Id = b.CutTemplate.Id, - DxfFilePath = b.CutTemplate.DxfFilePath, - ContentHash = b.CutTemplate.ContentHash, - Revision = b.CutTemplate.Revision, - Thickness = b.CutTemplate.Thickness, - KFactor = b.CutTemplate.KFactor, - DefaultBendRadius = b.CutTemplate.DefaultBendRadius - }, - FormProgram = b.FormProgram == null ? null : new FormProgramDto - { - Id = b.FormProgram.Id, - ProgramFilePath = b.FormProgram.ProgramFilePath, - ContentHash = b.FormProgram.ContentHash, - ProgramName = b.FormProgram.ProgramName, - Thickness = b.FormProgram.Thickness, - MaterialType = b.FormProgram.MaterialType, - KFactor = b.FormProgram.KFactor, - BendCount = b.FormProgram.BendCount, - UpperToolNames = b.FormProgram.UpperToolNames, - LowerToolNames = b.FormProgram.LowerToolNames, - SetupNotes = b.FormProgram.SetupNotes - } - }).ToList() ?? new() - }; - } -} diff --git a/FabWorks.Api/Controllers/FileBrowserController.cs b/FabWorks.Api/Controllers/FileBrowserController.cs deleted file mode 100644 index 895b20f..0000000 --- a/FabWorks.Api/Controllers/FileBrowserController.cs +++ /dev/null @@ -1,184 +0,0 @@ -using FabWorks.Api.Services; -using FabWorks.Core.Data; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; -using Microsoft.EntityFrameworkCore; - -namespace FabWorks.Api.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class FileBrowserController : ControllerBase - { - private readonly IFileStorageService _fileStorage; - private readonly FabWorksDbContext _db; - private readonly FileExtensionContentTypeProvider _contentTypeProvider = new(); - - public FileBrowserController(IFileStorageService fileStorage, FabWorksDbContext db) - { - _fileStorage = fileStorage; - _db = db; - } - - [HttpGet("files")] - public async Task> ListFiles( - [FromQuery] string search = null, - [FromQuery] string type = null) - { - var files = new List(); - - // Query DXF files from CutTemplates - if (type == null || type.Equals("dxf", StringComparison.OrdinalIgnoreCase)) - { - var dxfQuery = _db.CutTemplates - .Where(c => c.ContentHash != null) - .Select(c => new - { - c.Id, - c.DxfFilePath, - c.ContentHash, - c.Thickness, - DrawingNumber = c.BomItem.ExportRecord.DrawingNumber, - CreatedAt = c.BomItem.ExportRecord.ExportedAt - }); - - if (!string.IsNullOrWhiteSpace(search)) - { - var term = search.Trim().ToLower(); - dxfQuery = dxfQuery.Where(c => - c.DxfFilePath.ToLower().Contains(term) || - c.DrawingNumber.ToLower().Contains(term)); - } - - var dxfResults = await dxfQuery - .OrderByDescending(c => c.CreatedAt) - .Take(500) - .ToListAsync(); - - // Deduplicate by content hash (keep latest) - var seenDxf = new HashSet(); - foreach (var c in dxfResults) - { - if (seenDxf.Contains(c.ContentHash)) continue; - seenDxf.Add(c.ContentHash); - - var fileName = c.DxfFilePath?.Split(new[] { '/', '\\' }).LastOrDefault() ?? c.DxfFilePath; - files.Add(new StoredFileEntry - { - FileName = fileName, - ContentHash = c.ContentHash, - FileType = "dxf", - DrawingNumber = c.DrawingNumber, - Thickness = c.Thickness, - CreatedAt = c.CreatedAt - }); - } - } - - // Query PDF files from ExportRecords - if (type == null || type.Equals("pdf", StringComparison.OrdinalIgnoreCase)) - { - var pdfQuery = _db.ExportRecords - .Where(r => r.PdfContentHash != null) - .Select(r => new - { - r.Id, - r.DrawingNumber, - r.PdfContentHash, - r.ExportedAt - }); - - if (!string.IsNullOrWhiteSpace(search)) - { - var term = search.Trim().ToLower(); - pdfQuery = pdfQuery.Where(r => - r.DrawingNumber.ToLower().Contains(term)); - } - - var pdfResults = await pdfQuery - .OrderByDescending(r => r.ExportedAt) - .Take(500) - .ToListAsync(); - - // Deduplicate by content hash - var seenPdf = new HashSet(); - foreach (var r in pdfResults) - { - if (seenPdf.Contains(r.PdfContentHash)) continue; - seenPdf.Add(r.PdfContentHash); - - files.Add(new StoredFileEntry - { - FileName = $"{r.DrawingNumber}.pdf", - ContentHash = r.PdfContentHash, - FileType = "pdf", - DrawingNumber = r.DrawingNumber, - CreatedAt = r.ExportedAt - }); - } - } - - return new FileListResult - { - Total = files.Count, - Files = files.OrderByDescending(f => f.CreatedAt).ToList() - }; - } - - [HttpGet("preview")] - public IActionResult PreviewFile([FromQuery] string hash, [FromQuery] string ext = "dxf") - { - if (string.IsNullOrEmpty(hash) || hash.Length < 4) - return BadRequest("Invalid hash."); - - if (!_fileStorage.BlobExists(hash, ext)) - return NotFound("File not found."); - - var stream = _fileStorage.OpenBlob(hash, ext); - if (stream == null) - return NotFound("File not found."); - - var virtualName = $"file.{ext}"; - if (!_contentTypeProvider.TryGetContentType(virtualName, out var contentType)) - contentType = "application/octet-stream"; - - return File(stream, contentType); - } - - [HttpGet("download")] - public IActionResult DownloadFile([FromQuery] string hash, [FromQuery] string ext = "dxf", [FromQuery] string name = null) - { - if (string.IsNullOrEmpty(hash) || hash.Length < 4) - return BadRequest("Invalid hash."); - - if (!_fileStorage.BlobExists(hash, ext)) - return NotFound("File not found."); - - var stream = _fileStorage.OpenBlob(hash, ext); - if (stream == null) - return NotFound("File not found."); - - var fileName = name ?? $"{hash[..8]}.{ext}"; - if (!_contentTypeProvider.TryGetContentType(fileName, out var contentType)) - contentType = "application/octet-stream"; - - return File(stream, contentType, fileName); - } - } - - public class FileListResult - { - public int Total { get; set; } - public List Files { get; set; } - } - - public class StoredFileEntry - { - public string FileName { get; set; } - public string ContentHash { get; set; } - public string FileType { get; set; } - public string DrawingNumber { get; set; } - public double? Thickness { get; set; } - public DateTime CreatedAt { get; set; } - } -} diff --git a/FabWorks.Api/Controllers/FilesController.cs b/FabWorks.Api/Controllers/FilesController.cs deleted file mode 100644 index b8263f1..0000000 --- a/FabWorks.Api/Controllers/FilesController.cs +++ /dev/null @@ -1,93 +0,0 @@ -using FabWorks.Api.DTOs; -using FabWorks.Api.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.StaticFiles; - -namespace FabWorks.Api.Controllers -{ - [ApiController] - [Route("api/[controller]")] - public class FilesController : ControllerBase - { - private readonly IFileStorageService _fileStorage; - private readonly FileExtensionContentTypeProvider _contentTypeProvider = new(); - - public FilesController(IFileStorageService fileStorage) - { - _fileStorage = fileStorage; - } - - [HttpPost("dxf")] - [RequestSizeLimit(50_000_000)] // 50 MB - public async Task> UploadDxf( - IFormFile file, - [FromForm] string equipment, - [FromForm] string drawingNo, - [FromForm] string itemNo, - [FromForm] string contentHash) - { - if (file == null || file.Length == 0) - return BadRequest("No file uploaded."); - - using var stream = file.OpenReadStream(); - var result = await _fileStorage.StoreDxfAsync(stream, equipment, drawingNo, itemNo, contentHash); - - return Ok(new FileUploadResponse - { - StoredFilePath = result.FileName, - ContentHash = result.ContentHash, - FileName = result.FileName, - WasUnchanged = result.WasUnchanged, - IsNewFile = result.IsNewFile - }); - } - - [HttpPost("pdf")] - [RequestSizeLimit(100_000_000)] // 100 MB - public async Task> UploadPdf( - IFormFile file, - [FromForm] string equipment, - [FromForm] string drawingNo, - [FromForm] string contentHash, - [FromForm] int? exportRecordId = null) - { - if (file == null || file.Length == 0) - return BadRequest("No file uploaded."); - - using var stream = file.OpenReadStream(); - var result = await _fileStorage.StorePdfAsync(stream, equipment, drawingNo, contentHash, exportRecordId); - - return Ok(new FileUploadResponse - { - StoredFilePath = result.FileName, - ContentHash = result.ContentHash, - FileName = result.FileName, - WasUnchanged = result.WasUnchanged, - IsNewFile = result.IsNewFile - }); - } - - [HttpGet("blob/{hash}")] - public IActionResult GetBlob(string hash, [FromQuery] string ext = "dxf", [FromQuery] bool download = false, [FromQuery] string name = null) - { - if (string.IsNullOrEmpty(hash) || hash.Length < 4) - return BadRequest("Invalid hash."); - - if (!_fileStorage.BlobExists(hash, ext)) - return NotFound("Blob not found."); - - var stream = _fileStorage.OpenBlob(hash, ext); - if (stream == null) - return NotFound("Blob not found."); - - var fileName = !string.IsNullOrEmpty(name) ? name : $"{hash[..8]}.{ext}"; - if (!_contentTypeProvider.TryGetContentType(fileName, out var contentType)) - contentType = "application/octet-stream"; - - if (download) - return File(stream, contentType, fileName); - - return File(stream, contentType); - } - } -} diff --git a/FabWorks.Api/Controllers/FormProgramsController.cs b/FabWorks.Api/Controllers/FormProgramsController.cs deleted file mode 100644 index 39ab191..0000000 --- a/FabWorks.Api/Controllers/FormProgramsController.cs +++ /dev/null @@ -1,106 +0,0 @@ -using FabWorks.Api.DTOs; -using FabWorks.Api.Services; -using FabWorks.Core.Data; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace FabWorks.Api.Controllers -{ - [ApiController] - [Route("api/form-programs")] - public class FormProgramsController : ControllerBase - { - private readonly FabWorksDbContext _db; - private readonly FormProgramService _formService; - - public FormProgramsController(FabWorksDbContext db, FormProgramService formService) - { - _db = db; - _formService = formService; - } - - [HttpGet("by-drawing")] - public async Task>> GetByDrawing([FromQuery] string drawingNumber) - { - var programs = await _db.FormPrograms - .Include(fp => fp.BomItem) - .ThenInclude(b => b.ExportRecord) - .Where(fp => fp.BomItem.ExportRecord.DrawingNumber == drawingNumber) - .ToListAsync(); - - return programs.Select(fp => new FormProgramDto - { - Id = fp.Id, - ProgramFilePath = fp.ProgramFilePath, - ContentHash = fp.ContentHash, - ProgramName = fp.ProgramName, - Thickness = fp.Thickness, - MaterialType = fp.MaterialType, - KFactor = fp.KFactor, - BendCount = fp.BendCount, - UpperToolNames = fp.UpperToolNames, - LowerToolNames = fp.LowerToolNames, - SetupNotes = fp.SetupNotes - }).ToList(); - } - - [HttpPost("parse")] - public ActionResult Parse([FromQuery] string filePath) - { - if (!System.IO.File.Exists(filePath)) - return NotFound($"File not found: {filePath}"); - - var fp = _formService.ParseFromFile(filePath); - return new FormProgramDto - { - ProgramFilePath = fp.ProgramFilePath, - ContentHash = fp.ContentHash, - ProgramName = fp.ProgramName, - Thickness = fp.Thickness, - MaterialType = fp.MaterialType, - KFactor = fp.KFactor, - BendCount = fp.BendCount, - UpperToolNames = fp.UpperToolNames, - LowerToolNames = fp.LowerToolNames, - SetupNotes = fp.SetupNotes - }; - } - - [HttpPost("{bomItemId}")] - public async Task> AttachToItem(int bomItemId, [FromQuery] string filePath) - { - var bomItem = await _db.BomItems - .Include(b => b.FormProgram) - .FirstOrDefaultAsync(b => b.ID == bomItemId); - - if (bomItem == null) return NotFound("BOM item not found"); - - if (!System.IO.File.Exists(filePath)) - return NotFound($"File not found: {filePath}"); - - var fp = _formService.ParseFromFile(filePath); - fp.BomItemId = bomItemId; - - if (bomItem.FormProgram != null) - _db.FormPrograms.Remove(bomItem.FormProgram); - - bomItem.FormProgram = fp; - await _db.SaveChangesAsync(); - - return new FormProgramDto - { - Id = fp.Id, - ProgramFilePath = fp.ProgramFilePath, - ContentHash = fp.ContentHash, - ProgramName = fp.ProgramName, - Thickness = fp.Thickness, - MaterialType = fp.MaterialType, - KFactor = fp.KFactor, - BendCount = fp.BendCount, - UpperToolNames = fp.UpperToolNames, - LowerToolNames = fp.LowerToolNames, - SetupNotes = fp.SetupNotes - }; - } - } -} diff --git a/FabWorks.Api/DTOs/CreateExportRequest.cs b/FabWorks.Api/DTOs/CreateExportRequest.cs deleted file mode 100644 index db9e94d..0000000 --- a/FabWorks.Api/DTOs/CreateExportRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace FabWorks.Api.DTOs -{ - public class CreateExportRequest - { - public string DrawingNumber { get; set; } - public string Title { get; set; } - public string EquipmentNo { get; set; } - public string DrawingNo { get; set; } - public string SourceFilePath { get; set; } - public string OutputFolder { get; set; } - } - - public class UpdatePdfHashRequest - { - public string PdfContentHash { get; set; } - } -} diff --git a/FabWorks.Api/DTOs/ExportDetailDto.cs b/FabWorks.Api/DTOs/ExportDetailDto.cs deleted file mode 100644 index ed6eb78..0000000 --- a/FabWorks.Api/DTOs/ExportDetailDto.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace FabWorks.Api.DTOs -{ - public class ExportDetailDto - { - public int Id { get; set; } - public string DrawingNumber { get; set; } - public string Title { get; set; } - public string EquipmentNo { get; set; } - public string DrawingNo { get; set; } - public string SourceFilePath { get; set; } - public string OutputFolder { get; set; } - public DateTime ExportedAt { get; set; } - public string ExportedBy { get; set; } - public string PdfContentHash { get; set; } - public List BomItems { get; set; } = new(); - } - - public class BomItemDto - { - public int ID { get; set; } - public string ItemNo { get; set; } - public string PartNo { get; set; } - public int SortOrder { get; set; } - public int? Qty { get; set; } - public int? TotalQty { get; set; } - public string Description { get; set; } - public string PartName { get; set; } - public string ConfigurationName { get; set; } - public string Material { get; set; } - public CutTemplateDto CutTemplate { get; set; } - public FormProgramDto FormProgram { get; set; } - } - - public class CutTemplateDto - { - public int Id { get; set; } - public string DxfFilePath { get; set; } - public string ContentHash { get; set; } - public int Revision { get; set; } - public double? Thickness { get; set; } - public double? KFactor { get; set; } - public double? DefaultBendRadius { get; set; } - } - - public class FormProgramDto - { - public int Id { get; set; } - public string ProgramFilePath { get; set; } - public string ContentHash { get; set; } - public string ProgramName { get; set; } - public double? Thickness { get; set; } - public string MaterialType { get; set; } - public double? KFactor { get; set; } - public int BendCount { get; set; } - public string UpperToolNames { get; set; } - public string LowerToolNames { get; set; } - public string SetupNotes { get; set; } - } -} diff --git a/FabWorks.Api/DTOs/FileUploadResponse.cs b/FabWorks.Api/DTOs/FileUploadResponse.cs deleted file mode 100644 index 7a1fad0..0000000 --- a/FabWorks.Api/DTOs/FileUploadResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace FabWorks.Api.DTOs -{ - public class FileUploadResponse - { - public string StoredFilePath { get; set; } // kept for client compat, contains logical filename - public string ContentHash { get; set; } - public string FileName { get; set; } - public bool WasUnchanged { get; set; } - public bool IsNewFile { get; set; } - } -} diff --git a/FabWorks.Api/FabWorks.Api.csproj b/FabWorks.Api/FabWorks.Api.csproj deleted file mode 100644 index 921afe9..0000000 --- a/FabWorks.Api/FabWorks.Api.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net8.0 - disable - enable - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/FabWorks.Api/Program.cs b/FabWorks.Api/Program.cs deleted file mode 100644 index 42e6c76..0000000 --- a/FabWorks.Api/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FabWorks.Api.Configuration; -using FabWorks.Api.Services; -using FabWorks.Core.Data; -using Microsoft.EntityFrameworkCore; - -var builder = WebApplication.CreateBuilder(args); - -builder.Services.AddControllers(); -builder.Services.AddDbContext(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("FabWorksDb"))); -builder.Services.AddSingleton(); - -builder.Services.Configure( - builder.Configuration.GetSection(FileStorageOptions.SectionName)); -builder.Services.AddScoped(); - -var app = builder.Build(); - -app.UseDefaultFiles(); -app.UseStaticFiles(new StaticFileOptions -{ - OnPrepareResponse = ctx => - ctx.Context.Response.Headers.Append("Cache-Control", "no-cache, no-store") -}); -app.MapControllers(); -app.Run(); diff --git a/FabWorks.Api/Properties/launchSettings.json b/FabWorks.Api/Properties/launchSettings.json deleted file mode 100644 index 65e9483..0000000 --- a/FabWorks.Api/Properties/launchSettings.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:45483", - "sslPort": 44397 - } - }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "", - "applicationUrl": "http://localhost:5206", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "", - "applicationUrl": "https://localhost:7182;http://localhost:5206", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} diff --git a/FabWorks.Api/Services/FileStorageService.cs b/FabWorks.Api/Services/FileStorageService.cs deleted file mode 100644 index 52cdb0b..0000000 --- a/FabWorks.Api/Services/FileStorageService.cs +++ /dev/null @@ -1,159 +0,0 @@ -using FabWorks.Api.Configuration; -using FabWorks.Core.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; - -namespace FabWorks.Api.Services -{ - public class FileUploadResult - { - public string ContentHash { get; set; } - public string FileName { get; set; } - public bool WasUnchanged { get; set; } - public bool IsNewFile { get; set; } - } - - public interface IFileStorageService - { - string OutputFolder { get; } - Task StoreDxfAsync(Stream stream, string equipment, string drawingNo, string itemNo, string contentHash); - Task StorePdfAsync(Stream stream, string equipment, string drawingNo, string contentHash, int? exportRecordId = null); - Stream OpenBlob(string contentHash, string extension); - bool BlobExists(string contentHash, string extension); - } - - public class FileStorageService : IFileStorageService - { - private readonly FileStorageOptions _options; - private readonly FabWorksDbContext _db; - - public string OutputFolder => _options.OutputFolder; - - public FileStorageService(IOptions options, FabWorksDbContext db) - { - _options = options.Value; - _db = db; - - var blobRoot = Path.Combine(_options.OutputFolder, "blobs"); - if (!Directory.Exists(blobRoot)) - Directory.CreateDirectory(blobRoot); - } - - public async Task StoreDxfAsync(Stream stream, string equipment, string drawingNo, string itemNo, string contentHash) - { - var fileName = BuildDxfFileName(drawingNo, equipment, itemNo); - - // Look up previous hash by drawing number + item number - var drawingNumber = BuildDrawingNumber(equipment, drawingNo); - var previousHash = await _db.CutTemplates - .Where(c => c.BomItem.ExportRecord.DrawingNumber == drawingNumber - && c.BomItem.ItemNo == itemNo - && c.ContentHash != null) - .OrderByDescending(c => c.Id) - .Select(c => c.ContentHash) - .FirstOrDefaultAsync(); - - var wasUnchanged = previousHash != null && previousHash == contentHash; - var isNewFile = await StoreBlobAsync(stream, contentHash, "dxf"); - - return new FileUploadResult - { - ContentHash = contentHash, - FileName = fileName, - WasUnchanged = wasUnchanged, - IsNewFile = isNewFile - }; - } - - public async Task StorePdfAsync(Stream stream, string equipment, string drawingNo, string contentHash, int? exportRecordId = null) - { - var drawingNumber = BuildDrawingNumber(equipment, drawingNo); - var fileName = $"{drawingNumber}.pdf"; - - // Look up previous PDF hash - var previousHash = await _db.ExportRecords - .Where(r => r.DrawingNumber == drawingNumber - && r.PdfContentHash != null - && (exportRecordId == null || r.Id != exportRecordId)) - .OrderByDescending(r => r.Id) - .Select(r => r.PdfContentHash) - .FirstOrDefaultAsync(); - - var wasUnchanged = previousHash != null && previousHash == contentHash; - var isNewFile = await StoreBlobAsync(stream, contentHash, "pdf"); - - // Update the export record with the PDF content hash - if (exportRecordId.HasValue) - { - var record = await _db.ExportRecords.FindAsync(exportRecordId.Value); - if (record != null) - { - record.PdfContentHash = contentHash; - await _db.SaveChangesAsync(); - } - } - - return new FileUploadResult - { - ContentHash = contentHash, - FileName = fileName, - WasUnchanged = wasUnchanged, - IsNewFile = isNewFile - }; - } - - public Stream OpenBlob(string contentHash, string extension) - { - var path = GetBlobPath(contentHash, extension); - if (!File.Exists(path)) - return null; - return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); - } - - public bool BlobExists(string contentHash, string extension) - { - return File.Exists(GetBlobPath(contentHash, extension)); - } - - private async Task StoreBlobAsync(Stream stream, string contentHash, string extension) - { - var blobPath = GetBlobPath(contentHash, extension); - - if (File.Exists(blobPath)) - return false; // blob already exists (dedup) - - var dir = Path.GetDirectoryName(blobPath); - if (!Directory.Exists(dir)) - Directory.CreateDirectory(dir); - - using var fileStream = new FileStream(blobPath, FileMode.Create, FileAccess.Write); - await stream.CopyToAsync(fileStream); - return true; // new blob written - } - - private string GetBlobPath(string contentHash, string extension) - { - var prefix1 = contentHash[..2]; - var prefix2 = contentHash[2..4]; - return Path.Combine(_options.OutputFolder, "blobs", prefix1, prefix2, $"{contentHash}.{extension}"); - } - - private static string BuildDrawingNumber(string equipment, string drawingNo) - { - if (!string.IsNullOrEmpty(equipment) && !string.IsNullOrEmpty(drawingNo)) - return $"{equipment} {drawingNo}"; - if (!string.IsNullOrEmpty(equipment)) - return equipment; - return drawingNo ?? ""; - } - - private static string BuildDxfFileName(string drawingNo, string equipment, string itemNo) - { - var drawingNumber = BuildDrawingNumber(equipment, drawingNo); - var paddedItem = (itemNo ?? "").PadLeft(2, '0'); - if (!string.IsNullOrEmpty(drawingNumber) && !string.IsNullOrEmpty(itemNo)) - return $"{drawingNumber} PT{paddedItem}.dxf"; - return $"PT{paddedItem}.dxf"; - } - } -} diff --git a/FabWorks.Api/Services/FormProgramService.cs b/FabWorks.Api/Services/FormProgramService.cs deleted file mode 100644 index 01cb56e..0000000 --- a/FabWorks.Api/Services/FormProgramService.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FabWorks.Core.Models; -using FabWorks.Core.PressBrake; -using System.Security.Cryptography; - -namespace FabWorks.Api.Services -{ - public class FormProgramService - { - public FormProgram ParseFromFile(string filePath) - { - var pgm = FabWorks.Core.PressBrake.Program.Load(filePath); - var hash = ComputeFileHash(filePath); - - return new FormProgram - { - ProgramFilePath = filePath, - ContentHash = hash, - ProgramName = pgm.ProgName ?? "", - Thickness = pgm.MatThick > 0 ? pgm.MatThick : null, - MaterialType = pgm.MatType.ToString(), - KFactor = pgm.KFactor > 0 ? pgm.KFactor : null, - BendCount = pgm.Steps.Count, - UpperToolNames = string.Join(", ", pgm.UpperToolSets - .Select(t => t.Name).Where(n => !string.IsNullOrEmpty(n)).Distinct()), - LowerToolNames = string.Join(", ", pgm.LowerToolSets - .Select(t => t.Name).Where(n => !string.IsNullOrEmpty(n)).Distinct()), - SetupNotes = pgm.SetupNotes ?? "" - }; - } - - private static string ComputeFileHash(string filePath) - { - using var sha = SHA256.Create(); - using var stream = File.OpenRead(filePath); - var bytes = sha.ComputeHash(stream); - return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant(); - } - } -} diff --git a/FabWorks.Api/appsettings.Development.json b/FabWorks.Api/appsettings.Development.json deleted file mode 100644 index 0c208ae..0000000 --- a/FabWorks.Api/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/FabWorks.Api/appsettings.json b/FabWorks.Api/appsettings.json deleted file mode 100644 index b9b4456..0000000 --- a/FabWorks.Api/appsettings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "FabWorksDb": "Server=localhost;Database=ExportDxfDb;Trusted_Connection=True;TrustServerCertificate=True;" - }, - "FileStorage": { - "OutputFolder": "C:\\ExportDXF\\Output" - } -} diff --git a/FabWorks.Api/wwwroot/css/styles.css b/FabWorks.Api/wwwroot/css/styles.css deleted file mode 100644 index eadff8d..0000000 --- a/FabWorks.Api/wwwroot/css/styles.css +++ /dev/null @@ -1,745 +0,0 @@ -:root { - --bg-deep: #f0f1f3; - --bg: #f8f9fa; - --surface: #ffffff; - --surface-raised: #ffffff; - --border: #d0d5dd; - --border-subtle: #e4e7ec; - --text: #1a1a1a; - --text-secondary: #475467; - --text-dim: #667085; - --cyan: #0975b0; - --cyan-dim: rgba(9, 117, 176, 0.1); - --cyan-glow: rgba(9, 117, 176, 0.2); - --amber: #b54708; - --amber-dim: rgba(181, 71, 8, 0.08); - --green: #067647; - --green-dim: rgba(6, 118, 71, 0.08); - --red: #d92d20; - --sidebar-w: 64px; - --font-display: 'Outfit', sans-serif; - --font-body: 'IBM Plex Sans', sans-serif; - --font-mono: 'IBM Plex Mono', monospace; -} - -* { box-sizing: border-box; margin: 0; padding: 0; } - -body { - font-family: var(--font-body); - background: var(--bg); - color: var(--text); - display: flex; - min-height: 100vh; - overflow-x: hidden; -} - -/* ─── Sidebar ─── */ -.sidebar { - width: var(--sidebar-w); - background: var(--bg-deep); - border-right: 1px solid var(--border); - display: flex; - flex-direction: column; - align-items: center; - position: fixed; - top: 0; left: 0; bottom: 0; - z-index: 50; - padding-top: 8px; -} - -.sidebar-brand { - width: 40px; height: 40px; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 24px; - position: relative; -} - -.sidebar-brand::after { - content: ''; - position: absolute; - bottom: -12px; - left: 8px; right: 8px; - height: 1px; - background: var(--border); -} - -.sidebar-brand svg { - width: 26px; height: 26px; - color: var(--cyan); -} - -.sidebar-nav { - display: flex; - flex-direction: column; - gap: 4px; - padding-top: 16px; - width: 100%; -} - -.nav-item { - display: flex; - align-items: center; - justify-content: center; - width: 44px; height: 44px; - margin: 0 auto; - color: var(--text-dim); - text-decoration: none; - cursor: pointer; - border-radius: 8px; - transition: all 0.2s; - position: relative; -} - -.nav-item:hover { - color: var(--text-secondary); - background: var(--surface); -} - -.nav-item.active { - color: var(--cyan); - background: var(--cyan-dim); -} - -.nav-item.active::before { - content: ''; - position: absolute; - left: -10px; - top: 50%; - transform: translateY(-50%); - width: 3px; - height: 20px; - background: var(--cyan); - border-radius: 0 2px 2px 0; -} - -.nav-item svg { width: 20px; height: 20px; } - -.nav-tooltip { - position: absolute; - left: calc(100% + 12px); - top: 50%; - transform: translateY(-50%); - background: var(--text); - border: 1px solid var(--border); - color: #fff; - padding: 4px 10px; - border-radius: 4px; - font-size: 13px; - font-family: var(--font-body); - white-space: nowrap; - opacity: 0; - pointer-events: none; - transition: opacity 0.15s; - z-index: 100; -} - -.nav-item:hover .nav-tooltip { opacity: 1; } - -/* ─── Main ─── */ -.main { - margin-left: var(--sidebar-w); - flex: 1; - display: flex; - flex-direction: column; - min-height: 100vh; - position: relative; - z-index: 1; -} - -.topbar { - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(12px); - border-bottom: 1px solid var(--border); - padding: 0 32px; - height: 56px; - display: flex; - align-items: center; - justify-content: space-between; - position: sticky; - top: 0; - z-index: 40; -} - -.topbar-left { - display: flex; - align-items: center; - gap: 12px; -} - -.topbar h2 { - font-family: var(--font-display); - font-size: 18px; - font-weight: 600; - letter-spacing: -0.01em; -} - -.topbar-tag { - font-family: var(--font-mono); - font-size: 12px; - color: var(--cyan); - background: var(--cyan-dim); - padding: 2px 8px; - border-radius: 3px; - letter-spacing: 0.5px; - text-transform: uppercase; -} - -.page-content { - padding: 28px 32px; - flex: 1; -} - -/* ─── Animations ─── */ -@keyframes fadeSlideIn { - from { opacity: 0; transform: translateY(8px); } - to { opacity: 1; transform: translateY(0); } -} - -.animate-in { - animation: fadeSlideIn 0.3s ease forwards; - opacity: 0; -} - -.animate-in:nth-child(1) { animation-delay: 0.04s; } -.animate-in:nth-child(2) { animation-delay: 0.08s; } -.animate-in:nth-child(3) { animation-delay: 0.12s; } -.animate-in:nth-child(4) { animation-delay: 0.16s; } - -/* ─── Cards ─── */ -.card { - background: var(--surface); - border: 1px solid var(--border-subtle); - border-radius: 6px; - overflow: hidden; - box-shadow: 0 1px 2px rgba(0,0,0,0.05); -} - -.card-header { - padding: 14px 18px; - border-bottom: 1px solid var(--border-subtle); - font-family: var(--font-display); - font-weight: 600; - font-size: 14px; - letter-spacing: 0.02em; - display: flex; - align-items: center; - justify-content: space-between; - text-transform: uppercase; - color: var(--text-secondary); -} - -.card-body { padding: 18px; } - -/* ─── Stats ─── */ -.stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 12px; - margin-bottom: 24px; -} - -.stat-card { - background: var(--surface); - border: 1px solid var(--border-subtle); - border-radius: 6px; - padding: 18px 20px; - position: relative; - overflow: hidden; - box-shadow: 0 1px 2px rgba(0,0,0,0.05); -} - -.stat-card::before { - content: ''; - position: absolute; - top: 0; left: 0; - width: 100%; height: 3px; - background: linear-gradient(90deg, var(--cyan), transparent); - opacity: 0.5; -} - -.stat-label { - font-family: var(--font-mono); - font-size: 12px; - color: var(--text-dim); - text-transform: uppercase; - letter-spacing: 1.5px; -} - -.stat-value { - font-family: var(--font-display); - font-size: 32px; - font-weight: 700; - margin-top: 4px; - color: var(--text); - letter-spacing: -0.02em; -} - -.stat-value.stat-sm { - font-size: 15px; - font-weight: 500; - font-family: var(--font-mono); -} - -/* ─── Tables ─── */ -table { width: 100%; border-collapse: collapse; } - -th { - text-align: left; - padding: 10px 16px; - background: var(--bg); - border-bottom: 1px solid var(--border); - font-family: var(--font-mono); - font-size: 12px; - text-transform: uppercase; - letter-spacing: 1px; - color: var(--text-dim); - font-weight: 600; - white-space: nowrap; -} - -td { - padding: 12px 16px; - border-bottom: 1px solid var(--border-subtle); - font-size: 14px; -} - -tbody tr { transition: background 0.1s; } -tbody tr:hover td { background: var(--cyan-dim); } -tbody tr:last-child td { border-bottom: none; } - -/* ─── Badges ─── */ -.badge { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 10px; - border-radius: 3px; - font-family: var(--font-mono); - font-size: 12px; - font-weight: 600; - letter-spacing: 0.5px; - text-transform: uppercase; -} - -.badge svg { width: 14px; height: 14px; flex-shrink: 0; } -.badge-cyan { background: var(--cyan-dim); color: var(--cyan); } -.badge-amber { background: var(--amber-dim); color: var(--amber); } -.badge-green { background: var(--green-dim); color: var(--green); } -.badge-count { - background: var(--bg); - color: var(--text-secondary); - border: 1px solid var(--border); -} - -/* ─── Buttons ─── */ -.btn { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 7px 14px; - border-radius: 4px; - font-family: var(--font-body); - font-size: 13px; - font-weight: 500; - cursor: pointer; - text-decoration: none; - border: 1px solid var(--border); - background: var(--surface); - color: var(--text-secondary); - transition: all 0.15s; - white-space: nowrap; -} - -.btn:hover { - background: var(--bg); - color: var(--text); - border-color: var(--text-dim); -} - -.btn svg { width: 14px; height: 14px; } - -.btn-cyan { - background: var(--cyan-dim); - color: var(--cyan); - border-color: rgba(9, 117, 176, 0.25); -} - -.btn-cyan:hover { - background: rgba(9, 117, 176, 0.15); - border-color: rgba(9, 117, 176, 0.4); - color: var(--cyan); -} - -.btn-amber { - background: var(--amber-dim); - color: var(--amber); - border-color: rgba(181, 71, 8, 0.25); -} - -.btn-amber:hover { - background: rgba(181, 71, 8, 0.15); - border-color: rgba(181, 71, 8, 0.4); - color: var(--amber); -} - -.btn-red { - background: rgba(217, 45, 32, 0.08); - color: var(--red); - border-color: rgba(217, 45, 32, 0.25); -} - -.btn-red:hover { - background: rgba(217, 45, 32, 0.15); - border-color: rgba(217, 45, 32, 0.4); - color: var(--red); -} - -.btn-sm { padding: 4px 10px; font-size: 12px; } - -/* ─── Search ─── */ -.search-box { - display: flex; - align-items: center; - gap: 8px; - background: var(--surface); - border: 1px solid var(--border); - border-radius: 4px; - padding: 0 12px; - height: 36px; - width: 300px; - transition: border-color 0.2s, box-shadow 0.2s; -} - -.search-box:focus-within { - border-color: var(--cyan); - box-shadow: 0 0 0 2px var(--cyan-dim); -} - -.search-box svg { - width: 16px; height: 16px; - color: var(--text-dim); - flex-shrink: 0; -} - -.search-box input { - border: none; - outline: none; - font-family: var(--font-body); - font-size: 14px; - width: 100%; - background: transparent; - color: var(--text); -} - -.search-box input::placeholder { color: var(--text-dim); } - -/* ─── Clickable ─── */ -.clickable { cursor: pointer; } - -/* ─── Detail sections ─── */ -.detail-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 20px; -} - -.detail-field label { - display: block; - font-family: var(--font-mono); - font-size: 12px; - text-transform: uppercase; - letter-spacing: 1.2px; - color: var(--text-dim); - margin-bottom: 4px; -} - -.detail-field .value { - font-size: 15px; - font-weight: 500; - word-break: break-all; -} - -.detail-field .value.mono { - font-family: var(--font-mono); - font-size: 13px; - color: var(--text-secondary); -} - -/* ─── Back link ─── */ -.back-link { - display: inline-flex; - align-items: center; - gap: 6px; - color: var(--text-dim); - text-decoration: none; - font-size: 13px; - cursor: pointer; - margin-bottom: 20px; - font-family: var(--font-mono); - text-transform: uppercase; - letter-spacing: 0.5px; - transition: color 0.15s; -} - -.back-link:hover { color: var(--cyan); } -.back-link svg { width: 14px; height: 14px; } - -/* ─── BOM Expansion ─── */ -.bom-expand-row td { - padding: 0 !important; - background: var(--bg) !important; -} - -.bom-expand-content { - padding: 16px 16px 16px 48px; - border-left: 3px solid var(--cyan-dim); - margin-left: 16px; -} - -.bom-expand-content .info-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 6px 24px; -} - -.bom-expand-content .info-item { - font-size: 13px; - padding: 2px 0; -} - -.bom-expand-content .info-item .lbl { - color: var(--text-dim); - font-family: var(--font-mono); - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.5px; - margin-right: 6px; -} - -.bom-expand-content .info-item .val { - font-family: var(--font-mono); - color: var(--text); -} - -.bom-section-title { - font-family: var(--font-mono); - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1.5px; - color: var(--cyan); - margin: 14px 0 8px; - display: flex; - align-items: center; - gap: 8px; -} - -.bom-section-title svg { width: 14px; height: 14px; flex-shrink: 0; } -.bom-section-title:first-child { margin-top: 0; } - -.bom-section-title::after { - content: ''; - flex: 1; - height: 1px; - background: var(--border-subtle); -} - -/* ─── File Browser ─── */ -.breadcrumb { - display: flex; - align-items: center; - gap: 6px; - font-family: var(--font-mono); - font-size: 13px; - margin-bottom: 16px; - flex-wrap: wrap; - padding: 8px 14px; - background: var(--surface); - border: 1px solid var(--border-subtle); - border-radius: 4px; -} - -.breadcrumb a { - color: var(--cyan); - text-decoration: none; - cursor: pointer; - transition: opacity 0.15s; -} - -.breadcrumb a:hover { opacity: 0.7; } -.breadcrumb .sep { color: var(--text-dim); font-size: 11px; } -.breadcrumb .current { color: var(--text); font-weight: 500; } - -.file-name-cell { - display: flex; - align-items: center; - gap: 10px; -} - -.file-name-cell svg { width: 18px; height: 18px; flex-shrink: 0; } - -.file-name-cell a { - color: var(--text); - text-decoration: none; - cursor: pointer; - transition: color 0.15s; -} - -.file-name-cell a:hover { color: var(--cyan); } - -/* ─── Loading / Empty ─── */ -.loading, .empty { - text-align: center; - padding: 60px 24px; - color: var(--text-dim); - font-size: 14px; - font-family: var(--font-mono); -} - -.loading::before { - content: ''; - display: block; - width: 24px; - height: 24px; - border: 2px solid var(--border); - border-top-color: var(--cyan); - border-radius: 50%; - animation: spin 0.8s linear infinite; - margin: 0 auto 12px; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -/* ─── Chevron toggle ─── */ -.chevron-toggle { - display: inline-flex; - width: 18px; height: 18px; - align-items: center; - justify-content: center; - transition: transform 0.2s; - color: var(--text-dim); -} - -.chevron-toggle.open { - transform: rotate(90deg); - color: var(--cyan); -} - -/* ─── Drawing cards grid ─── */ -.drawings-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - gap: 12px; -} - -.drawing-card { - background: var(--surface); - border: 1px solid var(--border-subtle); - border-radius: 6px; - padding: 18px 20px; - cursor: pointer; - transition: all 0.2s; - position: relative; - box-shadow: 0 1px 2px rgba(0,0,0,0.05); -} - -.drawing-card:hover { - border-color: var(--cyan); - background: var(--cyan-dim); - box-shadow: 0 2px 8px rgba(9, 117, 176, 0.1); -} - -.drawing-card-title { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; - margin-bottom: 2px; -} - -.drawing-card-sub { - font-family: var(--font-mono); - font-size: 12px; - color: var(--text-dim); - text-transform: uppercase; - letter-spacing: 1px; -} - -/* ─── Equipment Groups ─── */ -.equip-group { - margin-bottom: 16px; -} - -.equip-group:last-child { margin-bottom: 0; } - -.equip-header { - display: flex; - align-items: center; - gap: 10px; - padding: 12px 16px; - background: var(--surface); - border: 1px solid var(--border-subtle); - border-radius: 6px 6px 0 0; - cursor: pointer; - transition: all 0.15s; - user-select: none; - box-shadow: 0 1px 2px rgba(0,0,0,0.05); -} - -.equip-header:hover { background: var(--cyan-dim); } - -.equip-header .chevron-toggle { flex-shrink: 0; } - -.equip-header-title { - font-family: var(--font-display); - font-size: 16px; - font-weight: 600; -} - -.equip-header-number { - font-family: var(--font-mono); - font-size: 15px; - color: var(--cyan); - font-weight: 600; -} - -.equip-header-meta { - margin-left: auto; - display: flex; - align-items: center; - gap: 12px; -} - -.equip-header-stat { - font-family: var(--font-mono); - font-size: 13px; - color: var(--text-dim); -} - -.equip-header-stat strong { - color: var(--text-secondary); -} - -.equip-body { - border: 1px solid var(--border-subtle); - border-top: none; - border-radius: 0 0 6px 6px; - overflow: hidden; -} - -.equip-body table { margin: 0; } - -.equip-group.collapsed .equip-body { display: none; } -.equip-group.collapsed .equip-header { border-radius: 6px; } - -/* ─── Responsive ─── */ -@media (max-width: 768px) { - .sidebar { display: none; } - .main { margin-left: 0; } - .search-box { width: 100%; } - .topbar { padding: 0 16px; } - .page-content { padding: 16px; } -} diff --git a/FabWorks.Api/wwwroot/index.html b/FabWorks.Api/wwwroot/index.html deleted file mode 100644 index 4b1b4c4..0000000 --- a/FabWorks.Api/wwwroot/index.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - FabWorks - - - - - - - - -
-
-
-

Exports

- -
-
-
-
-
- - - - - - - - - diff --git a/FabWorks.Api/wwwroot/js/components.js b/FabWorks.Api/wwwroot/js/components.js deleted file mode 100644 index fac1db5..0000000 --- a/FabWorks.Api/wwwroot/js/components.js +++ /dev/null @@ -1,60 +0,0 @@ -/* ─── BOM Detail Expansion ─── */ -function renderBomDetails(b) { - let html = '
'; - - if (b.cutTemplate) { - const ct = b.cutTemplate; - const displayName = ct.dxfFilePath?.split(/[/\\]/).pop() || ''; - html += ` -
${icons.laser} Cut Template
-
-
File${esc(displayName)}
-
Thickness${fmtThickness(ct.thickness)}
-
K-Factor${ct.kFactor != null ? ct.kFactor : '\u2014'}
-
Bend Radius${ct.defaultBendRadius != null ? ct.defaultBendRadius.toFixed(4) + '"' : '\u2014'}
-
`; - - if (ct.contentHash) { - html += `
- ${icons.download} Download DXF - ${esc(displayName)} -
`; - } - } - - if (b.formProgram) { - const fp = b.formProgram; - html += ` -
${icons.bend} Form Program
-
-
Program${esc(fp.programName)}
-
Thickness${fmtThickness(fp.thickness)}
-
Material${esc(fp.materialType)}
-
K-Factor${fp.kFactor != null ? fp.kFactor : '\u2014'}
-
Bends${fp.bendCount}
-
Upper Tools${esc(fp.upperToolNames) || '\u2014'}
-
Lower Tools${esc(fp.lowerToolNames) || '\u2014'}
-
- ${fp.setupNotes ? `
Setup Notes${esc(fp.setupNotes)}
` : ''}`; - } - - html += '
'; - return html; -} - -function toggleEquipGroup(id) { - const group = document.getElementById(id); - const icon = document.getElementById(id + '-icon'); - if (!group) return; - group.classList.toggle('collapsed'); - if (icon) icon.classList.toggle('open', !group.classList.contains('collapsed')); -} - -function toggleBomRow(id) { - const row = document.getElementById(id); - const icon = document.getElementById(id + '-icon'); - if (!row) return; - const visible = row.style.display !== 'none'; - row.style.display = visible ? 'none' : ''; - if (icon) icon.classList.toggle('open', !visible); -} diff --git a/FabWorks.Api/wwwroot/js/helpers.js b/FabWorks.Api/wwwroot/js/helpers.js deleted file mode 100644 index 787225f..0000000 --- a/FabWorks.Api/wwwroot/js/helpers.js +++ /dev/null @@ -1,50 +0,0 @@ -function fmtSize(b) { - if (!b) return '0 B'; - const k = 1024, s = ['B','KB','MB','GB']; - const i = Math.floor(Math.log(b) / Math.log(k)); - return parseFloat((b / Math.pow(k, i)).toFixed(1)) + ' ' + s[i]; -} - -function fmtDate(d) { - if (!d) return ''; - const dt = new Date(d); - return dt.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) + - ' ' + dt.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); -} - -function fmtThickness(t) { - if (t == null) return '\u2014'; - return `${t.toFixed(4)}"`; -} - -function esc(s) { - return s ? s.replace(//g,'>').replace(/"/g,'"').replace(/'/g,''') : ''; -} - -function setPage(title, tag = '') { - document.getElementById('page-title').textContent = title; - document.getElementById('page-tag').textContent = tag; - document.getElementById('page-tag').style.display = tag ? '' : 'none'; -} - -const api = { - async get(url) { - const r = await fetch(url); - if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); - return r.json(); - }, - async del(url) { - const r = await fetch(url, { method: 'DELETE' }); - if (!r.ok) throw new Error(`${r.status} ${r.statusText}`); - } -}; - -async function deleteExport(id) { - if (!confirm('Delete this export record? This cannot be undone.')) return; - try { - await api.del(`/api/exports/${id}`); - router.dispatch(); - } catch (err) { - alert('Failed to delete: ' + err.message); - } -} diff --git a/FabWorks.Api/wwwroot/js/icons.js b/FabWorks.Api/wwwroot/js/icons.js deleted file mode 100644 index 5af4ad1..0000000 --- a/FabWorks.Api/wwwroot/js/icons.js +++ /dev/null @@ -1,20 +0,0 @@ -const icons = { - search: ``, - folder: ``, - fileDxf: ``, - filePdf: ``, - fileGeneric: ``, - download: ``, - back: ``, - chevron: ``, - laser: ``, - bend: ``, - trash: ``, -}; - -function fileIcon(name) { - const ext = name.split('.').pop().toLowerCase(); - if (ext === 'dxf') return icons.fileDxf; - if (ext === 'pdf') return icons.filePdf; - return icons.fileGeneric; -} diff --git a/FabWorks.Api/wwwroot/js/pages.js b/FabWorks.Api/wwwroot/js/pages.js deleted file mode 100644 index c89b136..0000000 --- a/FabWorks.Api/wwwroot/js/pages.js +++ /dev/null @@ -1,428 +0,0 @@ -const pages = { - async exports(params) { - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage('Exports'); - - const searchVal = params.q || ''; - actions.innerHTML = ` - `; - - content.innerHTML = `
Loading exports
`; - - const searchInput = document.getElementById('export-search'); - let debounce; - searchInput.addEventListener('input', () => { - clearTimeout(debounce); - debounce = setTimeout(() => router.go('exports', { q: searchInput.value }), 400); - }); - - try { - const searchQ = searchVal ? `&search=${encodeURIComponent(searchVal)}` : ''; - const data = await api.get(`/api/exports?take=500${searchQ}`); - - if (data.items.length === 0) { - content.innerHTML = `
No exports found.
`; - return; - } - - setPage('Exports', `${data.items.length} exports`); - - const rows = data.items.map((e, i) => ` - - ${e.id} - ${esc(e.drawingNumber) || '\u2014'} - ${esc(e.title) || ''} - ${e.bomItemCount} - ${esc(e.exportedBy)} - ${fmtDate(e.exportedAt)} - - `).join(''); - - content.innerHTML = ` -
- - - - - - - - - - - ${rows} -
#DrawingTitleItemsExported ByDate
-
`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - }, - - async exportDetail(id) { - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage('Loading...'); - actions.innerHTML = ''; - content.innerHTML = `
Loading export
`; - - try { - const exp = await api.get(`/api/exports/${id}`); - setPage(exp.drawingNumber || `Export #${exp.id}`, 'export detail'); - - const dxfCount = (exp.bomItems || []).filter(b => b.cutTemplate?.contentHash).length; - - const bomRows = (exp.bomItems || []).map((b, i) => { - const hasDetails = b.cutTemplate || b.formProgram; - const toggleId = `bom-${b.id}`; - return ` - - ${hasDetails ? `${icons.chevron}` : ''} - ${esc(b.itemNo)} - ${esc(b.partName)} - ${esc(b.description)} - ${esc(b.material)} - ${b.qty ?? ''} - ${b.totalQty ?? ''} - - ${b.cutTemplate ? `${icons.laser} DXF` : ''} - ${b.formProgram ? `${icons.bend} Form` : ''} - - - ${hasDetails ? `${renderBomDetails(b)}` : ''}`; - }).join(''); - - content.innerHTML = ` - ${icons.back} Back to exports - -
-
Export Information
-
-
-
${esc(exp.drawingNumber) || '\u2014'}
- ${exp.title ? `
${esc(exp.title)}
` : ''} -
${esc(exp.exportedBy)}
-
${fmtDate(exp.exportedAt)}
-
${esc(exp.sourceFilePath)}
-
-
-
- -
-
- BOM Items - ${exp.bomItems?.length || 0} items - - ${exp.pdfContentHash ? `${icons.download} PDF` : ''} - ${dxfCount > 0 ? `${icons.download} All DXFs` : ''} - - -
- ${exp.bomItems?.length ? ` - - - - - - - - - - - - ${bomRows} -
ItemPart NameDescriptionMaterialQtyTotalData
` : '
No BOM items for this export.
'} -
`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - }, - - async drawings(params) { - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage('Drawings'); - - const searchVal = (params && params.q) || ''; - actions.innerHTML = ` - `; - - content.innerHTML = `
Loading drawings
`; - - const searchInput = document.getElementById('drawing-search'); - let debounce; - searchInput.addEventListener('input', () => { - clearTimeout(debounce); - debounce = setTimeout(() => router.go('drawings', { q: searchInput.value }), 400); - }); - - try { - const searchQ = searchVal ? `&search=${encodeURIComponent(searchVal)}` : ''; - const data = await api.get(`/api/exports?take=500${searchQ}`); - - if (data.items.length === 0) { - content.innerHTML = `
No drawings found.
`; - return; - } - - // Deduplicate: keep only the latest export per drawing number - const seen = new Set(); - const unique = data.items.filter(e => { - const dn = e.drawingNumber || ''; - if (seen.has(dn)) return false; - seen.add(dn); - return true; - }); - - // Group by equipment number (first token of drawing number) - const groups = new Map(); - unique.forEach(e => { - const dn = e.drawingNumber || ''; - const spaceIdx = dn.indexOf(' '); - const equip = spaceIdx > 0 ? dn.substring(0, spaceIdx) : (dn || 'Other'); - if (!groups.has(equip)) groups.set(equip, []); - groups.get(equip).push(e); - }); - - // Sort equipment groups by number descending (most recent equipment first) - const sortedGroups = [...groups.entries()].sort((a, b) => { - const numA = parseInt(a[0]) || 0; - const numB = parseInt(b[0]) || 0; - return numB - numA; - }); - - const uniqueEquip = sortedGroups.length; - const uniqueDrawings = unique.length; - setPage('Drawings', `${uniqueDrawings} drawings / ${uniqueEquip} equipment`); - - const groupsHtml = sortedGroups.map(([equip, items], gi) => { - const totalBom = items.reduce((s, e) => s + e.bomItemCount, 0); - - const rows = items.map((e, i) => { - const dn = e.drawingNumber || ''; - const spaceIdx = dn.indexOf(' '); - const drawingPart = spaceIdx > 0 ? dn.substring(spaceIdx + 1) : dn; - - return ` - - ${esc(drawingPart) || '\u2014'} - ${esc(e.title) || ''} - ${e.bomItemCount} - ${esc(e.exportedBy)} - ${fmtDate(e.exportedAt)} - `; - }).join(''); - - return ` -
-
- ${icons.chevron} - ${esc(equip)} -
- ${items.length} drawings - ${totalBom} items -
-
-
- - - - - - - - - ${rows} -
DrawingTitleItemsExported ByLatest Export
-
-
`; - }).join(''); - - content.innerHTML = ` -
-
Drawings
${uniqueDrawings}
-
Equipment
${uniqueEquip}
-
- ${groupsHtml}`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - }, - - async drawingDetail(drawingEncoded) { - const drawingNumber = decodeURIComponent(drawingEncoded); - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage(drawingNumber, 'drawing'); - actions.innerHTML = ''; - content.innerHTML = `
Loading drawing
`; - - try { - const exports = await api.get(`/api/exports/by-drawing?drawingNumber=${encodeURIComponent(drawingNumber)}`); - - if (exports.length === 0) { - content.innerHTML = ` - ${icons.back} Back to drawings -
No exports found for this drawing.
`; - return; - } - - const allBom = []; - exports.forEach(exp => { - (exp.bomItems || []).forEach(b => { - allBom.push({ ...b, exportId: exp.id, exportedAt: exp.exportedAt }); - }); - }); - - const bomRows = allBom.map((b, i) => { - const hasDetails = b.cutTemplate || b.formProgram; - const toggleId = `dbom-${b.id}`; - return ` - - ${hasDetails ? `${icons.chevron}` : ''} - ${esc(b.itemNo)} - ${esc(b.partName)} - ${esc(b.description)} - ${esc(b.material)} - ${b.qty ?? ''} - ${b.totalQty ?? ''} - - ${b.cutTemplate ? `${icons.laser} DXF` : ''} - ${b.formProgram ? `${icons.bend} Form` : ''} - - - ${hasDetails ? `${renderBomDetails(b)}` : ''}`; - }).join(''); - - content.innerHTML = ` - ${icons.back} Back to drawings - -
-
Exports
${exports.length}
-
BOM Items
${allBom.length}
-
Latest Export
${fmtDate(exports[0].exportedAt)}
-
- -
-
- All BOM Items - ${allBom.length} items -
- ${allBom.length ? ` - - - - - - - - - - - - ${bomRows} -
ItemPart NameDescriptionMaterialQtyTotalData
` : '
No BOM items.
'} -
`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - }, - - async files(params) { - const actions = document.getElementById('topbar-actions'); - const content = document.getElementById('page-content'); - setPage('Files'); - - const searchVal = params.q || ''; - actions.innerHTML = ` -
- - -
`; - - content.innerHTML = `
Loading files
`; - - const searchInput = document.getElementById('file-search'); - const typeFilter = document.getElementById('file-type-filter'); - let debounce; - const refresh = () => { - clearTimeout(debounce); - debounce = setTimeout(() => router.go('files', { q: searchInput.value + (typeFilter.value ? '&type=' + typeFilter.value : '') }), 400); - }; - searchInput.addEventListener('input', refresh); - typeFilter.addEventListener('change', refresh); - - // Parse search and type from combined param - let searchQ = searchVal; - let typeQ = ''; - if (searchVal.includes('&type=')) { - const parts = searchVal.split('&type='); - searchQ = parts[0]; - typeQ = parts[1] || ''; - searchInput.value = searchQ; - typeFilter.value = typeQ; - } - - try { - let url = '/api/filebrowser/files?'; - if (searchQ) url += `search=${encodeURIComponent(searchQ)}&`; - if (typeQ) url += `type=${encodeURIComponent(typeQ)}&`; - const data = await api.get(url); - - setPage('Files', `${data.total} files`); - - if (data.files.length === 0) { - content.innerHTML = `
No files found.
`; - return; - } - - const rows = data.files.map((f, i) => { - const ext = f.fileType || f.fileName.split('.').pop().toLowerCase(); - const hashShort = f.contentHash ? f.contentHash.substring(0, 12) : ''; - return ` - -
${ext === 'pdf' ? icons.filePdf : icons.fileDxf}${esc(f.fileName)}
- ${ext.toUpperCase()} - ${esc(f.drawingNumber)} - ${f.thickness != null ? f.thickness.toFixed(4) + '"' : '\u2014'} - ${fmtDate(f.createdAt)} - ${esc(hashShort)} - - ${icons.download} - - `; - }).join(''); - - content.innerHTML = ` -
- - - - - - - - - - - ${rows} -
NameTypeDrawingThicknessDateHashActions
-
`; - } catch (err) { - content.innerHTML = `
Error: ${esc(err.message)}
`; - } - } -}; diff --git a/FabWorks.Api/wwwroot/js/router.js b/FabWorks.Api/wwwroot/js/router.js deleted file mode 100644 index 66fdfc1..0000000 --- a/FabWorks.Api/wwwroot/js/router.js +++ /dev/null @@ -1,35 +0,0 @@ -const router = { - go(page, params = {}) { - const hash = page + (params.id ? '/' + params.id : '') + (params.q ? '?q=' + encodeURIComponent(params.q) : ''); - location.hash = hash; - }, - parse() { - const h = location.hash.slice(1) || 'exports'; - const [path, qs] = h.split('?'); - const parts = path.split('/'); - const params = {}; - if (qs) qs.split('&').forEach(p => { const [k,v] = p.split('='); params[k] = decodeURIComponent(v); }); - return { page: parts[0], id: parts[1], params }; - }, - init() { - window.addEventListener('hashchange', () => this.dispatch()); - this.dispatch(); - }, - dispatch() { - const { page, id, params } = this.parse(); - document.querySelectorAll('.nav-item').forEach(el => { - el.classList.toggle('active', - el.dataset.page === page || - (page === 'export-detail' && el.dataset.page === 'exports') || - (page === 'drawing-detail' && el.dataset.page === 'drawings')); - }); - switch(page) { - case 'exports': pages.exports(params); break; - case 'export-detail': pages.exportDetail(id); break; - case 'drawings': pages.drawings(params); break; - case 'drawing-detail': pages.drawingDetail(id, params); break; - case 'files': pages.files(params); break; - default: pages.exports(params); - } - } -}; diff --git a/FabWorks.Core/Data/FabWorksDbContext.cs b/FabWorks.Core/Data/FabWorksDbContext.cs deleted file mode 100644 index f2d6a40..0000000 --- a/FabWorks.Core/Data/FabWorksDbContext.cs +++ /dev/null @@ -1,79 +0,0 @@ -using FabWorks.Core.Models; -using Microsoft.EntityFrameworkCore; - -namespace FabWorks.Core.Data -{ - public class FabWorksDbContext : DbContext - { - public DbSet ExportRecords { get; set; } - public DbSet BomItems { get; set; } - public DbSet CutTemplates { get; set; } - public DbSet FormPrograms { get; set; } - - public FabWorksDbContext(DbContextOptions options) : base(options) { } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.Id); - entity.Property(e => e.DrawingNumber).HasMaxLength(100); - entity.Property(e => e.Title).HasMaxLength(200); - entity.Property(e => e.EquipmentNo).HasMaxLength(50); - entity.Property(e => e.DrawingNo).HasMaxLength(50); - entity.Property(e => e.SourceFilePath).HasMaxLength(500); - entity.Property(e => e.OutputFolder).HasMaxLength(500); - entity.Property(e => e.ExportedBy).HasMaxLength(100); - entity.Property(e => e.PdfContentHash).HasMaxLength(64); - - entity.HasMany(e => e.BomItems) - .WithOne(b => b.ExportRecord) - .HasForeignKey(b => b.ExportRecordId) - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.ID); - entity.Property(e => e.ItemNo).HasMaxLength(50); - entity.Property(e => e.PartNo).HasMaxLength(100); - entity.Property(e => e.Description).HasMaxLength(500); - entity.Property(e => e.PartName).HasMaxLength(200); - entity.Property(e => e.ConfigurationName).HasMaxLength(100); - entity.Property(e => e.Material).HasMaxLength(100); - - entity.HasOne(e => e.CutTemplate) - .WithOne(ct => ct.BomItem) - .HasForeignKey(ct => ct.BomItemId) - .OnDelete(DeleteBehavior.Cascade); - - entity.HasOne(e => e.FormProgram) - .WithOne(fp => fp.BomItem) - .HasForeignKey(fp => fp.BomItemId) - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.Id); - entity.Property(e => e.DxfFilePath).HasMaxLength(500); - entity.Property(e => e.CutTemplateName).HasMaxLength(100); - entity.Property(e => e.ContentHash).HasMaxLength(64); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.Id); - entity.Property(e => e.ProgramFilePath).HasMaxLength(500); - entity.Property(e => e.ContentHash).HasMaxLength(64); - entity.Property(e => e.ProgramName).HasMaxLength(200); - entity.Property(e => e.MaterialType).HasMaxLength(50); - entity.Property(e => e.UpperToolNames).HasMaxLength(500); - entity.Property(e => e.LowerToolNames).HasMaxLength(500); - entity.Property(e => e.SetupNotes).HasMaxLength(2000); - }); - } - } -} diff --git a/FabWorks.Core/FabWorks.Core.csproj b/FabWorks.Core/FabWorks.Core.csproj deleted file mode 100644 index a82a833..0000000 --- a/FabWorks.Core/FabWorks.Core.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net8.0 - disable - disable - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/FabWorks.Core/Migrations/20260218171742_InitialCreate.Designer.cs b/FabWorks.Core/Migrations/20260218171742_InitialCreate.Designer.cs deleted file mode 100644 index 2b8a06c..0000000 --- a/FabWorks.Core/Migrations/20260218171742_InitialCreate.Designer.cs +++ /dev/null @@ -1,270 +0,0 @@ -// -using System; -using FabWorks.Core.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FabWorks.Core.Migrations -{ - [DbContext(typeof(FabWorksDbContext))] - [Migration("20260218171742_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); - - b.Property("ConfigurationName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ExportRecordId") - .HasColumnType("int"); - - b.Property("ItemNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Material") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("PartName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("PartNo") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Qty") - .HasColumnType("int"); - - b.Property("SortOrder") - .HasColumnType("int"); - - b.Property("TotalQty") - .HasColumnType("int"); - - b.HasKey("ID"); - - b.HasIndex("ExportRecordId"); - - b.ToTable("BomItems"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("CutTemplateName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("DefaultBendRadius") - .HasColumnType("float"); - - b.Property("DxfFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("CutTemplates"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("DrawingNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("DrawingNumber") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EquipmentNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ExportedAt") - .HasColumnType("datetime2"); - - b.Property("ExportedBy") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("OutputFolder") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("PdfContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("SourceFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.HasKey("Id"); - - b.ToTable("ExportRecords"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BendCount") - .HasColumnType("int"); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("LowerToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("MaterialType") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProgramFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ProgramName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("SetupNotes") - .HasMaxLength(2000) - .HasColumnType("nvarchar(2000)"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.Property("UpperToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("FormPrograms"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.HasOne("FabWorks.Core.Models.ExportRecord", "ExportRecord") - .WithMany("BomItems") - .HasForeignKey("ExportRecordId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ExportRecord"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("CutTemplate") - .HasForeignKey("FabWorks.Core.Models.CutTemplate", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("FormProgram") - .HasForeignKey("FabWorks.Core.Models.FormProgram", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Navigation("CutTemplate"); - - b.Navigation("FormProgram"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Navigation("BomItems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/FabWorks.Core/Migrations/20260218171742_InitialCreate.cs b/FabWorks.Core/Migrations/20260218171742_InitialCreate.cs deleted file mode 100644 index 77ae070..0000000 --- a/FabWorks.Core/Migrations/20260218171742_InitialCreate.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FabWorks.Core.Migrations -{ - /// - public partial class InitialCreate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ExportRecords", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - DrawingNumber = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - Title = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), - EquipmentNo = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - DrawingNo = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - SourceFilePath = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - OutputFolder = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - ExportedAt = table.Column(type: "datetime2", nullable: false), - ExportedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - PdfContentHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_ExportRecords", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "BomItems", - columns: table => new - { - ID = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - ItemNo = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - PartNo = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - SortOrder = table.Column(type: "int", nullable: false), - Qty = table.Column(type: "int", nullable: true), - TotalQty = table.Column(type: "int", nullable: true), - Description = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - PartName = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), - ConfigurationName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - Material = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - ExportRecordId = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_BomItems", x => x.ID); - table.ForeignKey( - name: "FK_BomItems_ExportRecords_ExportRecordId", - column: x => x.ExportRecordId, - principalTable: "ExportRecords", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "CutTemplates", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - DxfFilePath = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - ContentHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - CutTemplateName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), - Thickness = table.Column(type: "float", nullable: true), - KFactor = table.Column(type: "float", nullable: true), - DefaultBendRadius = table.Column(type: "float", nullable: true), - BomItemId = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CutTemplates", x => x.Id); - table.ForeignKey( - name: "FK_CutTemplates_BomItems_BomItemId", - column: x => x.BomItemId, - principalTable: "BomItems", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "FormPrograms", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - ProgramFilePath = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - ContentHash = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - ProgramName = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: true), - Thickness = table.Column(type: "float", nullable: true), - MaterialType = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), - KFactor = table.Column(type: "float", nullable: true), - BendCount = table.Column(type: "int", nullable: false), - UpperToolNames = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - LowerToolNames = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: true), - SetupNotes = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: true), - BomItemId = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_FormPrograms", x => x.Id); - table.ForeignKey( - name: "FK_FormPrograms_BomItems_BomItemId", - column: x => x.BomItemId, - principalTable: "BomItems", - principalColumn: "ID", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_BomItems_ExportRecordId", - table: "BomItems", - column: "ExportRecordId"); - - migrationBuilder.CreateIndex( - name: "IX_CutTemplates_BomItemId", - table: "CutTemplates", - column: "BomItemId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_FormPrograms_BomItemId", - table: "FormPrograms", - column: "BomItemId", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "CutTemplates"); - - migrationBuilder.DropTable( - name: "FormPrograms"); - - migrationBuilder.DropTable( - name: "BomItems"); - - migrationBuilder.DropTable( - name: "ExportRecords"); - } - } -} diff --git a/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.Designer.cs b/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.Designer.cs deleted file mode 100644 index 59ad995..0000000 --- a/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.Designer.cs +++ /dev/null @@ -1,273 +0,0 @@ -// -using System; -using FabWorks.Core.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FabWorks.Core.Migrations -{ - [DbContext(typeof(FabWorksDbContext))] - [Migration("20260219134027_AddCutTemplateRevision")] - partial class AddCutTemplateRevision - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); - - b.Property("ConfigurationName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ExportRecordId") - .HasColumnType("int"); - - b.Property("ItemNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Material") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("PartName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("PartNo") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Qty") - .HasColumnType("int"); - - b.Property("SortOrder") - .HasColumnType("int"); - - b.Property("TotalQty") - .HasColumnType("int"); - - b.HasKey("ID"); - - b.HasIndex("ExportRecordId"); - - b.ToTable("BomItems"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("CutTemplateName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("DefaultBendRadius") - .HasColumnType("float"); - - b.Property("DxfFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("Revision") - .HasColumnType("int"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("CutTemplates"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("DrawingNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("DrawingNumber") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EquipmentNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ExportedAt") - .HasColumnType("datetime2"); - - b.Property("ExportedBy") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("OutputFolder") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("PdfContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("SourceFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.HasKey("Id"); - - b.ToTable("ExportRecords"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BendCount") - .HasColumnType("int"); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("LowerToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("MaterialType") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProgramFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ProgramName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("SetupNotes") - .HasMaxLength(2000) - .HasColumnType("nvarchar(2000)"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.Property("UpperToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("FormPrograms"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.HasOne("FabWorks.Core.Models.ExportRecord", "ExportRecord") - .WithMany("BomItems") - .HasForeignKey("ExportRecordId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ExportRecord"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("CutTemplate") - .HasForeignKey("FabWorks.Core.Models.CutTemplate", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("FormProgram") - .HasForeignKey("FabWorks.Core.Models.FormProgram", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Navigation("CutTemplate"); - - b.Navigation("FormProgram"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Navigation("BomItems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.cs b/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.cs deleted file mode 100644 index 4f8d959..0000000 --- a/FabWorks.Core/Migrations/20260219134027_AddCutTemplateRevision.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace FabWorks.Core.Migrations -{ - /// - public partial class AddCutTemplateRevision : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Revision", - table: "CutTemplates", - type: "int", - nullable: false, - defaultValue: 0); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Revision", - table: "CutTemplates"); - } - } -} diff --git a/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs b/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs deleted file mode 100644 index c6ebca9..0000000 --- a/FabWorks.Core/Migrations/FabWorksDbContextModelSnapshot.cs +++ /dev/null @@ -1,270 +0,0 @@ -// -using System; -using FabWorks.Core.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace FabWorks.Core.Migrations -{ - [DbContext(typeof(FabWorksDbContext))] - partial class FabWorksDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "8.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Property("ID") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ID")); - - b.Property("ConfigurationName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ExportRecordId") - .HasColumnType("int"); - - b.Property("ItemNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("Material") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("PartName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("PartNo") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("Qty") - .HasColumnType("int"); - - b.Property("SortOrder") - .HasColumnType("int"); - - b.Property("TotalQty") - .HasColumnType("int"); - - b.HasKey("ID"); - - b.HasIndex("ExportRecordId"); - - b.ToTable("BomItems"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("CutTemplateName") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("DefaultBendRadius") - .HasColumnType("float"); - - b.Property("DxfFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("Revision") - .HasColumnType("int"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("CutTemplates"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("DrawingNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("DrawingNumber") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("EquipmentNo") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ExportedAt") - .HasColumnType("datetime2"); - - b.Property("ExportedBy") - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("OutputFolder") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("PdfContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("SourceFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("Title") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.HasKey("Id"); - - b.ToTable("ExportRecords"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("BendCount") - .HasColumnType("int"); - - b.Property("BomItemId") - .HasColumnType("int"); - - b.Property("ContentHash") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("KFactor") - .HasColumnType("float"); - - b.Property("LowerToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("MaterialType") - .HasMaxLength(50) - .HasColumnType("nvarchar(50)"); - - b.Property("ProgramFilePath") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.Property("ProgramName") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("SetupNotes") - .HasMaxLength(2000) - .HasColumnType("nvarchar(2000)"); - - b.Property("Thickness") - .HasColumnType("float"); - - b.Property("UpperToolNames") - .HasMaxLength(500) - .HasColumnType("nvarchar(500)"); - - b.HasKey("Id"); - - b.HasIndex("BomItemId") - .IsUnique(); - - b.ToTable("FormPrograms"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.HasOne("FabWorks.Core.Models.ExportRecord", "ExportRecord") - .WithMany("BomItems") - .HasForeignKey("ExportRecordId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ExportRecord"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.CutTemplate", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("CutTemplate") - .HasForeignKey("FabWorks.Core.Models.CutTemplate", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.FormProgram", b => - { - b.HasOne("FabWorks.Core.Models.BomItem", "BomItem") - .WithOne("FormProgram") - .HasForeignKey("FabWorks.Core.Models.FormProgram", "BomItemId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BomItem"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.BomItem", b => - { - b.Navigation("CutTemplate"); - - b.Navigation("FormProgram"); - }); - - modelBuilder.Entity("FabWorks.Core.Models.ExportRecord", b => - { - b.Navigation("BomItems"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/FabWorks.Core/Models/BomItem.cs b/FabWorks.Core/Models/BomItem.cs deleted file mode 100644 index 863eb47..0000000 --- a/FabWorks.Core/Models/BomItem.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace FabWorks.Core.Models -{ - public class BomItem - { - public int ID { get; set; } - public string ItemNo { get; set; } = ""; - public string PartNo { get; set; } = ""; - public int SortOrder { get; set; } - public int? Qty { get; set; } - public int? TotalQty { get; set; } - public string Description { get; set; } = ""; - public string PartName { get; set; } = ""; - public string ConfigurationName { get; set; } = ""; - public string Material { get; set; } = ""; - - public int ExportRecordId { get; set; } - public virtual ExportRecord ExportRecord { get; set; } - - public virtual CutTemplate CutTemplate { get; set; } - public virtual FormProgram FormProgram { get; set; } - } -} diff --git a/FabWorks.Core/Models/CutTemplate.cs b/FabWorks.Core/Models/CutTemplate.cs deleted file mode 100644 index db0c9eb..0000000 --- a/FabWorks.Core/Models/CutTemplate.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace FabWorks.Core.Models -{ - public class CutTemplate - { - public int Id { get; set; } - public string DxfFilePath { get; set; } = ""; - public string ContentHash { get; set; } - public string CutTemplateName { get; set; } = ""; - - private double? _thickness; - public double? Thickness - { - get => _thickness; - set => _thickness = value.HasValue ? Math.Round(value.Value, 8) : null; - } - - public int Revision { get; set; } = 1; - - public double? KFactor { get; set; } - - private double? _defaultBendRadius; - public double? DefaultBendRadius - { - get => _defaultBendRadius; - set => _defaultBendRadius = value.HasValue ? Math.Round(value.Value, 8) : null; - } - - public int BomItemId { get; set; } - public virtual BomItem BomItem { get; set; } - } -} diff --git a/FabWorks.Core/Models/ExportRecord.cs b/FabWorks.Core/Models/ExportRecord.cs deleted file mode 100644 index 29b5937..0000000 --- a/FabWorks.Core/Models/ExportRecord.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace FabWorks.Core.Models -{ - public class ExportRecord - { - public int Id { get; set; } - public string DrawingNumber { get; set; } - public string Title { get; set; } - public string EquipmentNo { get; set; } - public string DrawingNo { get; set; } - public string SourceFilePath { get; set; } - public string OutputFolder { get; set; } - public DateTime ExportedAt { get; set; } - public string ExportedBy { get; set; } - public string PdfContentHash { get; set; } - - public virtual ICollection BomItems { get; set; } = new List(); - } -} diff --git a/FabWorks.Core/Models/FormProgram.cs b/FabWorks.Core/Models/FormProgram.cs deleted file mode 100644 index feaa03f..0000000 --- a/FabWorks.Core/Models/FormProgram.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace FabWorks.Core.Models -{ - public class FormProgram - { - public int Id { get; set; } - public string ProgramFilePath { get; set; } = ""; - public string ContentHash { get; set; } - public string ProgramName { get; set; } = ""; - public double? Thickness { get; set; } - public string MaterialType { get; set; } = ""; - public double? KFactor { get; set; } - public int BendCount { get; set; } - public string UpperToolNames { get; set; } = ""; - public string LowerToolNames { get; set; } = ""; - public string SetupNotes { get; set; } = ""; - - public int BomItemId { get; set; } - public virtual BomItem BomItem { get; set; } - } -} diff --git a/FabWorks.Core/PressBrake/Extensions.cs b/FabWorks.Core/PressBrake/Extensions.cs deleted file mode 100644 index d86ce26..0000000 --- a/FabWorks.Core/PressBrake/Extensions.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Xml.Linq; - -namespace FabWorks.Core.PressBrake -{ - internal static class Extensions - { - private static bool? ToBool(this string s) - { - if (string.IsNullOrWhiteSpace(s)) - return null; - - int intValue; - - if (!int.TryParse(s, out intValue)) - return null; - - return Convert.ToBoolean(intValue); - } - - public static bool ToBool(this XAttribute a, bool defaultValue = false) - { - if (a == null) - return defaultValue; - - var b = a.Value.ToBool(); - - return b != null ? b.Value : defaultValue; - } - - public static bool? ToBoolOrNull(this XAttribute a) - { - if (a == null) - return null; - - return a.Value.ToBool(); - } - - private static int? ToInt(this string s) - { - if (string.IsNullOrWhiteSpace(s)) - return null; - - int intValue; - - if (!int.TryParse(s, out intValue)) - return null; - - return intValue; - } - - public static int ToInt(this XAttribute a, int defaultValue = 0) - { - if (a == null) - return defaultValue; - - var b = a.Value.ToInt(); - - return b != null ? b.Value : defaultValue; - } - - public static int? ToIntOrNull(this XAttribute a) - { - if (a == null) - return null; - - return a.Value.ToInt(); - } - - public static int ToInt(this XElement a, int defaultValue = 0) - { - if (a == null) - return defaultValue; - - var b = a.Value.ToInt(); - - return b != null ? b.Value : defaultValue; - } - - public static int? ToIntOrNull(this XElement a) - { - if (a == null) - return null; - - return a.Value.ToInt(); - } - - private static double? ToDouble(this string s) - { - if (string.IsNullOrWhiteSpace(s)) - return null; - - double d; - - if (!double.TryParse(s, out d)) - return null; - - return d; - } - - public static double ToDouble(this XAttribute a, double defaultValue = 0) - { - if (a == null) - return defaultValue; - - var b = a.Value.ToDouble(); - - return b != null ? b.Value : defaultValue; - } - - public static double? ToDoubleOrNull(this XAttribute a) - { - if (a == null) - return null; - - return a.Value.ToDouble(); - } - - public static double ToDouble(this XElement a, double defaultValue = 0) - { - if (a == null) - return defaultValue; - - var b = a.Value.ToDouble(); - - return b != null ? b.Value : defaultValue; - } - - public static double? ToDoubleOrNull(this XElement a) - { - if (a == null) - return null; - - return a.Value.ToDouble(); - } - - public static DateTime? ToDateTime(this XAttribute a) - { - if (a == null || string.IsNullOrWhiteSpace(a.Value)) - return null; - - DateTime d; - - if (!DateTime.TryParse(a.Value, out d)) - return null; - - return d; - } - - public static TimeSpan? ToTimeSpan(this XElement e) - { - if (e == null || string.IsNullOrWhiteSpace(e.Value)) - return null; - - TimeSpan d; - - if (!TimeSpan.TryParse(e.Value, out d)) - return null; - - return d; - } - - public static DateTime RoundDown(this DateTime dt, TimeSpan d) - { - var modTicks = dt.Ticks % d.Ticks; - var delta = -modTicks; - - return new DateTime(dt.Ticks + delta, dt.Kind); - } - } -} diff --git a/FabWorks.Core/PressBrake/MatType.cs b/FabWorks.Core/PressBrake/MatType.cs deleted file mode 100644 index c2716f8..0000000 --- a/FabWorks.Core/PressBrake/MatType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace FabWorks.Core.PressBrake -{ - public enum MatType - { - MildSteel, - HighStrengthSteel, - Stainless, - SoftAluminum, - HardAluminum - } -} diff --git a/FabWorks.Core/PressBrake/Program.cs b/FabWorks.Core/PressBrake/Program.cs deleted file mode 100644 index 7b36a87..0000000 --- a/FabWorks.Core/PressBrake/Program.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace FabWorks.Core.PressBrake -{ - public class Program - { - public Program() - { - UpperToolSets = new List(); - LowerToolSets = new List(); - Steps = new List(); - } - - public int Version { get; set; } - - public string ProgName { get; set; } - - public string FilePath { get; set; } - - public double MatThick { get; set; } - - public MatType MatType { get; set; } - - public double KFactor { get; set; } - - public string TeachName { get; set; } - - public string PartName { get; set; } - - public string SetupNotes { get; set; } - - public string ProgNotes { get; set; } - - public bool RZEnabled { get; set; } - - public List UpperToolSets { get; set; } - - public List LowerToolSets { get; set; } - - public List Steps { get; set; } - - public static Program Load(string file) - { - var reader = new ProgramReader(); - reader.Read(file); - return reader.Program; - } - - public static Program Load(Stream stream) - { - var reader = new ProgramReader(); - reader.Read(stream); - return reader.Program; - } - } -} diff --git a/FabWorks.Core/PressBrake/ProgramReader.cs b/FabWorks.Core/PressBrake/ProgramReader.cs deleted file mode 100644 index 0267e79..0000000 --- a/FabWorks.Core/PressBrake/ProgramReader.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Xml.Linq; - -namespace FabWorks.Core.PressBrake -{ - public class ProgramReader - { - public Program Program { get; set; } - - public ProgramReader() - { - Program = new Program(); - } - - public void Read(string file) - { - var xml = XDocument.Load(file); - Program.FilePath = file; - Read(xml); - } - - public void Read(Stream stream) - { - var xml = XDocument.Load(stream); - Read(xml); - } - - private void Read(XDocument doc) - { - var data = doc.Root.Element("PressBrakeProgram"); - - Program.Version = data.Attribute("Version").ToInt(); - Program.ProgName = data.Attribute("ProgName")?.Value; - Program.MatThick = data.Attribute("MatThick").ToDouble(); - Program.MatType = GetMaterialType(data.Attribute("MatType")?.Value); - Program.KFactor = data.Attribute("KFactor").ToDouble(); - Program.TeachName = data.Attribute("TeachName")?.Value; - Program.PartName = data.Attribute("PartName")?.Value; - Program.SetupNotes = data.Attribute("SetupNotes")?.Value; - Program.ProgNotes = data.Attribute("ProgNotes")?.Value; - Program.RZEnabled = Convert.ToBoolean(data.Attribute("RZEnabled").ToInt()); - - foreach (var item in data.Element("UpperToolSets").Descendants("ToolSetup")) - { - var setup = ReadToolSetup(item); - Program.UpperToolSets.Add(setup); - } - - foreach (var item in data.Element("LowerToolSets").Descendants("ToolSetup")) - { - var setup = ReadToolSetup(item); - Program.LowerToolSets.Add(setup); - } - - foreach (var item in data.Element("StepData").Descendants("Step")) - { - var step = ReadStep(item); - step.UpperTool = Program.UpperToolSets.FirstOrDefault(t => t.Id == step.UpperID); - step.LowerTool = Program.LowerToolSets.FirstOrDefault(t => t.Id == step.LowerID); - - Program.Steps.Add(step); - } - } - - private ToolSetup ReadToolSetup(XElement x) - { - var setup = new ToolSetup(); - - setup.Name = x.Attribute("Name").Value; - setup.Id = x.Attribute("ID").ToInt(); - setup.Length = x.Attribute("Length").ToDouble(); - setup.StackedHolderType = x.Attribute("StackedHolderType").ToInt(); - setup.HolderHeight = x.Attribute("HolderHeight").ToDouble(); - - foreach (var item in x.Descendants("SegEntry")) - { - var entry = new SegEntry(); - entry.SegValue = item.Attribute("SegValue").ToDouble(); - setup.Segments.Add(entry); - } - - return setup; - } - - private Step ReadStep(XElement x) - { - var step = new Step(); - - step.RevMode = x.Attribute("RevMode").ToInt(); - step.RevTons = x.Attribute("RevTons").ToDouble(); - step.MaxTons = x.Attribute("MaxTons").ToDouble(); - step.RevAbsPos = x.Attribute("RevAbsPos").ToDouble(); - step.ActualAng = x.Attribute("ActualAng").ToDouble(); - step.AngleAdj = x.Attribute("AngleAdj").ToDouble(); - step.BendLen = x.Attribute("BendLen").ToDouble(); - step.StrokeLen = x.Attribute("StrokeLen").ToDouble(); - step.UpperID = x.Attribute("UpperID").ToInt(); - step.LowerID = x.Attribute("LowerID").ToInt(); - step.SpdChgDwn = x.Attribute("SpdChgDwn").ToDouble(); - step.SpdChgUp = x.Attribute("SpdChgUp").ToDouble(); - step.Tilt = x.Attribute("Tilt").ToDouble(); - step.FormSpeed = x.Attribute("FormSpeed").ToDouble(); - step.XLeft = x.Attribute("XLeft").ToDouble(); - step.XRight = x.Attribute("XRight").ToDouble(); - step.RLeft = x.Attribute("RLeft").ToDouble(); - step.RRight = x.Attribute("RRight").ToDouble(); - step.ZLeft = x.Attribute("ZLeft").ToDouble(); - step.ZRight = x.Attribute("ZRight").ToDouble(); - step.FLeft = x.Attribute("FLeft").ToDouble(); - step.FRight = x.Attribute("FRight").ToDouble(); - step.SSLeft = x.Attribute("SSLeft").ToDouble(); - step.SSRight = x.Attribute("SSRight").ToDouble(); - step.ReturnSpd = x.Attribute("ReturnSpd").ToDouble(); - step.SideFlgHeight = x.Attribute("SideFlgHeight").ToDouble(); - - return step; - } - - private MatType GetMaterialType(string value) - { - if (value == null) - return MatType.MildSteel; - - int i; - - if (!int.TryParse(value, out i)) - return MatType.MildSteel; - - switch (i) - { - case 0: - return MatType.MildSteel; - case 1: - return MatType.HighStrengthSteel; - case 2: - return MatType.Stainless; - case 3: - return MatType.SoftAluminum; - case 4: - return MatType.HardAluminum; - } - - return MatType.MildSteel; - } - } -} diff --git a/FabWorks.Core/PressBrake/SegEntry.cs b/FabWorks.Core/PressBrake/SegEntry.cs deleted file mode 100644 index e6ed33d..0000000 --- a/FabWorks.Core/PressBrake/SegEntry.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace FabWorks.Core.PressBrake -{ - public class SegEntry - { - public double SegValue { get; set; } - } -} diff --git a/FabWorks.Core/PressBrake/Step.cs b/FabWorks.Core/PressBrake/Step.cs deleted file mode 100644 index 3520a17..0000000 --- a/FabWorks.Core/PressBrake/Step.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace FabWorks.Core.PressBrake -{ - public class Step - { - public int RevMode { get; set; } - public double RevTons { get; set; } - public double MaxTons { get; set; } - public double RevAbsPos { get; set; } - public double ActualAng { get; set; } - public double AngleAdj { get; set; } - public double BendLen { get; set; } - public double StrokeLen { get; set; } - public double Tilt { get; set; } - public int UpperID { get; set; } - public int LowerID { get; set; } - public double SpdChgDwn { get; set; } - public double SpdChgUp { get; set; } - public double FormSpeed { get; set; } - public double XLeft { get; set; } - public double XRight { get; set; } - public double RLeft { get; set; } - public double RRight { get; set; } - public double ZLeft { get; set; } - public double ZRight { get; set; } - public double FLeft { get; set; } - public double FRight { get; set; } - - public double SSLeft { get; set; } - public double SSRight { get; set; } - public double ReturnSpd { get; set; } - public double SideFlgHeight { get; set; } - - public ToolSetup UpperTool { get; set; } - public ToolSetup LowerTool { get; set; } - } -} diff --git a/FabWorks.Core/PressBrake/ToolSetup.cs b/FabWorks.Core/PressBrake/ToolSetup.cs deleted file mode 100644 index c71d4b9..0000000 --- a/FabWorks.Core/PressBrake/ToolSetup.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; - -namespace FabWorks.Core.PressBrake -{ - public class ToolSetup - { - public ToolSetup() - { - Segments = new List(); - } - - public string Name { get; set; } - - public int Id { get; set; } - - public double Length { get; set; } - - public int StackedHolderType { get; set; } - - public double HolderHeight { get; set; } - - public List Segments { get; set; } - } -} diff --git a/FabWorks.Tests/FabWorks.Tests.csproj b/FabWorks.Tests/FabWorks.Tests.csproj deleted file mode 100644 index 12e226c..0000000 --- a/FabWorks.Tests/FabWorks.Tests.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - net8.0 - enable - enable - - false - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/FabWorks.Tests/FormProgramServiceTests.cs b/FabWorks.Tests/FormProgramServiceTests.cs deleted file mode 100644 index 146355c..0000000 --- a/FabWorks.Tests/FormProgramServiceTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using FabWorks.Api.Services; -using Xunit; - -namespace FabWorks.Tests -{ - public class FormProgramServiceTests - { - [Fact] - public void ParseFromFile_SamplePgm_PopulatesMaterialType() - { - var service = new FormProgramService(); - var fp = service.ParseFromFile("TestData/sample.pgm"); - - // ProgName is empty in the sample file, so verify MaterialType instead - Assert.False(string.IsNullOrEmpty(fp.MaterialType)); - } - - [Fact] - public void ParseFromFile_SamplePgm_PopulatesThickness() - { - var service = new FormProgramService(); - var fp = service.ParseFromFile("TestData/sample.pgm"); - Assert.NotNull(fp.Thickness); - Assert.True(fp.Thickness > 0); - } - - [Fact] - public void ParseFromFile_SamplePgm_PopulatesBendCount() - { - var service = new FormProgramService(); - var fp = service.ParseFromFile("TestData/sample.pgm"); - Assert.True(fp.BendCount > 0); - } - - [Fact] - public void ParseFromFile_SamplePgm_PopulatesToolNames() - { - var service = new FormProgramService(); - var fp = service.ParseFromFile("TestData/sample.pgm"); - Assert.False(string.IsNullOrEmpty(fp.UpperToolNames)); - Assert.False(string.IsNullOrEmpty(fp.LowerToolNames)); - } - - [Fact] - public void ParseFromFile_SamplePgm_ComputesContentHash() - { - var service = new FormProgramService(); - var fp = service.ParseFromFile("TestData/sample.pgm"); - Assert.NotNull(fp.ContentHash); - Assert.Equal(64, fp.ContentHash.Length); // SHA256 hex = 64 chars - } - } -} diff --git a/FabWorks.Tests/PressBrake/ProgramReaderTests.cs b/FabWorks.Tests/PressBrake/ProgramReaderTests.cs deleted file mode 100644 index b2614cf..0000000 --- a/FabWorks.Tests/PressBrake/ProgramReaderTests.cs +++ /dev/null @@ -1,48 +0,0 @@ -using FabWorks.Core.PressBrake; -using Xunit; - -namespace FabWorks.Tests.PressBrake -{ - public class ProgramReaderTests - { - [Fact] - public void Load_SamplePgm_ParsesProgramAttributes() - { - var pgm = Program.Load("TestData/sample.pgm"); - - // ProgName may be empty on some exports; verify PartName was parsed instead - Assert.False(string.IsNullOrEmpty(pgm.PartName)); - } - - [Fact] - public void Load_SamplePgm_ParsesThickness() - { - var pgm = Program.Load("TestData/sample.pgm"); - Assert.True(pgm.MatThick > 0); - } - - [Fact] - public void Load_SamplePgm_ParsesSteps() - { - var pgm = Program.Load("TestData/sample.pgm"); - Assert.NotEmpty(pgm.Steps); - } - - [Fact] - public void Load_SamplePgm_ParsesToolSetups() - { - var pgm = Program.Load("TestData/sample.pgm"); - Assert.NotEmpty(pgm.UpperToolSets); - Assert.NotEmpty(pgm.LowerToolSets); - } - - [Fact] - public void Load_SamplePgm_ResolvesStepToolReferences() - { - var pgm = Program.Load("TestData/sample.pgm"); - var step = pgm.Steps[0]; - Assert.NotNull(step.UpperTool); - Assert.NotNull(step.LowerTool); - } - } -} diff --git a/FabWorks.Tests/TestData/sample.pgm b/FabWorks.Tests/TestData/sample.pgm deleted file mode 100644 index 76b06ad..0000000 --- a/FabWorks.Tests/TestData/sample.pgm +++ /dev/null @@ -1,593 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -