feat: Built the native SwiftUI app shell mirroring the WebOS interface (Dashboard, Inventory, Oracle tabs) (#2)

I have attached the screenshots of the native SwiftUI app.

<img width="1705" alt="image.png" src="attachments/59fec2f3-0ae2-4b58-9349-457618ea0678">
<img width="1699" alt="image.png" src="attachments/0bf7c4f9-c883-4929-be36-774685b82fc4">
<img width="1698" alt="image.png" src="attachments/e3407e84-aaf2-45c0-9325-247d4020bace">
<img width="1694" alt="image.png" src="attachments/ee2cd47d-800d-4a40-855c-d54856680e79">
<img width="1694" alt="image.png" src="attachments/a2c902f1-9bc9-4427-8cae-b5801527c1ff">

Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local>
Reviewed-on: #2
Co-authored-by: sayan <sayan@desineuron.in>
Co-committed-by: sayan <sayan@desineuron.in>
This commit was merged in pull request #2.
This commit is contained in:
2026-03-07 18:46:02 +05:30
committed by sagnik
parent 8fe2344e71
commit cb6c752c8e
33 changed files with 6930 additions and 67 deletions

View File

@@ -0,0 +1,256 @@
import SwiftUI
import Combine
// MARK: Data Models
enum SentimentType: String, CaseIterable {
case excited, interested, neutral, confused, disinterested
var score: Int {
switch self {
case .excited: return 100
case .interested: return 80
case .neutral: return 50
case .confused: return 30
case .disinterested: return 10
}
}
var emoji: String {
switch self {
case .excited: return "😃"
case .interested: return "🤔"
case .neutral: return "😐"
case .confused: return "😕"
case .disinterested: return "😴"
}
}
var color: Color {
switch self {
case .excited: return VelocityTheme.success
case .interested: return VelocityTheme.accent
case .neutral: return VelocityTheme.mutedFg
case .confused: return VelocityTheme.warning
case .disinterested: return VelocityTheme.danger
}
}
}
struct Visitor: Identifiable {
let id: String
let faceId: String
var sentiment: SentimentType
var confidence: Double
var dwellTime: Int // seconds
var zone: String
let timestamp: Date
}
enum LeadSource: String {
case whatsapp = "WhatsApp"
case walkin = "Walk-in"
case website = "Website"
}
enum LeadStatus: String {
case hot = "Hot"
case engaged = "Engaged"
case new = "New"
case qualified = "Qualified"
case closed = "Closed"
var color: Color {
switch self {
case .hot: return VelocityTheme.danger
case .engaged: return VelocityTheme.accent
case .new: return VelocityTheme.mutedFg
case .qualified: return VelocityTheme.success
case .closed: return Color(red: 0.60, green: 0.57, blue: 0.99)
}
}
}
struct Lead: Identifiable {
let id: String
let name: String
let phone: String
let source: LeadSource
var status: LeadStatus
var lastMessage: String
var lastActive: Date
var unreadCount: Int
let qualification: String
let budget: String
let interest: String
var initials: String { String(name.split(separator: " ").prefix(2).compactMap(\.first)) }
}
struct ChatMessage: Identifiable {
let id: String
let sender: String // "user" | "oracle" | "ai"
let content: String
let timestamp: Date
}
struct SystemHealth {
var cpu: Double // 01
var gpu: Double
var memory: Double
}
struct DashboardMetrics {
var activeVisitors: Int
var revenue: String
var aiJobs: Int
var dailyVisitors: Int
var sentimentScore: Double // 0100
var systemHealth: SystemHealth
}
// MARK: Shared Store
@Observable
final class AppStore {
static let shared = AppStore()
private init() { startTimer() }
// Dashboard
var metrics = DashboardMetrics(
activeVisitors: 17,
revenue: "$3.2M",
aiJobs: 24,
dailyVisitors: 128,
sentimentScore: 78,
systemHealth: SystemHealth(cpu: 0.42, gpu: 0.61, memory: 0.55)
)
var dashboardMessages: [ChatMessage] = [
ChatMessage(id: "d0", sender: "ai",
content: "Hello, Ahmed. I've analysed the Q3 pipeline. Would you like a refined strategy for the Apex Innovations deal?",
timestamp: Date().addingTimeInterval(-300))
]
var isDashboardThinking = false
// Visitors
var visitors: [Visitor] = [
Visitor(id: "v1", faceId: "face_001", sentiment: .excited, confidence: 0.92, dwellTime: 450, zone: "Penthouse Show", timestamp: Date()),
Visitor(id: "v2", faceId: "face_002", sentiment: .interested, confidence: 0.87, dwellTime: 320, zone: "Amenity Deck VR", timestamp: Date()),
Visitor(id: "v3", faceId: "face_003", sentiment: .neutral, confidence: 0.78, dwellTime: 180, zone: "Reception", timestamp: Date()),
Visitor(id: "v4", faceId: "face_004", sentiment: .confused, confidence: 0.74, dwellTime: 95, zone: "Penthouse Show", timestamp: Date()),
Visitor(id: "v5", faceId: "face_005", sentiment: .disinterested, confidence: 0.65, dwellTime: 60, zone: "Gallery", timestamp: Date()),
]
// Alerts
var isAlertActive = false
var alertMessage = ""
func triggerAlert(_ msg: String) {
isAlertActive = true
alertMessage = msg
}
func clearAlert() {
isAlertActive = false
alertMessage = ""
}
// Leads (Oracle)
var leads: [Lead] = [
Lead(id: "1", name: "Mohammed Al-Rashid", phone: "+971 55 123 4567", source: .whatsapp,
status: .hot, lastMessage: "Can we schedule a viewing for the penthouse tomorrow?",
lastActive: Date().addingTimeInterval(-300), unreadCount: 2,
qualification: "whale", budget: "AED 15M+", interest: "Penthouse Suite"),
Lead(id: "2", name: "Sarah Chen", phone: "+971 50 987 6543", source: .walkin,
status: .engaged, lastMessage: "Thank you for the brochure. I will review with my partner.",
lastActive: Date().addingTimeInterval(-1800), unreadCount: 0,
qualification: "potential", budget: "AED 58M", interest: "2BR Sea View"),
Lead(id: "3", name: "James Wilson", phone: "+971 52 456 7890", source: .website,
status: .new, lastMessage: "Interested in investment opportunities.",
lastActive: Date().addingTimeInterval(-7200), unreadCount: 1,
qualification: "potential", budget: "AED 35M", interest: "1BR Investment"),
Lead(id: "4", name: "Fatima Hassan", phone: "+971 54 321 0987", source: .whatsapp,
status: .qualified,lastMessage: "What are the payment plan options?",
lastActive: Date().addingTimeInterval(-14400), unreadCount: 0,
qualification: "whale", budget: "AED 12M+", interest: "3BR + Maid"),
Lead(id: "5", name: "David Kumar", phone: "+971 56 789 0123", source: .walkin,
status: .closed, lastMessage: "Contract signed. Thank you!",
lastActive: Date().addingTimeInterval(-86400), unreadCount: 0,
qualification: "whale", budget: "AED 20M", interest: "Full Floor"),
]
var messages: [String: [ChatMessage]] = [
"1": [
ChatMessage(id: "m1", sender: "user", content: "Hi, I am interested in the penthouse units.",
timestamp: Date().addingTimeInterval(-7200)),
ChatMessage(id: "m2", sender: "oracle",
content: "Welcome! Our penthouse collection features 4 exclusive units with panoramic sea views. Prices start at AED 15M.",
timestamp: Date().addingTimeInterval(-7200 + 30)),
ChatMessage(id: "m3", sender: "user", content: "Can we schedule a viewing tomorrow?",
timestamp: Date().addingTimeInterval(-300)),
],
"2": [
ChatMessage(id: "m4", sender: "oracle",
content: "Hello Sarah! Here is the digital brochure for the 2-bedroom units we discussed.",
timestamp: Date().addingTimeInterval(-14400)),
ChatMessage(id: "m5", sender: "user", content: "Thank you. I will review with my partner.",
timestamp: Date().addingTimeInterval(-1800)),
],
]
var activeLeadId: String? = "1"
var isOracleThinking = false
func addDashboardMessage(sender: String, content: String) {
let msg = ChatMessage(id: UUID().uuidString, sender: sender, content: content, timestamp: Date())
dashboardMessages.append(msg)
}
func addOracleMessage(leadId: String, sender: String, content: String) {
let msg = ChatMessage(id: UUID().uuidString, sender: sender, content: content, timestamp: Date())
if messages[leadId] == nil { messages[leadId] = [] }
messages[leadId]!.append(msg)
}
// Live ticker
private var timerTask: AnyCancellable?
private var alertTask: DispatchWorkItem?
private func startTimer() {
timerTask = Timer.publish(every: 5, on: .main, in: .common)
.autoconnect()
.sink { [weak self] _ in self?.tick() }
}
private func tick() {
// jitter visitor count ±1
let delta = Int.random(in: -1...1)
metrics.activeVisitors = max(10, metrics.activeVisitors + delta)
// jitter sentiment ±2
let sDelta = Double.random(in: -2...2)
metrics.sentimentScore = min(100, max(40, metrics.sentimentScore + sDelta))
// jitter system health
metrics.systemHealth.cpu = Double.random(in: 0.30...0.65)
metrics.systemHealth.gpu = Double.random(in: 0.45...0.75)
metrics.systemHealth.memory = Double.random(in: 0.40...0.70)
// Random alert (same 10% chance as WebOS every tick)
if !isAlertActive && Double.random(in: 0...1) > 0.85 {
triggerAlert("Confusion detected in Zone B Penthouse Gallery")
let work = DispatchWorkItem { [weak self] in self?.clearAlert() }
alertTask = work
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: work)
}
}
}
// MARK: Helpers
extension Date {
var relativeShort: String {
let diff = Int(Date().timeIntervalSince(self))
if diff < 60 { return "now" }
if diff < 3600 { return "\(diff / 60)m ago" }
if diff < 86400 { return "\(diff / 3600)h ago" }
return "\(diff / 86400)d ago"
}
}