forked from sagnik/Project_Velocity
Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local> Reviewed-on: sagnik/Project_Velocity#41
225 lines
9.9 KiB
Swift
225 lines
9.9 KiB
Swift
import SwiftUI
|
|
|
|
struct SettingsView: View {
|
|
@State private var store = AppStore.shared
|
|
@State private var session = SessionStore.shared
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 24) {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
Text("Settings")
|
|
.font(.system(size: 28, weight: .bold))
|
|
.foregroundStyle(VelocityTheme.foreground)
|
|
Text("Live runtime configuration")
|
|
.font(.system(size: 12))
|
|
.foregroundStyle(VelocityTheme.mutedFg)
|
|
}
|
|
|
|
SettingsSection(title: "Connectivity") {
|
|
SettingsRow(
|
|
label: "Backend endpoint",
|
|
value: session.endpointDisplay,
|
|
icon: "server.rack",
|
|
accentColor: VelocityTheme.accent
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Dream Weaver endpoint",
|
|
value: session.dreamWeaverEndpointDisplay,
|
|
icon: "wand.and.stars",
|
|
accentColor: session.dreamWeaverEndpointModeDescription == "Dedicated gateway" ? VelocityTheme.warning : VelocityTheme.mutedFg
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Dream Weaver route mode",
|
|
value: session.dreamWeaverEndpointModeDescription,
|
|
icon: "point.3.connected.trianglepath.dotted",
|
|
accentColor: session.dreamWeaverEndpointModeDescription == "Dedicated gateway" ? VelocityTheme.warning : VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Dream Weaver auth",
|
|
value: session.dreamWeaverAuthenticationDescription,
|
|
icon: "key.horizontal",
|
|
accentColor: session.dreamWeaverAuthenticationDescription == "API key configured" ? VelocityTheme.success : VelocityTheme.mutedFg
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Auth mode",
|
|
value: session.authModeDescription,
|
|
icon: "lock.shield",
|
|
accentColor: session.isConfigured ? VelocityTheme.success : VelocityTheme.warning
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Config source",
|
|
value: session.configurationSourceDescription,
|
|
icon: "externaldrive.badge.icloud",
|
|
accentColor: session.isUsingStoredRuntimeConfiguration ? VelocityTheme.success : VelocityTheme.mutedFg
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Last refresh",
|
|
value: store.lastRefreshAt?.relativeShort ?? "No live fetch yet",
|
|
icon: "arrow.clockwise",
|
|
accentColor: VelocityTheme.mutedFg
|
|
)
|
|
}
|
|
|
|
SettingsSection(title: "Operator") {
|
|
SettingsRow(
|
|
label: "Identity",
|
|
value: session.operatorIdentity,
|
|
icon: "person.crop.circle",
|
|
accentColor: VelocityTheme.accent
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "CRM contacts loaded",
|
|
value: "\(store.contacts.count)",
|
|
icon: "person.3",
|
|
accentColor: VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Pending CRM tasks loaded",
|
|
value: "\(store.tasks.count)",
|
|
icon: "checklist",
|
|
accentColor: VelocityTheme.warning
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Property records loaded",
|
|
value: "\(store.properties.count)",
|
|
icon: "building.2",
|
|
accentColor: VelocityTheme.warning
|
|
)
|
|
}
|
|
|
|
SettingsSection(title: "Production Readiness") {
|
|
SettingsRow(
|
|
label: "Canonical contacts",
|
|
value: "\(store.contacts.count) loaded",
|
|
icon: "person.text.rectangle",
|
|
accentColor: store.contacts.isEmpty ? VelocityTheme.warning : VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Pipeline lanes",
|
|
value: "\(store.kanbanColumns.reduce(0) { $0 + $1.count }) leads",
|
|
icon: "square.grid.3x1.below.line.grid.1x2",
|
|
accentColor: store.kanbanColumns.isEmpty ? VelocityTheme.warning : VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Deals",
|
|
value: "\(store.opportunities.count) opportunities",
|
|
icon: "target",
|
|
accentColor: store.opportunities.isEmpty ? VelocityTheme.warning : VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Timeline events",
|
|
value: "\(store.timelineEvents.count) hydrated",
|
|
icon: "clock.arrow.circlepath",
|
|
accentColor: store.timelineEvents.isEmpty ? VelocityTheme.warning : VelocityTheme.success
|
|
)
|
|
Divider().background(VelocityTheme.borderSubtle)
|
|
SettingsRow(
|
|
label: "Last app error",
|
|
value: store.errorMessage ?? "None",
|
|
icon: "exclamationmark.triangle",
|
|
accentColor: store.errorMessage == nil ? VelocityTheme.success : VelocityTheme.danger
|
|
)
|
|
}
|
|
|
|
SessionConfigurationPanel(
|
|
title: "Session Configuration",
|
|
subtitle: "Update the production endpoint, point Dream Weaver at a dedicated gateway when needed, or rotate operator credentials without rebuilding the app. Saving clears the cached token, re-runs a live refresh, and probes the Dream Weaver routes.",
|
|
primaryActionTitle: "Save and refresh",
|
|
allowsClearingStoredConfiguration: true
|
|
)
|
|
|
|
SettingsSection(title: "Production Notes") {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("This build avoids local demo data. Runtime session overrides are stored on-device so investor or operator installs no longer depend on committed build-time credentials.")
|
|
.font(.system(size: 13))
|
|
.foregroundStyle(VelocityTheme.foreground)
|
|
Text("\(SentinelScope.navigationTitle) remains the truthful iPad label for the current \(SentinelScope.productFamilyName) surface because visitor analytics stay disabled until a dedicated production feed exists; Communications, Calendar, Dashboard, Oracle pipeline, and inventory summaries are live-backed. Dream Weaver can now use a dedicated gateway with an optional per-gateway API key, and backend plus generation route health are still enforced and reported truthfully.")
|
|
.font(.system(size: 12))
|
|
.foregroundStyle(VelocityTheme.mutedFg)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 14)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding(24)
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
|
.background(VelocityTheme.background)
|
|
}
|
|
}
|
|
|
|
private struct SettingsSection<Content: View>: View {
|
|
let title: String
|
|
@ViewBuilder let content: Content
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
Text(title.uppercased())
|
|
.font(.system(size: 10, weight: .semibold))
|
|
.tracking(1.2)
|
|
.foregroundStyle(VelocityTheme.mutedFg)
|
|
.padding(.bottom, 8)
|
|
.padding(.horizontal, 4)
|
|
|
|
VStack(spacing: 0) {
|
|
content
|
|
}
|
|
.padding(.vertical, 4)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 14)
|
|
.fill(Color(red: 0.031, green: 0.039, blue: 0.071))
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 14)
|
|
.stroke(VelocityTheme.borderSubtle, lineWidth: 1)
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct SettingsRow: View {
|
|
let label: String
|
|
let value: String
|
|
let icon: String
|
|
let accentColor: Color
|
|
|
|
var body: some View {
|
|
HStack(spacing: 14) {
|
|
ZStack {
|
|
RoundedRectangle(cornerRadius: 7)
|
|
.fill(accentColor.opacity(0.12))
|
|
.frame(width: 30, height: 30)
|
|
Image(systemName: icon)
|
|
.font(.system(size: 13, weight: .medium))
|
|
.foregroundStyle(accentColor)
|
|
}
|
|
|
|
Text(label)
|
|
.font(.system(size: 14))
|
|
.foregroundStyle(VelocityTheme.foreground)
|
|
|
|
Spacer()
|
|
|
|
Text(value)
|
|
.font(.system(size: 13))
|
|
.foregroundStyle(VelocityTheme.mutedFg)
|
|
.multilineTextAlignment(.trailing)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.vertical, 12)
|
|
}
|
|
}
|