package helper import ( "fmt" "log" "users_management/m/model/dto/res" "users_management/m/model/entity" "users_management/m/utils/service" ) func ConvertToDeviceDetailsResponses(devices []entity.DeviceDetails, geocoder service.GeocodingService) ([]res.DeviceDetailsResponse, error) { var responses []res.DeviceDetailsResponse for _, device := range devices { response, err := ConvertToDeviceDetailsResponse(device, geocoder) if err != nil { return nil, err } responses = append(responses, response) } return responses, nil } func ConvertToDeviceDetailsResponse(device entity.DeviceDetails, geocoder service.GeocodingService) (res.DeviceDetailsResponse, error) { // Get address address := "" if geocoder != nil { addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude) if err != nil { log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err) address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude) } else { address = addr } } // Get all backbones connected to this device allBackbones := device.GetAllBackbones() backboneInfos := make([]res.BackboneConnectionInfo, 0) for _, backbone := range allBackbones { isStartDevice := backbone.DeviceStartID == device.ID connectedTo := "" if isStartDevice && backbone.DeviceEnd.DeviceCode != "" { connectedTo = backbone.DeviceEnd.DeviceCode } else if !isStartDevice && backbone.DeviceStart.DeviceCode != "" { connectedTo = backbone.DeviceStart.DeviceCode } info := res.BackboneConnectionInfo{ ID: backbone.ID, BackboneCode: backbone.BackboneCode, CoreAmount: backbone.CoreAmount, IsStartDevice: isStartDevice, ConnectedTo: connectedTo, } backboneInfos = append(backboneInfos, info) } // Get all fishbones connected to this device allFishbones := device.GetAllFishbones() fishboneInfos := make([]res.FishboneConnectionInfo, 0) for _, fishbone := range allFishbones { isStartDevice := fishbone.DeviceStartID == device.ID connectedTo := "" if isStartDevice && fishbone.DeviceEnd.DeviceCode != "" { connectedTo = fishbone.DeviceEnd.DeviceCode } else if !isStartDevice && fishbone.DeviceStart.DeviceCode != "" { connectedTo = fishbone.DeviceStart.DeviceCode } info := res.FishboneConnectionInfo{ ID: fishbone.ID, FishboneCode: fishbone.FishboneCode, CoreAmount: fishbone.CoreAmount, BackboneCode: fishbone.Backbone.BackboneCode, IsStartDevice: isStartDevice, ConnectedTo: connectedTo, } fishboneInfos = append(fishboneInfos, info) } // Convert tower connections - safely handle nullable ExternalTower towerInfos := make([]res.TowerConnectionDetail, 0) for _, tower := range device.Towers { distance := calculateDistance(device.Latitude, device.Longitude, tower.Latitude, tower.Longitude) // Safely handle nullable ExternalTower field var externalTower *bool if tower.ExternalTower != nil { externalTower = tower.ExternalTower } // If tower.ExternalTower is nil, externalTower remains nil // Safely handle nullable ImageURL var imageURL *string if tower.ImageURL != "" { imageURL = &tower.ImageURL } info := res.TowerConnectionDetail{ ID: tower.ID, TowerCode: tower.TowerCode, Distance: distance, ExternalTower: externalTower, // This will be null if tower.ExternalTower is nil ImageURL: imageURL, ImageURLs: tower.GetAllImageURLs(), // Get all images as a slice } towerInfos = append(towerInfos, info) } // Convert entity.PortAssignmentResponse to res.PortAssignmentResponse entityPortAssignments := device.DevicePort.GetPortAssignmentsWithDetails() resPortAssignments := make([]res.PortAssignmentResponse, len(entityPortAssignments)) for i, pa := range entityPortAssignments { resPortAssignments[i] = res.PortAssignmentResponse{ PortNumber: pa.PortNumber, CustomerName: pa.CustomerName, IsOccupied: pa.IsOccupied, } } portAssignments := device.DevicePort.GetPortAssignmentsWithDetails() // Ensure we show all ports up to PortAmount portAssignmentMap := make(map[int]res.PortAssignmentResponse) for _, pa := range portAssignments { portAssignmentMap[pa.PortNumber] = res.PortAssignmentResponse{ PortNumber: pa.PortNumber, CustomerName: pa.CustomerName, IsOccupied: pa.IsOccupied, } } // Fill in missing ports finalPortAssignments := make([]res.PortAssignmentResponse, 0) for i := 1; i <= device.PortAmount; i++ { if pa, exists := portAssignmentMap[i]; exists { finalPortAssignments = append(finalPortAssignments, pa) } else { // Determine if this port should be occupied isOccupied := false // Count occupied ports before this one occupiedBefore := 0 for j := 1; j < i; j++ { if existingPA, exists := portAssignmentMap[j]; exists && existingPA.IsOccupied { occupiedBefore++ } } // Count total ports with customers customersCount := 0 for _, pa := range portAssignments { if pa.CustomerName != nil && *pa.CustomerName != "" { customersCount++ } } // This port should be occupied if we haven't reached port_used yet if occupiedBefore + customersCount < device.DevicePort.PortUsed { isOccupied = true } finalPortAssignments = append(finalPortAssignments, res.PortAssignmentResponse{ PortNumber: i, CustomerName: nil, IsOccupied: isOccupied, }) } } var customerNames []string // If port_used is 0, return empty port assignments if device.PortAmount == 0 { finalPortAssignments = []res.PortAssignmentResponse{} customerNames = []string{} } else if device.DevicePort.PortUsed == 0 { // If port_used is 0, return empty port assignments but keep port_amount structure finalPortAssignments = []res.PortAssignmentResponse{} customerNames = []string{} } else { // Get port assignments with details portAssignments := device.DevicePort.GetPortAssignmentsWithDetails() // Convert to response format finalPortAssignments = make([]res.PortAssignmentResponse, len(portAssignments)) for i, pa := range portAssignments { finalPortAssignments[i] = res.PortAssignmentResponse{ PortNumber: pa.PortNumber, CustomerName: pa.CustomerName, IsOccupied: pa.IsOccupied, } } // Get customer names customerNames = device.DevicePort.GetCustomerNamesOnly() } var deviceImageURLs []string if device.ImageURL != nil && *device.ImageURL != "" { deviceImageURLs = append(deviceImageURLs, *device.ImageURL) } // Use deviceImageURLs instead of trying to call a non-existent method allImageURLs := deviceImageURLs // Handle empty slice vs nil for JSON response if len(allImageURLs) == 0 { allImageURLs = []string{} // Return empty array instead of nil } response := res.DeviceDetailsResponse{ ID: device.ID, DeviceCode: device.DeviceCode, DeviceType: string(device.DeviceType), Address: address, Longitude: device.Longitude, Latitude: device.Latitude, Status: string(device.Status), PortAmount: device.PortAmount, PortUsed: device.DevicePort.PortUsed, PortAvailable: device.DevicePort.PortAvailable, CustomerNames: customerNames, // Just customer names PortAssignments: finalPortAssignments, // Use finalPortAssignments instead of resPortAssignments Region: device.Region, Province: device.Province, City: device.City, District: device.District, ImageURL: device.ImageURL, ImageURLs: allImageURLs, // Use allImageURLs to ensure empty array instead of nil Backbones: backboneInfos, Fishbones: fishboneInfos, Towers: towerInfos, CreatedAt: device.CreatedAt, UpdatedAt: device.UpdatedAt, } return response, nil } // ... rest of helper functions remain the same