Cross-platform refinements to appearance/battery/charger editors, tabs and navigation, plus persistence, screenshot previews and CLAUDE.md docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
560 lines
19 KiB
Swift
560 lines
19 KiB
Swift
//
|
||
// ScreenshotPreviews.swift
|
||
// Cable
|
||
//
|
||
// Screenshot previews for all views in all supported languages.
|
||
//
|
||
|
||
import SwiftUI
|
||
import SwiftData
|
||
|
||
// MARK: - Sample Data
|
||
|
||
private enum ScreenshotData {
|
||
@MainActor
|
||
static func makeContainer() -> ModelContainer {
|
||
let container = try! ModelContainer(
|
||
for: ElectricalSystem.self, SavedLoad.self, SavedBattery.self, SavedCharger.self, Item.self,
|
||
configurations: ModelConfiguration(isStoredInMemoryOnly: true)
|
||
)
|
||
let ctx = container.mainContext
|
||
|
||
// System 1: Sailboat
|
||
let sailboat = ElectricalSystem(
|
||
name: "Sailboat Aurora",
|
||
location: "Marina 7",
|
||
iconName: "sailboat",
|
||
colorName: "blue",
|
||
targetRuntimeHours: 15,
|
||
targetChargeTimeHours: 3
|
||
)
|
||
ctx.insert(sailboat)
|
||
|
||
let sailLoads: [(String, Double, Double, Double, Double, Double, String, String, Double, Double)] = [
|
||
("Navigation Lights", 12.8, 2.4, 28.8, 5.0, 2.5, "light.beacon.max", "red", 100, 10),
|
||
("Refrigerator", 12.8, 4.0, 48.0, 3.0, 2.5, "refrigerator", "blue", 40, 24),
|
||
("VHF Radio", 12.8, 6.0, 72.0, 8.0, 4.0, "antenna.radiowaves.left.and.right", "green", 30, 8),
|
||
("Anchor Windlass", 12.8, 80.0, 960.0, 6.0, 35.0, "arrow.up.and.down", "orange", 5, 0.5),
|
||
("LED Cabin Lights", 12.8, 1.5, 18.0, 4.0, 1.5, "lightbulb", "yellow", 100, 6),
|
||
]
|
||
for (name, voltage, current, power, length, crossSection, icon, color, duty, hours) in sailLoads {
|
||
ctx.insert(SavedLoad(
|
||
name: name, voltage: voltage, current: current, power: power,
|
||
length: length, crossSection: crossSection,
|
||
iconName: icon, colorName: color,
|
||
dutyCyclePercent: duty, dailyUsageHours: hours,
|
||
system: sailboat
|
||
))
|
||
}
|
||
|
||
ctx.insert(SavedBattery(
|
||
name: "House Bank",
|
||
nominalVoltage: 12.8, capacityAmpHours: 200,
|
||
chemistry: .lithiumIronPhosphate,
|
||
iconName: "battery.100.bolt", colorName: "green",
|
||
system: sailboat
|
||
))
|
||
ctx.insert(SavedBattery(
|
||
name: "Starter Battery",
|
||
nominalVoltage: 12.0, capacityAmpHours: 90,
|
||
chemistry: .agm,
|
||
iconName: "bolt", colorName: "orange",
|
||
system: sailboat
|
||
))
|
||
|
||
ctx.insert(SavedCharger(
|
||
name: "Shore Charger",
|
||
inputVoltage: 230, outputVoltage: 14.4,
|
||
maxCurrentAmps: 40, maxPowerWatts: 580,
|
||
iconName: "powerplug", colorName: "orange",
|
||
system: sailboat, powerSourceType: "shore"
|
||
))
|
||
ctx.insert(SavedCharger(
|
||
name: "Solar MPPT",
|
||
inputVoltage: 36, outputVoltage: 14.2,
|
||
maxCurrentAmps: 30, maxPowerWatts: 426,
|
||
iconName: "sun.max.fill", colorName: "yellow",
|
||
system: sailboat, powerSourceType: "solar"
|
||
))
|
||
|
||
// System 2: Camper Van
|
||
let camper = ElectricalSystem(
|
||
name: "Camper Van",
|
||
location: "Road Trip",
|
||
iconName: "bus",
|
||
colorName: "teal",
|
||
targetRuntimeHours: 24,
|
||
targetChargeTimeHours: 4
|
||
)
|
||
ctx.insert(camper)
|
||
|
||
let camperLoads: [(String, Double, Double, Double, Double, Double, String, String)] = [
|
||
("Water Pump", 12.8, 3.5, 42.0, 4.0, 2.5, "drop", "cyan"),
|
||
("USB Charger", 12.8, 2.0, 24.0, 2.0, 1.5, "cable.connector", "gray"),
|
||
]
|
||
for (name, voltage, current, power, length, crossSection, icon, color) in camperLoads {
|
||
ctx.insert(SavedLoad(
|
||
name: name, voltage: voltage, current: current, power: power,
|
||
length: length, crossSection: crossSection,
|
||
iconName: icon, colorName: color, system: camper
|
||
))
|
||
}
|
||
|
||
ctx.insert(SavedBattery(
|
||
name: "LiFePO4 200Ah",
|
||
nominalVoltage: 12.8, capacityAmpHours: 200,
|
||
chemistry: .lithiumIronPhosphate,
|
||
iconName: "battery.100.bolt", colorName: "green",
|
||
system: camper
|
||
))
|
||
|
||
// System 3: Cabin (empty, for variety)
|
||
let cabin = ElectricalSystem(
|
||
name: "Off-Grid Cabin",
|
||
location: "Mountains",
|
||
iconName: "house",
|
||
colorName: "brown"
|
||
)
|
||
ctx.insert(cabin)
|
||
|
||
return container
|
||
}
|
||
|
||
@MainActor
|
||
static func firstSystem(in container: ModelContainer) -> ElectricalSystem {
|
||
let systems = try! container.mainContext.fetch(
|
||
FetchDescriptor<ElectricalSystem>(sortBy: [SortDescriptor(\.timestamp)])
|
||
)
|
||
return systems.first!
|
||
}
|
||
}
|
||
|
||
// MARK: - Wrapper Views
|
||
|
||
private struct LoadsViewScreenshot: View {
|
||
let container: ModelContainer
|
||
let system: ElectricalSystem
|
||
var initialTab: LoadsView.ComponentTab = .overview
|
||
|
||
var body: some View {
|
||
NavigationStack {
|
||
LoadsView(system: system, initialTab: initialTab)
|
||
}
|
||
.modelContainer(container)
|
||
.environmentObject(UnitSystemSettings())
|
||
}
|
||
}
|
||
|
||
private struct SystemsViewScreenshot: View {
|
||
let container: ModelContainer
|
||
|
||
var body: some View {
|
||
SystemsView()
|
||
.modelContainer(container)
|
||
.environmentObject(UnitSystemSettings())
|
||
}
|
||
}
|
||
|
||
private struct CalculatorViewScreenshot: View {
|
||
let container: ModelContainer
|
||
|
||
var body: some View {
|
||
NavigationStack {
|
||
CalculatorView()
|
||
}
|
||
.modelContainer(container)
|
||
.environmentObject(UnitSystemSettings())
|
||
}
|
||
}
|
||
|
||
private struct BatteryEditorScreenshot: View {
|
||
var body: some View {
|
||
NavigationStack {
|
||
BatteryEditorView(
|
||
configuration: BatteryConfiguration(
|
||
name: "House Bank",
|
||
nominalVoltage: 12.8,
|
||
capacityAmpHours: 200,
|
||
chemistry: .lithiumIronPhosphate,
|
||
iconName: "battery.100.bolt",
|
||
colorName: "green",
|
||
system: ElectricalSystem(name: "Sailboat Aurora")
|
||
),
|
||
onSave: { _ in }
|
||
)
|
||
}
|
||
.environmentObject(UnitSystemSettings())
|
||
}
|
||
}
|
||
|
||
private struct ChargerEditorScreenshot: View {
|
||
var body: some View {
|
||
NavigationStack {
|
||
ChargerEditorView(
|
||
configuration: ChargerConfiguration(
|
||
name: "Shore Charger",
|
||
inputVoltage: 230,
|
||
outputVoltage: 14.4,
|
||
maxCurrentAmps: 40,
|
||
maxPowerWatts: 580,
|
||
iconName: "powerplug",
|
||
colorName: "orange",
|
||
system: ElectricalSystem(name: "Sailboat Aurora")
|
||
),
|
||
onSave: { _ in }
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
private struct ComponentLibraryScreenshot: View {
|
||
var body: some View {
|
||
ComponentLibraryView(
|
||
viewModel: ComponentLibraryViewModel(previewItems: Self.sampleItems),
|
||
onSelect: { _ in }
|
||
)
|
||
}
|
||
|
||
private static let sampleItems: [ComponentLibraryItem] = [
|
||
ComponentLibraryItem(
|
||
id: "1", name: "Navigation Lights",
|
||
translations: ["de": "Navigationslichter", "es": "Luces de navegación", "fr": "Feux de navigation", "nl": "Navigatieverlichting"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 25,
|
||
dutyCyclePercent: 100, defaultUtilizationFactorPercent: 40,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "2", name: "Refrigerator Compressor",
|
||
translations: ["de": "Kühlschrank-Kompressor", "es": "Compresor de refrigerador", "fr": "Compresseur réfrigérateur", "nl": "Koelkastcompressor"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 48,
|
||
dutyCyclePercent: 40, defaultUtilizationFactorPercent: 100,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "3", name: "Anchor Windlass",
|
||
translations: ["de": "Ankerwinde", "es": "Molinete de ancla", "fr": "Guindeau", "nl": "Ankerlier"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 960,
|
||
dutyCyclePercent: 5, defaultUtilizationFactorPercent: 2,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "4", name: "VHF Radio",
|
||
translations: ["de": "UKW Funkgerät", "es": "Radio VHF", "fr": "Radio VHF", "nl": "Marifoon"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 72,
|
||
dutyCyclePercent: 30, defaultUtilizationFactorPercent: 33,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "5", name: "LED Interior Lights",
|
||
translations: ["de": "LED Innenbeleuchtung", "es": "Iluminación LED interior", "fr": "Éclairage LED intérieur", "nl": "LED binnenverlichting"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 18,
|
||
dutyCyclePercent: 100, defaultUtilizationFactorPercent: 25,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "6", name: "Water Pump",
|
||
translations: ["de": "Wasserpumpe", "es": "Bomba de agua", "fr": "Pompe à eau", "nl": "Waterpomp"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 42,
|
||
dutyCyclePercent: 20, defaultUtilizationFactorPercent: 10,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "7", name: "Diesel Heater",
|
||
translations: ["de": "Dieselheizung", "es": "Calefactor diésel", "fr": "Chauffage diesel", "nl": "Dieselverwarming"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 36,
|
||
dutyCyclePercent: 60, defaultUtilizationFactorPercent: 50,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
ComponentLibraryItem(
|
||
id: "8", name: "USB Charging Station",
|
||
translations: ["de": "USB-Ladestation", "es": "Estación de carga USB", "fr": "Station de charge USB", "nl": "USB-laadstation"],
|
||
voltageIn: 12.8, voltageOut: nil, watt: 24,
|
||
dutyCyclePercent: 100, defaultUtilizationFactorPercent: 30,
|
||
componentCategory: nil, iconURL: nil
|
||
),
|
||
]
|
||
}
|
||
|
||
// MARK: - Language helper
|
||
|
||
private let locales: [(String, String)] = [
|
||
("EN", "en"),
|
||
("DE", "de"),
|
||
("ES", "es"),
|
||
("FR", "fr"),
|
||
("NL", "nl"),
|
||
]
|
||
|
||
// MARK: - 1. Overview Tab
|
||
|
||
#Preview("Overview – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .overview)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Overview – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .overview)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Overview – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .overview)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Overview – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .overview)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Overview – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .overview)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 2. Components Tab
|
||
|
||
#Preview("Components – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .components)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Components – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .components)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Components – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .components)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Components – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .components)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Components – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .components)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 3. Batteries Tab
|
||
|
||
#Preview("Batteries – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .batteries)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Batteries – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .batteries)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Batteries – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .batteries)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Batteries – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .batteries)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Batteries – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .batteries)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 4. Chargers Tab
|
||
|
||
#Preview("Chargers – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .chargers)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Chargers – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .chargers)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Chargers – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .chargers)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Chargers – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .chargers)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Chargers – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
LoadsViewScreenshot(container: c, system: ScreenshotData.firstSystem(in: c), initialTab: .chargers)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 5. Systems List
|
||
|
||
#Preview("Systems – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
SystemsViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Systems – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
SystemsViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Systems – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
SystemsViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Systems – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
SystemsViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Systems – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
SystemsViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 6. Parts Library
|
||
|
||
#Preview("Library – EN") {
|
||
ComponentLibraryScreenshot()
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("Library – DE") {
|
||
ComponentLibraryScreenshot()
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("Library – ES") {
|
||
ComponentLibraryScreenshot()
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("Library – FR") {
|
||
ComponentLibraryScreenshot()
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("Library – NL") {
|
||
ComponentLibraryScreenshot()
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 7. Load Editor (Calculator)
|
||
|
||
#Preview("LoadEditor – EN") {
|
||
let c = ScreenshotData.makeContainer()
|
||
CalculatorViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("LoadEditor – DE") {
|
||
let c = ScreenshotData.makeContainer()
|
||
CalculatorViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("LoadEditor – ES") {
|
||
let c = ScreenshotData.makeContainer()
|
||
CalculatorViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("LoadEditor – FR") {
|
||
let c = ScreenshotData.makeContainer()
|
||
CalculatorViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("LoadEditor – NL") {
|
||
let c = ScreenshotData.makeContainer()
|
||
CalculatorViewScreenshot(container: c)
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 8. Battery Editor
|
||
|
||
#Preview("BatteryEditor – EN") {
|
||
BatteryEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("BatteryEditor – DE") {
|
||
BatteryEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("BatteryEditor – ES") {
|
||
BatteryEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("BatteryEditor – FR") {
|
||
BatteryEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("BatteryEditor – NL") {
|
||
BatteryEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|
||
|
||
// MARK: - 9. Charger Editor
|
||
|
||
#Preview("ChargerEditor – EN") {
|
||
ChargerEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "en"))
|
||
}
|
||
|
||
#Preview("ChargerEditor – DE") {
|
||
ChargerEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "de"))
|
||
}
|
||
|
||
#Preview("ChargerEditor – ES") {
|
||
ChargerEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "es"))
|
||
}
|
||
|
||
#Preview("ChargerEditor – FR") {
|
||
ChargerEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "fr"))
|
||
}
|
||
|
||
#Preview("ChargerEditor – NL") {
|
||
ChargerEditorScreenshot()
|
||
.environment(\.locale, Locale(identifier: "nl"))
|
||
}
|