import SwiftUI enum AppSection: String, CaseIterable, Hashable, Identifiable { var id: String { rawValue } case dashboard = "Dashboard" case clients = "Clients" case imports = "Imports" case communications = "Communications" case calendar = "Calendar" case oracle = "Oracle" case sentinel = "Sentinel" case inventory = "Inventory" case settings = "Settings" var displayTitle: String { switch self { case .sentinel: return SentinelScope.navigationTitle default: return rawValue } } var systemImage: String { switch self { case .dashboard: return "square.grid.2x2" case .clients: return "person.text.rectangle" case .imports: return "tray.and.arrow.down" case .communications: return "phone.connection" case .calendar: return "calendar.badge.clock" case .oracle: return "message.and.waveform" case .sentinel: return "person.crop.rectangle" case .inventory: return "shippingbox" case .settings: return "gearshape" } } var accentColor: Color { switch self { case .dashboard: return VelocityTheme.accent case .clients: return Color(red: 0.22, green: 0.78, blue: 0.96) case .imports: return Color(red: 0.94, green: 0.70, blue: 0.25) case .communications: return Color(red: 0.19, green: 0.84, blue: 0.63) case .calendar: return Color(red: 0.96, green: 0.67, blue: 0.16) case .oracle: return Color(red: 0.13, green: 0.83, blue: 0.93) // cyan case .sentinel: return Color(red: 0.60, green: 0.57, blue: 0.99) // indigo case .inventory: return VelocityTheme.warning case .settings: return VelocityTheme.mutedFg } } } struct ContentView: View { @State private var selectedSection: AppSection? = .dashboard @State private var session = SessionStore.shared var body: some View { Group { if session.isConfigured { NavigationSplitView(columnVisibility: .constant(.all)) { sidebarContent } detail: { detailContent } .navigationSplitViewStyle(.balanced) } else { ConfigurationGateView() } } } // MARK: โ€“ Sidebar private var sidebarContent: some View { ZStack { VelocityTheme.sidebarBg.ignoresSafeArea() VStack(spacing: 0) { // App title HStack(spacing: 10) { ZStack { RoundedRectangle(cornerRadius: 9) .fill(VelocityTheme.accent.opacity(0.18)) .frame(width: 34, height: 34) Image(systemName: "bolt.fill") .font(.system(size: 15, weight: .semibold)) .foregroundStyle(VelocityTheme.accent) } VStack(alignment: .leading, spacing: 1) { Text("Velocity") .font(.system(size: 16, weight: .semibold)) .foregroundStyle(VelocityTheme.foreground) Text("Project Velocity ยท v1.1") .font(.system(size: 10)) .foregroundStyle(VelocityTheme.mutedFg) } Spacer() } .padding(.horizontal, 16) .padding(.top, 20) .padding(.bottom, 16) Divider() .background(VelocityTheme.borderSubtle) .padding(.bottom, 8) // Nav items VStack(spacing: 2) { ForEach(AppSection.allCases) { section in Button { selectedSection = section } label: { SidebarRow(section: section, isSelected: selectedSection == section) } .buttonStyle(.plain) .accessibilityLabel(section.displayTitle) .accessibilityAddTraits(selectedSection == section ? [.isSelected] : []) } } .padding(.horizontal, 8) Spacer() // User footer Divider() .background(VelocityTheme.borderSubtle) HStack(spacing: 10) { ZStack { RoundedRectangle(cornerRadius: 8) .fill(VelocityTheme.accent) .frame(width: 32, height: 32) Text(operatorInitials) .font(.system(size: 11, weight: .bold)) .foregroundStyle(.white) } VStack(alignment: .leading, spacing: 2) { Text(operatorName) .font(.system(size: 12, weight: .medium)) .foregroundStyle(VelocityTheme.foreground) Text(session.authModeDescription) .font(.system(size: 10)) .foregroundStyle(VelocityTheme.mutedFg) } Spacer() } .padding(16) } } .navigationTitle("") .toolbar(.hidden, for: .navigationBar) } // MARK: โ€“ Detail private var detailContent: some View { ZStack { VelocityTheme.background.ignoresSafeArea() Group { switch selectedSection { case .dashboard: DashboardView() case .clients: ClientsView() case .imports: ImportsView() case .communications: CommunicationsView() case .calendar: CalendarView() case .oracle: OracleView() case .sentinel: SentinelView() case .inventory: InventoryView() case .settings: SettingsView() case .none: DashboardView() } } .frame(maxWidth: .infinity, maxHeight: .infinity) } } private var operatorName: String { session.operatorIdentity } private var operatorInitials: String { let source = session.operatorIdentity let parts = source .replacingOccurrences(of: "@", with: " ") .split(separator: ".") .flatMap { $0.split(separator: " ") } let initials = parts.prefix(2).compactMap(\.first) return initials.isEmpty ? "VO" : String(initials) } } // MARK: โ€“ Sidebar Row private struct SidebarRow: View { let section: AppSection let isSelected: Bool var body: some View { HStack(spacing: 11) { Image(systemName: section.systemImage) .font(.system(size: 14, weight: .medium)) .foregroundStyle(isSelected ? section.accentColor : VelocityTheme.mutedFg) .frame(width: 20) Text(section.displayTitle) .font(.system(size: 14, weight: isSelected ? .semibold : .regular)) .foregroundStyle(isSelected ? VelocityTheme.foreground : VelocityTheme.mutedFg) Spacer() } .padding(.horizontal, 12) .padding(.vertical, 9) .background( RoundedRectangle(cornerRadius: 10) .fill(isSelected ? section.accentColor.opacity(0.12) : .clear) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(isSelected ? section.accentColor.opacity(0.25) : .clear, lineWidth: 1) ) ) .contentShape(Rectangle()) } } #Preview { ContentView() }