Files
pos-system/apps/app-client-base-swift/AppClientBaseSwift/AppClientBaseSwift/Views/Home/ActivityFeed.swift

195 lines
5.1 KiB
Swift

//
// ActivityFeed.swift
// AppClientBaseSwift
//
// Recent activity/transaction feed - Pastel Yellow Theme
// Feed hot đng/giao dch gn đây - Theme Pastel Vàng
//
import SwiftUI
// MARK: - Activity Item
// Item Activity
/// Activity/Transaction item model
/// Model item hot đng/giao dch
struct ActivityItem: Identifiable {
let id = UUID()
let title: String
let subtitle: String
let amount: Double
let icon: String
let color: Color
let time: String
/// Whether amount is positive (received)
/// Amount có dương (nhn tin) không
var isPositive: Bool {
amount >= 0
}
static let samples: [ActivityItem] = [
ActivityItem(
title: "Grab - Đặt xe",
subtitle: "Quận 1 → Quận 7",
amount: -45000,
icon: "car.fill",
color: Color.success,
time: "10:30"
),
ActivityItem(
title: "Nhận tiền",
subtitle: "Từ Nguyễn Văn A",
amount: 200000,
icon: "arrow.down.circle.fill",
color: Color.info,
time: "Hôm qua"
),
ActivityItem(
title: "Thanh toán điện",
subtitle: "EVN HCM",
amount: -350000,
icon: "bolt.fill",
color: Color.brandAccent,
time: "20/01"
),
ActivityItem(
title: "Hoàn tiền",
subtitle: "Khuyến mãi đặt xe",
amount: 15000,
icon: "gift.fill",
color: Color.brandSecondary,
time: "19/01"
)
]
}
// MARK: - Activity Feed
// Feed Activity
/// Activity/Transaction feed list
/// List feed hot đng/giao dch
struct ActivityFeed: View {
// MARK: - Properties
/// Activity items
/// Các items hot đng
let activities: [ActivityItem]
/// Item tap callback
/// Callback tap item
var onActivityTap: ((ActivityItem) -> Void)?
// MARK: - Init
init(activities: [ActivityItem] = ActivityItem.samples, onActivityTap: ((ActivityItem) -> Void)? = nil) {
self.activities = activities
self.onActivityTap = onActivityTap
}
// MARK: - Body
var body: some View {
VStack(alignment: .leading, spacing: 16) {
// Header
HStack {
Text("Hoạt động gần đây")
.font(.headline)
.fontWeight(.semibold)
.foregroundStyle(Color.textPrimary)
Spacer()
Button("Xem tất cả") {
// Show all activities
}
.font(.subheadline)
.foregroundStyle(Color.brandAccent)
}
// Activity list
// Danh sách hot đng
VStack(spacing: 0) {
ForEach(activities) { activity in
ActivityRow(activity: activity)
.onTapGesture {
onActivityTap?(activity)
}
if activity.id != activities.last?.id {
Divider()
.padding(.leading, 56)
}
}
}
.background(Color.cardBackground)
.cornerRadius(16)
.shadow(color: AppTheme.cardShadow, radius: 10, x: 0, y: 5)
}
}
}
// MARK: - Activity Row
// Row Activity
/// Individual activity row
/// Row hot đng riêng l
struct ActivityRow: View {
let activity: ActivityItem
var body: some View {
HStack(spacing: 14) {
// Icon
ZStack {
Circle()
.fill(activity.color.opacity(0.15))
.frame(width: 44, height: 44)
Image(systemName: activity.icon)
.font(.system(size: 18))
.foregroundStyle(activity.color)
}
// Content
// Ni dung
VStack(alignment: .leading, spacing: 4) {
Text(activity.title)
.font(.subheadline)
.fontWeight(.medium)
.foregroundStyle(Color.textPrimary)
Text(activity.subtitle)
.font(.caption)
.foregroundStyle(Color.textSecondary)
}
Spacer()
// Amount + Time
VStack(alignment: .trailing, spacing: 4) {
Text(activity.isPositive ? "+\(String.formatVND(activity.amount))" : String.formatVND(activity.amount))
.font(.subheadline)
.fontWeight(.semibold)
.foregroundStyle(activity.isPositive ? Color.success : Color.textPrimary)
Text(activity.time)
.font(.caption)
.foregroundStyle(Color.textSecondary)
}
}
.padding(.horizontal, 16)
.padding(.vertical, 14)
}
}
// MARK: - Preview
#Preview {
ZStack {
Color.appBackground.ignoresSafeArea()
ActivityFeed()
.padding()
}
}