refactor: add color assets and update UI components

This commit is contained in:
2026-03-16 16:36:21 +07:00
parent 37b87ececd
commit d991d06f17
25 changed files with 532 additions and 160 deletions
@@ -2,7 +2,7 @@ import SwiftUI
struct NotificationDetailView: View {
let notificationId: UUID
@ObservedObject var viewModel: NotificationsViewModel
var viewModel: NotificationsViewModel
private var notification: AppNotification? {
viewModel.notifications.first { $0.id == notificationId }
@@ -56,12 +56,12 @@ struct NotificationDetailView: View {
} label: {
Text("mark_as_read")
.font(.headline)
.foregroundStyle(.red)
.foregroundStyle(.brand)
.frame(maxWidth: .infinity)
.padding(.vertical, 14)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.red.opacity(0.1))
.fill(Color.brand.opacity(0.1))
)
}
.padding(.horizontal, 16)
@@ -109,8 +109,8 @@ struct NotificationDetailView: View {
private func statusBadge(for notification: AppNotification) -> some View {
let (text, color): (String, Color) = notification.isRead
? (String(localized: "status_read"), .green)
: (String(localized: "status_new"), .red)
? (String(localized: "status_read"), .success)
: (String(localized: "status_new"), .brand)
return Text(text)
.font(.caption.bold())
.foregroundStyle(color)
@@ -239,3 +239,18 @@ struct NotificationDetailView: View {
}
}
}
#Preview {
let notification = AppNotification(
id: UUID(), userId: UUID(), scopeId: nil, channel: .inApp,
contentType: .plain, templateId: nil, subject: "CPU Usage Critical",
body: "Server load has exceeded 95% for the last 5 minutes. Immediate action is required to prevent service degradation.",
source: "monitoring", metadata: ["severity": "critical", "host": "prod-01", "region": "eu-west-1"],
status: .sent, error: nil, attempts: 1, maxAttempts: 3,
nextRetryAt: nil, sentAt: Date(), readAt: nil, createdAt: Date()
)
let vm = NotificationsViewModel()
NavigationStack {
NotificationDetailView(notification: notification, viewModel: vm)
}
}
@@ -1,18 +1,10 @@
import SwiftUI
struct NotificationsView: View {
@EnvironmentObject var authViewModel: AuthViewModel
@StateObject private var viewModel = NotificationsViewModel()
@Environment(AuthViewModel.self) private var authViewModel
@State private var viewModel = NotificationsViewModel()
@State private var showSettings = false
private var unreadNotifications: [AppNotification] {
viewModel.notifications.filter { !$0.isRead }
}
private var readNotifications: [AppNotification] {
viewModel.notifications.filter { $0.isRead }
}
var body: some View {
NavigationStack {
Group {
@@ -49,7 +41,7 @@ struct NotificationsView: View {
}
.sheet(isPresented: $showSettings) {
SettingsView()
.environmentObject(authViewModel)
.environment(authViewModel)
}
.task {
await viewModel.load()
@@ -64,12 +56,12 @@ struct NotificationsView: View {
}
}
var notificationsList: some View {
private var notificationsList: some View {
ScrollView {
LazyVStack(spacing: 0) {
if !unreadNotifications.isEmpty {
if !viewModel.unreadNotifications.isEmpty {
sectionHeader(String(localized: "notifications_active"))
ForEach(unreadNotifications) { notification in
ForEach(viewModel.unreadNotifications) { notification in
NavigationLink(destination: NotificationDetailView(notification: notification, viewModel: viewModel)) {
ActiveNotificationCard(notification: notification)
}
@@ -85,9 +77,9 @@ struct NotificationsView: View {
}
}
if !readNotifications.isEmpty {
if !viewModel.readNotifications.isEmpty {
sectionHeader(String(localized: "notifications_completed"))
ForEach(readNotifications) { notification in
ForEach(viewModel.readNotifications) { notification in
NavigationLink(destination: NotificationDetailView(notification: notification, viewModel: viewModel)) {
ResolvedNotificationCard(notification: notification)
}
@@ -112,7 +104,7 @@ struct NotificationsView: View {
}
}
func sectionHeader(_ title: String) -> some View {
private func sectionHeader(_ title: String) -> some View {
HStack {
Text(title)
.font(.subheadline)
@@ -127,6 +119,43 @@ struct NotificationsView: View {
}
}
// MARK: - Previews
private extension AppNotification {
static let previewUnread = AppNotification(
id: UUID(), userId: UUID(), scopeId: nil, channel: .inApp,
contentType: .plain, templateId: nil, subject: "CPU Usage Critical",
body: "Server load has exceeded 95% for the last 5 minutes.",
source: "monitoring", metadata: ["severity": "critical"],
status: .sent, error: nil, attempts: 1, maxAttempts: 3,
nextRetryAt: nil, sentAt: Date(), readAt: nil, createdAt: Date()
)
static let previewRead = AppNotification(
id: UUID(), userId: UUID(), scopeId: nil, channel: .inApp,
contentType: .plain, templateId: nil, subject: "Deployment Successful",
body: "Version 2.1.0 has been deployed to production.",
source: "ci/cd", metadata: ["severity": "success"],
status: .read, error: nil, attempts: 1, maxAttempts: 3,
nextRetryAt: nil, sentAt: Date(), readAt: Date(), createdAt: Date().addingTimeInterval(-3600)
)
}
#Preview("Active Card") {
ActiveNotificationCard(notification: .previewUnread)
.padding()
}
#Preview("Resolved Card") {
ResolvedNotificationCard(notification: .previewRead)
.padding()
}
#Preview("Notifications List") {
NotificationsView()
.environment(AuthViewModel())
}
// MARK: - Active (Unread) Card
struct ActiveNotificationCard: View {
@@ -166,7 +195,7 @@ struct ActiveNotificationCard: View {
Spacer()
Text("open_button")
.font(.subheadline.bold())
.foregroundStyle(Color.red)
.foregroundStyle(Color.brand)
.padding(.horizontal, 32)
.padding(.vertical, 10)
.background(Color(.systemBackground))
@@ -177,13 +206,13 @@ struct ActiveNotificationCard: View {
.padding(16)
.background(
LinearGradient(
colors: [Color.red, Color.red.opacity(0.85)],
colors: [Color.brand, Color.brand.opacity(0.85)],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.clipShape(RoundedRectangle(cornerRadius: 20))
.shadow(color: .red.opacity(0.3), radius: 8, y: 4)
.shadow(color: .brand.opacity(0.3), radius: 8, y: 4)
}
}
@@ -231,7 +260,7 @@ struct ResolvedNotificationCard: View {
HStack(spacing: 4) {
Image(systemName: "checkmark")
.font(.caption2)
.foregroundStyle(.green)
.foregroundStyle(.success)
Text("notification_read_at \(readAt.formatted(date: .abbreviated, time: .shortened))")
.font(.caption)
.foregroundStyle(.secondary)