Cleaned up and removed columns so it is what an actual Solari board would show

- This update adds ISO date/time helpers for Amtrak timestamps, refactors the train table to show arrival time, status, and track details, and tracks the current station context in the UI.
- It also hardens station status decoding with a safe fallback to Unknown and simplifies the row model to match the new display.
This commit is contained in:
Tony Bark 2026-08-23 19:52:27 -04:00
parent ebff2c9c32
commit bf67475107
3 changed files with 99 additions and 31 deletions

View file

@ -0,0 +1,37 @@
import Foundation
struct AmtrakDateFormatting {
static let isoFormatter = ISO8601DateFormatter()
static let timeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter
}()
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()
static func time(from isoString: String?) -> String? {
guard let isoString, !isoString.isEmpty, let date = isoFormatter.date(
from: isoString) else {
return nil
}
return timeFormatter.string(from: date)
}
static func date(from isoString: String?) -> String? {
guard let isoString, !isoString.isEmpty, let date = isoFormatter.date(
from: isoString) else {
return nil
}
return dateFormatter.string(from: date)
}
}

View file

@ -1,14 +1,9 @@
import SwiftCrossUI
import Foundation
extension Train {
var displayNumber: String {
let parts = trainID.split(separator: "-", maxSplits: 1)
guard parts.count <= 2 else {
return trainNum
}
return "\(trainNum) (\(parts[1]))"
var serviceDate: String {
AmtrakDateFormatting.date(from: createdAt) ?? "-"
}
}
@ -19,6 +14,9 @@ struct ContentView: View {
@State var rows: [TrainStatusRow] = []
@State var isLoading = false
@State var errorMessage: String?
@State var currentStation: String = ""
let unknown = "Unknown"
var body: some View {
VStack {
@ -56,11 +54,13 @@ struct ContentView: View {
}
Table(rows) {
TableColumn("Time", value: \TrainStatusRow.arrival)
TableColumn("Number", value: \TrainStatusRow.trainNum)
TableColumn("Route", value: \TrainStatusRow.routeName)
TableColumn("Platform", value: \TrainStatusRow.platform)
TableColumn("Origin", value: \TrainStatusRow.origin)
TableColumn("Destination", value: \TrainStatusRow.destination)
TableColumn("Train", value: \TrainStatusRow.routeName)
TableColumn("To", value: \TrainStatusRow.origin)
TableColumn("From", value: \TrainStatusRow.destination)
TableColumn("Status", value: \TrainStatusRow.status)
TableColumn("Track", value: \TrainStatusRow.platform)
}.overlay(alignment: .bottomTrailing) {
if isLoading {
@ -73,6 +73,8 @@ struct ContentView: View {
HStack {
if let errorMessage {
Text(errorMessage)
} else if !currentStation.isEmpty {
Text(currentStation)
}
}.padding(10)
}
@ -117,17 +119,7 @@ struct ContentView: View {
where: { $0.code == code
},
)?.platform
newRows.append(TrainStatusRow(
trainID: train.trainID,
trainNum: train.displayNumber,
routeName: train.routeName,
origin: train.origName,
destination: train.destName,
platform: (
platform?.isEmpty == false,
) ? platform! : "",
))
newRows.append(trainStatus(for: train, atStationCode: code))
}
}
@ -141,4 +133,43 @@ struct ContentView: View {
rows = []
}
}
func isOnTime(arr: String?, schArr: String?) -> String {
let amtrakDate = AmtrakDateFormatting()
if AmtrakDateFormatting
.time(from: arr) != AmtrakDateFormatting
.time(from: schArr) {
return "Late"
}
return "On Time"
}
func trainStatus(for train: Train, atStationCode code: String) -> TrainStatusRow {
let leg = train.stations?.first { $0.code == code }
let platform = leg?.platform ?? "-"
let stationName = leg?.name ?? unknown
let arrival = AmtrakDateFormatting.time(from: leg?.arr) ?? AmtrakDateFormatting.time(
from: leg?.schArr
)
let depature = AmtrakDateFormatting.time(from: leg?.dep) ?? AmtrakDateFormatting.time(
from: leg?.schDep
)
currentStation = "\(stationName) station"
return TrainStatusRow(
trainID: train.trainID,
trainNum: train.trainNum,
routeName: train.routeName,
status: isOnTime(arr: leg?.arr, schArr: leg?.schArr),
platform: leg?.platform ?? "",
arrival: arrival ?? "",
departure: depature ?? "",
origin: train.origName,
destination: train.destName,
// serviceDate: train.serviceDate
)
}
}

View file

@ -25,6 +25,12 @@ enum StationStatus: String, Codable, Sendable {
case station = "Station"
case departed = "Departed"
case unknown = "Unknown"
init(from decoder: any Decoder) throws {
let container = try decoder.singleValueContainer()
let rawValue = try container.decode(String.self)
self = StationStatus(rawValue: rawValue) ?? .unknown
}
}
struct Train: Codable {
@ -83,12 +89,6 @@ struct StaleData: Codable {
typealias TrainResponse = [String: [Train]]
typealias StationResponse = [String: StationMeta]
struct AmtrakDateFormatting {
let isoFormatter = ISO8601DateFormatter()
}
struct TrainStatusRow: Identifiable {
var id: String {
trainID
@ -96,12 +96,12 @@ struct TrainStatusRow: Identifiable {
var trainID: String
var trainNum: String
var originating: String
var routeName: String
var status: String
var platform: String
var arrival: String
var departure: String
var origin: String
var syatus: String
var destination: String
// var serviceDate: String
}