195 lines
5.1 KiB
Swift
195 lines
5.1 KiB
Swift
//
|
|
// ActivityFeed.swift
|
|
// AppClientBaseSwift
|
|
//
|
|
// Recent activity/transaction feed - Pastel Yellow Theme
|
|
// Feed hoạt động/giao dịch gần đây - Theme Pastel Vàng
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
// MARK: - Activity Item
|
|
// Item Activity
|
|
|
|
/// Activity/Transaction item model
|
|
/// Model item hoạt động/giao dịch
|
|
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 (nhận tiền) 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 hoạt động/giao dịch
|
|
struct ActivityFeed: View {
|
|
|
|
// MARK: - Properties
|
|
|
|
/// Activity items
|
|
/// Các items hoạt độ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 hoạt độ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 hoạt độ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
|
|
// Nội 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()
|
|
}
|
|
}
|