feat: introduce a new pastel yellow theme by adding dedicated color assets and updating various UI components to utilize them.
This commit is contained in:
Binary file not shown.
@@ -1,6 +1,33 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.243",
|
||||
"green" : "0.729",
|
||||
"red" : "0.957"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.180",
|
||||
"green" : "0.780",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.945",
|
||||
"green" : "0.988",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.110",
|
||||
"green" : "0.110",
|
||||
"red" : "0.110"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.243",
|
||||
"green" : "0.729",
|
||||
"red" : "0.957"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.180",
|
||||
"green" : "0.780",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.690",
|
||||
"green" : "0.875",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.400",
|
||||
"green" : "0.851",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.725",
|
||||
"green" : "0.855",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.600",
|
||||
"green" : "0.780",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.170",
|
||||
"green" : "0.170",
|
||||
"red" : "0.170"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.620",
|
||||
"green" : "0.580",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.580",
|
||||
"green" : "0.540",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.725",
|
||||
"green" : "0.855",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.600",
|
||||
"green" : "0.780",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.690",
|
||||
"green" : "0.875",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.400",
|
||||
"green" : "0.851",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.831",
|
||||
"red" : "0.706"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.860",
|
||||
"red" : "0.750"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.541",
|
||||
"green" : "0.784",
|
||||
"red" : "0.478"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.600",
|
||||
"green" : "0.820",
|
||||
"red" : "0.540"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.145",
|
||||
"green" : "0.125",
|
||||
"red" : "0.118"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.961",
|
||||
"green" : "0.961",
|
||||
"red" : "0.961"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.459",
|
||||
"green" : "0.420",
|
||||
"red" : "0.400"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.700",
|
||||
"green" : "0.700",
|
||||
"red" : "0.700"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.380",
|
||||
"green" : "0.780",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.320",
|
||||
"green" : "0.800",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -116,12 +116,26 @@ struct ContentView: View {
|
||||
/// View loading khi đang kiểm tra auth
|
||||
private var loadingView: some View {
|
||||
ZStack {
|
||||
Color(red: 0.05, green: 0.05, blue: 0.12)
|
||||
Color.appBackground
|
||||
.ignoresSafeArea()
|
||||
|
||||
ProgressView()
|
||||
.scaleEffect(1.5)
|
||||
.tint(.white)
|
||||
|
||||
VStack(spacing: 16) {
|
||||
// App logo
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(AppTheme.primaryGradient)
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
Image(systemName: "location.fill")
|
||||
.font(.system(size: 32))
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
.shadow(color: AppTheme.cardShadow, radius: 20, x: 0, y: 10)
|
||||
|
||||
ProgressView()
|
||||
.scaleEffect(1.2)
|
||||
.tint(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +187,7 @@ struct ContentView: View {
|
||||
}
|
||||
.tag(Tab.profile)
|
||||
}
|
||||
.tint(.accentColor)
|
||||
.tint(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,22 +159,63 @@ enum DesignSystem {
|
||||
// MARK: - Colors
|
||||
// Màu sắc
|
||||
|
||||
/// App color palette
|
||||
/// Bảng màu ứng dụng
|
||||
extension Color {
|
||||
/// Primary brand color
|
||||
/// Màu chính thương hiệu
|
||||
static let brandPrimary = Color("BrandPrimary", bundle: nil)
|
||||
// Note: Color assets are auto-generated by Xcode 16+ from Assets.xcassets
|
||||
// Các color assets được Xcode 16+ tự động generate từ Assets.xcassets
|
||||
//
|
||||
// Available colors / Màu có sẵn:
|
||||
// - .brandPrimary - Pastel Yellow (#FFDFB0)
|
||||
// - .brandSecondary - Soft Peach (#FFDAB9)
|
||||
// - .brandAccent - Warm Gold (#F4BA3E)
|
||||
// - .appBackground - Warm White (#FFFCF1)
|
||||
// - .cardBackground - Pure White (#FFFFFF)
|
||||
// - .textPrimary - Dark Brown (#1E2025)
|
||||
// - .textSecondary - Medium Gray (#666B75)
|
||||
// - .success - Pastel Green (#7AC88A)
|
||||
// - .warning - Soft Orange (#FFC761)
|
||||
// - .error - Pastel Red (#FF949E)
|
||||
// - .info - Pastel Blue (#B4D4FF)
|
||||
// - .gradientStart - Light Yellow (#FFF5D6)
|
||||
// - .gradientEnd - Soft Peach (#FFE8D6)
|
||||
|
||||
/// Secondary brand color
|
||||
/// Màu phụ thương hiệu
|
||||
static let brandSecondary = Color("BrandSecondary", bundle: nil)
|
||||
// MARK: - Theme
|
||||
// Theme ứng dụng
|
||||
|
||||
/// Background color
|
||||
/// Màu nền
|
||||
static let appBackground = Color("AppBackground", bundle: nil)
|
||||
/// App theme configuration
|
||||
/// Cấu hình theme ứng dụng
|
||||
enum AppTheme {
|
||||
|
||||
/// Card background color
|
||||
/// Màu nền card
|
||||
static let cardBackground = Color("CardBackground", bundle: nil)
|
||||
/// Primary gradient - Pastel Yellow to Peach
|
||||
/// Gradient chính - Vàng pastel sang Đào
|
||||
static let primaryGradient = LinearGradient(
|
||||
colors: [Color.gradientStart, Color.gradientEnd],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
|
||||
/// Warm gradient - Yellow to Orange accent
|
||||
/// Gradient ấm - Vàng sang cam nhấn
|
||||
static let warmGradient = LinearGradient(
|
||||
colors: [Color.brandPrimary, Color.brandAccent],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
|
||||
/// Soft gradient - Light yellow with transparency
|
||||
/// Gradient nhẹ - Vàng nhạt với độ trong suốt
|
||||
static let softGradient = LinearGradient(
|
||||
colors: [
|
||||
Color.brandPrimary.opacity(0.3),
|
||||
Color.brandSecondary.opacity(0.2)
|
||||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
|
||||
/// Card shadow color
|
||||
/// Màu đổ bóng card
|
||||
static let cardShadow = Color.brandAccent.opacity(0.15)
|
||||
|
||||
/// Button shadow color
|
||||
/// Màu đổ bóng nút
|
||||
static let buttonShadow = Color.brandAccent.opacity(0.3)
|
||||
}
|
||||
|
||||
@@ -26,16 +26,26 @@ struct AuthContainerView: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
// Background gradient
|
||||
// Gradient nền
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Color(red: 0.05, green: 0.05, blue: 0.12),
|
||||
Color(red: 0.1, green: 0.08, blue: 0.18)
|
||||
],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
// Background - Pastel Yellow Theme
|
||||
// Nền - Theme Pastel Vàng
|
||||
Color.appBackground
|
||||
.ignoresSafeArea()
|
||||
|
||||
// Decorative gradient circles
|
||||
// Các vòng tròn gradient trang trí
|
||||
GeometryReader { geometry in
|
||||
Circle()
|
||||
.fill(AppTheme.softGradient)
|
||||
.frame(width: 300, height: 300)
|
||||
.blur(radius: 60)
|
||||
.offset(x: -100, y: -50)
|
||||
|
||||
Circle()
|
||||
.fill(Color.brandSecondary.opacity(0.2))
|
||||
.frame(width: 200, height: 200)
|
||||
.blur(radius: 40)
|
||||
.offset(x: geometry.size.width - 100, y: geometry.size.height - 200)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
|
||||
// Content
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// ForgotPasswordView.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Forgot password screen view
|
||||
// View màn hình quên mật khẩu
|
||||
// Forgot password screen view - Pastel Yellow Theme
|
||||
// View màn hình quên mật khẩu - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -91,7 +91,7 @@ struct ForgotPasswordView: View {
|
||||
Text("Quay lại")
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
@@ -102,11 +102,11 @@ struct ForgotPasswordView: View {
|
||||
Text("Quên mật khẩu?")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text("Nhập email để nhận link đặt lại")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ struct ForgotPasswordView: View {
|
||||
.fill(
|
||||
RadialGradient(
|
||||
colors: [
|
||||
Color.blue.opacity(0.2),
|
||||
Color.brandPrimary.opacity(0.3),
|
||||
Color.clear
|
||||
],
|
||||
center: .center,
|
||||
@@ -133,23 +133,14 @@ struct ForgotPasswordView: View {
|
||||
// Icon
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Color(red: 0.2, green: 0.6, blue: 1.0),
|
||||
Color(red: 0.4, green: 0.3, blue: 0.9)
|
||||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.fill(AppTheme.primaryGradient)
|
||||
.frame(width: 100, height: 100)
|
||||
|
||||
Image(systemName: "envelope.open.fill")
|
||||
.font(.system(size: 40))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
.shadow(color: .blue.opacity(0.4), radius: 15, x: 0, y: 8)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 20, x: 0, y: 10)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +161,7 @@ struct ForgotPasswordView: View {
|
||||
// Help text
|
||||
Text("Chúng tôi sẽ gửi link đặt lại mật khẩu đến email của bạn.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.white.opacity(0.5))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
}
|
||||
@@ -186,20 +177,29 @@ struct ForgotPasswordView: View {
|
||||
Image(systemName: "paperplane.fill")
|
||||
}
|
||||
.font(.headline)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: viewModel.isForgotPasswordValid
|
||||
? [Color(red: 0.2, green: 0.6, blue: 1.0), Color(red: 0.4, green: 0.3, blue: 0.9)]
|
||||
: [Color.gray, Color.gray.opacity(0.8)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
Group {
|
||||
if viewModel.isForgotPasswordValid {
|
||||
AppTheme.warmGradient
|
||||
} else {
|
||||
LinearGradient(
|
||||
colors: [Color.gray.opacity(0.3), Color.gray.opacity(0.2)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.shadow(color: viewModel.isForgotPasswordValid ? .blue.opacity(0.4) : .clear, radius: 10, x: 0, y: 5)
|
||||
.shadow(
|
||||
color: viewModel.isForgotPasswordValid ? AppTheme.buttonShadow : .clear,
|
||||
radius: 15,
|
||||
x: 0,
|
||||
y: 8
|
||||
)
|
||||
}
|
||||
.disabled(!viewModel.isForgotPasswordValid)
|
||||
}
|
||||
@@ -215,7 +215,7 @@ struct ForgotPasswordView: View {
|
||||
Text("Quay lại đăng nhập")
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// LoginView.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Login screen view
|
||||
// View màn hình đăng nhập
|
||||
// Login screen view - Pastel Yellow Theme
|
||||
// View màn hình đăng nhập - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -83,34 +83,25 @@ struct LoginView: View {
|
||||
// Logo
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Color(red: 0.2, green: 0.6, blue: 1.0),
|
||||
Color(red: 0.4, green: 0.3, blue: 0.9)
|
||||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.frame(width: 70, height: 70)
|
||||
.fill(AppTheme.primaryGradient)
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
Image(systemName: "location.fill")
|
||||
.font(.system(size: 30))
|
||||
.foregroundStyle(.white)
|
||||
.font(.system(size: 32))
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
.shadow(color: .blue.opacity(0.4), radius: 15, x: 0, y: 8)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 20, x: 0, y: 10)
|
||||
|
||||
// Title
|
||||
VStack(spacing: DesignSystem.spacingXS) {
|
||||
Text("Chào mừng trở lại!")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text("Đăng nhập để tiếp tục")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +139,7 @@ struct LoginView: View {
|
||||
} label: {
|
||||
Text("Quên mật khẩu?")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,20 +156,29 @@ struct LoginView: View {
|
||||
Image(systemName: "arrow.right")
|
||||
}
|
||||
.font(.headline)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: viewModel.isLoginValid
|
||||
? [Color(red: 0.2, green: 0.6, blue: 1.0), Color(red: 0.4, green: 0.3, blue: 0.9)]
|
||||
: [Color.gray, Color.gray.opacity(0.8)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
Group {
|
||||
if viewModel.isLoginValid {
|
||||
AppTheme.warmGradient
|
||||
} else {
|
||||
LinearGradient(
|
||||
colors: [Color.gray.opacity(0.3), Color.gray.opacity(0.2)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.shadow(color: viewModel.isLoginValid ? .blue.opacity(0.4) : .clear, radius: 10, x: 0, y: 5)
|
||||
.shadow(
|
||||
color: viewModel.isLoginValid ? AppTheme.buttonShadow : .clear,
|
||||
radius: 15,
|
||||
x: 0,
|
||||
y: 8
|
||||
)
|
||||
}
|
||||
.disabled(!viewModel.isLoginValid)
|
||||
}
|
||||
@@ -188,15 +188,15 @@ struct LoginView: View {
|
||||
private var dividerSection: some View {
|
||||
HStack(spacing: DesignSystem.spacingMD) {
|
||||
Rectangle()
|
||||
.fill(Color.white.opacity(0.2))
|
||||
.fill(Color.textSecondary.opacity(0.3))
|
||||
.frame(height: 1)
|
||||
|
||||
Text("hoặc")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.5))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
|
||||
Rectangle()
|
||||
.fill(Color.white.opacity(0.2))
|
||||
.fill(Color.textSecondary.opacity(0.3))
|
||||
.frame(height: 1)
|
||||
}
|
||||
}
|
||||
@@ -205,7 +205,7 @@ struct LoginView: View {
|
||||
/// Các nút đăng nhập mạng xã hội
|
||||
private var socialButtons: some View {
|
||||
HStack(spacing: DesignSystem.spacingMD) {
|
||||
SocialLoginButton(icon: "apple.logo", color: .white) {
|
||||
SocialLoginButton(icon: "apple.logo", color: .black) {
|
||||
// Apple login
|
||||
}
|
||||
|
||||
@@ -224,14 +224,14 @@ struct LoginView: View {
|
||||
private var registerSection: some View {
|
||||
HStack(spacing: DesignSystem.spacingXS) {
|
||||
Text("Chưa có tài khoản?")
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
|
||||
Button {
|
||||
viewModel.navigateTo(.register)
|
||||
} label: {
|
||||
Text("Đăng ký ngay")
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
.font(.subheadline)
|
||||
@@ -252,8 +252,8 @@ struct LoginView: View {
|
||||
// MARK: - Auth Text Field
|
||||
// Text Field Auth
|
||||
|
||||
/// Custom text field for auth forms
|
||||
/// Text field tùy chỉnh cho form auth
|
||||
/// Custom text field for auth forms - Pastel Theme
|
||||
/// Text field tùy chỉnh cho form auth - Theme Pastel
|
||||
struct AuthTextField: View {
|
||||
let icon: String
|
||||
let placeholder: String
|
||||
@@ -264,31 +264,32 @@ struct AuthTextField: View {
|
||||
HStack(spacing: DesignSystem.spacingMD) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 18))
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
.frame(width: 24)
|
||||
|
||||
TextField("", text: $text, prompt: Text(placeholder).foregroundStyle(.white.opacity(0.4)))
|
||||
.foregroundStyle(.white)
|
||||
TextField("", text: $text, prompt: Text(placeholder).foregroundStyle(Color.textSecondary.opacity(0.6)))
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.keyboardType(keyboardType)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
}
|
||||
.padding(.horizontal, DesignSystem.spacingMD)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(Color.white.opacity(0.1))
|
||||
.background(Color.cardBackground)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: DesignSystem.cornerRadiusMD)
|
||||
.stroke(Color.white.opacity(0.2), lineWidth: 1)
|
||||
.stroke(Color.brandPrimary.opacity(0.5), lineWidth: 1.5)
|
||||
)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 5, x: 0, y: 2)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Auth Secure Field
|
||||
// Secure Field Auth
|
||||
|
||||
/// Custom secure field for auth forms
|
||||
/// Secure field tùy chỉnh cho form auth
|
||||
/// Custom secure field for auth forms - Pastel Theme
|
||||
/// Secure field tùy chỉnh cho form auth - Theme Pastel
|
||||
struct AuthSecureField: View {
|
||||
let icon: String
|
||||
let placeholder: String
|
||||
@@ -299,17 +300,17 @@ struct AuthSecureField: View {
|
||||
HStack(spacing: DesignSystem.spacingMD) {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 18))
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
.frame(width: 24)
|
||||
|
||||
Group {
|
||||
if isVisible {
|
||||
TextField("", text: $text, prompt: Text(placeholder).foregroundStyle(.white.opacity(0.4)))
|
||||
TextField("", text: $text, prompt: Text(placeholder).foregroundStyle(Color.textSecondary.opacity(0.6)))
|
||||
} else {
|
||||
SecureField("", text: $text, prompt: Text(placeholder).foregroundStyle(.white.opacity(0.4)))
|
||||
SecureField("", text: $text, prompt: Text(placeholder).foregroundStyle(Color.textSecondary.opacity(0.6)))
|
||||
}
|
||||
}
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.textInputAutocapitalization(.never)
|
||||
.autocorrectionDisabled()
|
||||
|
||||
@@ -317,25 +318,26 @@ struct AuthSecureField: View {
|
||||
isVisible.toggle()
|
||||
} label: {
|
||||
Image(systemName: isVisible ? "eye.slash.fill" : "eye.fill")
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, DesignSystem.spacingMD)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(Color.white.opacity(0.1))
|
||||
.background(Color.cardBackground)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: DesignSystem.cornerRadiusMD)
|
||||
.stroke(Color.white.opacity(0.2), lineWidth: 1)
|
||||
.stroke(Color.brandPrimary.opacity(0.5), lineWidth: 1.5)
|
||||
)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 5, x: 0, y: 2)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Social Login Button
|
||||
// Nút đăng nhập mạng xã hội
|
||||
|
||||
/// Social login button component
|
||||
/// Component nút đăng nhập mạng xã hội
|
||||
/// Social login button component - Pastel Theme
|
||||
/// Component nút đăng nhập mạng xã hội - Theme Pastel
|
||||
struct SocialLoginButton: View {
|
||||
let icon: String
|
||||
let color: Color
|
||||
@@ -347,12 +349,13 @@ struct SocialLoginButton: View {
|
||||
.font(.system(size: 24))
|
||||
.foregroundStyle(color)
|
||||
.frame(width: 56, height: 56)
|
||||
.background(Color.white.opacity(0.1))
|
||||
.background(Color.cardBackground)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: DesignSystem.cornerRadiusMD)
|
||||
.stroke(Color.white.opacity(0.2), lineWidth: 1)
|
||||
.stroke(Color.brandPrimary.opacity(0.3), lineWidth: 1)
|
||||
)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 5, x: 0, y: 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// RegisterView.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Registration screen view
|
||||
// View màn hình đăng ký
|
||||
// Registration screen view - Pastel Yellow Theme
|
||||
// View màn hình đăng ký - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -84,7 +84,7 @@ struct RegisterView: View {
|
||||
Text("Quay lại")
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
@@ -95,11 +95,11 @@ struct RegisterView: View {
|
||||
Text("Tạo tài khoản")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text("Điền thông tin để đăng ký")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ struct RegisterView: View {
|
||||
RoundedRectangle(cornerRadius: 2)
|
||||
.fill(index < viewModel.passwordStrength
|
||||
? viewModel.passwordStrengthColor
|
||||
: Color.white.opacity(0.2))
|
||||
: Color.textSecondary.opacity(0.2))
|
||||
.frame(height: 4)
|
||||
}
|
||||
}
|
||||
@@ -180,8 +180,8 @@ struct RegisterView: View {
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(viewModel.registerPassword == viewModel.registerConfirmPassword
|
||||
? .green
|
||||
: .red)
|
||||
? Color.success
|
||||
: Color.error)
|
||||
}
|
||||
|
||||
// Terms checkbox
|
||||
@@ -198,19 +198,19 @@ struct RegisterView: View {
|
||||
} label: {
|
||||
HStack(alignment: .top, spacing: DesignSystem.spacingSM) {
|
||||
Image(systemName: viewModel.agreedToTerms ? "checkmark.square.fill" : "square")
|
||||
.foregroundStyle(viewModel.agreedToTerms ? .blue : .white.opacity(0.6))
|
||||
.foregroundStyle(viewModel.agreedToTerms ? Color.brandAccent : Color.textSecondary)
|
||||
|
||||
Text("Tôi đồng ý với ")
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
+
|
||||
Text("Điều khoản sử dụng")
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
+
|
||||
Text(" và ")
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
+
|
||||
Text("Chính sách bảo mật")
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
.font(.subheadline)
|
||||
.multilineTextAlignment(.leading)
|
||||
@@ -228,20 +228,29 @@ struct RegisterView: View {
|
||||
Image(systemName: "arrow.right")
|
||||
}
|
||||
.font(.headline)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: viewModel.isRegisterValid
|
||||
? [Color(red: 0.2, green: 0.6, blue: 1.0), Color(red: 0.4, green: 0.3, blue: 0.9)]
|
||||
: [Color.gray, Color.gray.opacity(0.8)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
Group {
|
||||
if viewModel.isRegisterValid {
|
||||
AppTheme.warmGradient
|
||||
} else {
|
||||
LinearGradient(
|
||||
colors: [Color.gray.opacity(0.3), Color.gray.opacity(0.2)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.shadow(color: viewModel.isRegisterValid ? .blue.opacity(0.4) : .clear, radius: 10, x: 0, y: 5)
|
||||
.shadow(
|
||||
color: viewModel.isRegisterValid ? AppTheme.buttonShadow : .clear,
|
||||
radius: 15,
|
||||
x: 0,
|
||||
y: 8
|
||||
)
|
||||
}
|
||||
.disabled(!viewModel.isRegisterValid)
|
||||
}
|
||||
@@ -251,14 +260,14 @@ struct RegisterView: View {
|
||||
private var loginSection: some View {
|
||||
HStack(spacing: DesignSystem.spacingXS) {
|
||||
Text("Đã có tài khoản?")
|
||||
.foregroundStyle(.white.opacity(0.6))
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
|
||||
Button {
|
||||
viewModel.navigateTo(.login)
|
||||
} label: {
|
||||
Text("Đăng nhập")
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
.font(.subheadline)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// ActivityFeed.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Recent activity/transaction feed
|
||||
// Feed hoạt động/giao dịch gần đây
|
||||
// Recent activity/transaction feed - Pastel Yellow Theme
|
||||
// Feed hoạt động/giao dịch gần đây - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -34,7 +34,7 @@ struct ActivityItem: Identifiable {
|
||||
subtitle: "Quận 1 → Quận 7",
|
||||
amount: -45000,
|
||||
icon: "car.fill",
|
||||
color: .green,
|
||||
color: Color.success,
|
||||
time: "10:30"
|
||||
),
|
||||
ActivityItem(
|
||||
@@ -42,7 +42,7 @@ struct ActivityItem: Identifiable {
|
||||
subtitle: "Từ Nguyễn Văn A",
|
||||
amount: 200000,
|
||||
icon: "arrow.down.circle.fill",
|
||||
color: .blue,
|
||||
color: Color.info,
|
||||
time: "Hôm qua"
|
||||
),
|
||||
ActivityItem(
|
||||
@@ -50,7 +50,7 @@ struct ActivityItem: Identifiable {
|
||||
subtitle: "EVN HCM",
|
||||
amount: -350000,
|
||||
icon: "bolt.fill",
|
||||
color: .yellow,
|
||||
color: Color.brandAccent,
|
||||
time: "20/01"
|
||||
),
|
||||
ActivityItem(
|
||||
@@ -58,7 +58,7 @@ struct ActivityItem: Identifiable {
|
||||
subtitle: "Khuyến mãi đặt xe",
|
||||
amount: 15000,
|
||||
icon: "gift.fill",
|
||||
color: .pink,
|
||||
color: Color.brandSecondary,
|
||||
time: "19/01"
|
||||
)
|
||||
]
|
||||
@@ -97,6 +97,7 @@ struct ActivityFeed: View {
|
||||
Text("Hoạt động gần đây")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -104,7 +105,7 @@ struct ActivityFeed: View {
|
||||
// Show all activities
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
|
||||
// Activity list
|
||||
@@ -122,9 +123,9 @@ struct ActivityFeed: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(Color.white)
|
||||
.background(Color.cardBackground)
|
||||
.cornerRadius(16)
|
||||
.shadow(color: .black.opacity(0.05), radius: 10, x: 0, y: 5)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 10, x: 0, y: 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,11 +157,11 @@ struct ActivityRow: View {
|
||||
Text(activity.title)
|
||||
.font(.subheadline)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.primary)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text(activity.subtitle)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -170,11 +171,11 @@ struct ActivityRow: View {
|
||||
Text(activity.isPositive ? "+\(String.formatVND(activity.amount))" : String.formatVND(activity.amount))
|
||||
.font(.subheadline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(activity.isPositive ? .green : .primary)
|
||||
.foregroundStyle(activity.isPositive ? Color.success : Color.textPrimary)
|
||||
|
||||
Text(activity.time)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
@@ -186,7 +187,7 @@ struct ActivityRow: View {
|
||||
|
||||
#Preview {
|
||||
ZStack {
|
||||
Color.gray.opacity(0.1).ignoresSafeArea()
|
||||
Color.appBackground.ignoresSafeArea()
|
||||
ActivityFeed()
|
||||
.padding()
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// PromoCarousel.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Promotional banners carousel
|
||||
// Carousel banners khuyến mãi
|
||||
// Promotional banners carousel - Pastel Yellow Theme
|
||||
// Carousel banners khuyến mãi - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -24,19 +24,19 @@ struct PromoItem: Identifiable {
|
||||
PromoItem(
|
||||
title: "Giảm 50% Đặt xe",
|
||||
subtitle: "Áp dụng đến 31/01",
|
||||
gradientColors: [.orange, .red],
|
||||
gradientColors: [Color.brandPrimary, Color.brandAccent],
|
||||
icon: "car.fill"
|
||||
),
|
||||
PromoItem(
|
||||
title: "Freeship Đồ ăn",
|
||||
subtitle: "Đơn từ 50K",
|
||||
gradientColors: [.green, .mint],
|
||||
gradientColors: [Color.success, Color.success.opacity(0.7)],
|
||||
icon: "takeoutbag.and.cup.and.straw.fill"
|
||||
),
|
||||
PromoItem(
|
||||
title: "Hoàn tiền 20%",
|
||||
subtitle: "Thanh toán hóa đơn",
|
||||
gradientColors: [.blue, .purple],
|
||||
gradientColors: [Color.info, Color.brandSecondary],
|
||||
icon: "creditcard.fill"
|
||||
)
|
||||
]
|
||||
@@ -88,7 +88,7 @@ struct PromoCarousel: View {
|
||||
HStack(spacing: 6) {
|
||||
ForEach(0..<items.count, id: \.self) { index in
|
||||
Circle()
|
||||
.fill(index == currentIndex ? Color.accentColor : Color.gray.opacity(0.3))
|
||||
.fill(index == currentIndex ? Color.brandAccent : Color.textSecondary.opacity(0.3))
|
||||
.frame(width: index == currentIndex ? 8 : 6, height: index == currentIndex ? 8 : 6)
|
||||
.animation(.easeInOut(duration: 0.2), value: currentIndex)
|
||||
}
|
||||
@@ -138,11 +138,11 @@ struct PromoCard: View {
|
||||
Text(item.title)
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text(item.subtitle)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
.foregroundStyle(Color.textPrimary.opacity(0.7))
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -150,10 +150,10 @@ struct PromoCard: View {
|
||||
Text("Xem ngay →")
|
||||
.font(.caption)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(.white.opacity(0.2))
|
||||
.background(Color.white.opacity(0.5))
|
||||
.cornerRadius(12)
|
||||
}
|
||||
.padding(20)
|
||||
@@ -163,7 +163,7 @@ struct PromoCard: View {
|
||||
// Icon
|
||||
Image(systemName: item.icon)
|
||||
.font(.system(size: 50))
|
||||
.foregroundStyle(.white.opacity(0.3))
|
||||
.foregroundStyle(Color.textPrimary.opacity(0.2))
|
||||
.padding(.trailing, 20)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -175,6 +175,7 @@ struct PromoCard: View {
|
||||
)
|
||||
)
|
||||
.cornerRadius(16)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 10, x: 0, y: 5)
|
||||
.padding(.horizontal, 4)
|
||||
}
|
||||
}
|
||||
@@ -182,6 +183,9 @@ struct PromoCard: View {
|
||||
// MARK: - Preview
|
||||
|
||||
#Preview {
|
||||
PromoCarousel()
|
||||
.padding()
|
||||
ZStack {
|
||||
Color.appBackground.ignoresSafeArea()
|
||||
PromoCarousel()
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// ServiceGrid.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Services grid with icons and labels
|
||||
// Grid services với icons và labels
|
||||
// Services grid with icons and labels - Pastel Yellow Theme
|
||||
// Grid services với icons và labels - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -21,14 +21,14 @@ struct ServiceItem: Identifiable {
|
||||
let action: String
|
||||
|
||||
static let defaultServices: [ServiceItem] = [
|
||||
ServiceItem(icon: "car.fill", title: "Đặt xe", color: .orange, action: "booking"),
|
||||
ServiceItem(icon: "takeoutbag.and.cup.and.straw.fill", title: "Đồ ăn", color: .red, action: "food"),
|
||||
ServiceItem(icon: "creditcard.fill", title: "Thanh toán", color: .blue, action: "payment"),
|
||||
ServiceItem(icon: "arrow.left.arrow.right", title: "Chuyển tiền", color: .purple, action: "transfer"),
|
||||
ServiceItem(icon: "tag.fill", title: "Khuyến mãi", color: .pink, action: "promo"),
|
||||
ServiceItem(icon: "plus.circle.fill", title: "Nạp tiền", color: .green, action: "topup"),
|
||||
ServiceItem(icon: "ticket.fill", title: "Đặt vé", color: .orange, action: "tickets"),
|
||||
ServiceItem(icon: "ellipsis", title: "Thêm", color: .gray, action: "more")
|
||||
ServiceItem(icon: "car.fill", title: "Đặt xe", color: Color.brandAccent, action: "booking"),
|
||||
ServiceItem(icon: "takeoutbag.and.cup.and.straw.fill", title: "Đồ ăn", color: Color.error, action: "food"),
|
||||
ServiceItem(icon: "creditcard.fill", title: "Thanh toán", color: Color.info, action: "payment"),
|
||||
ServiceItem(icon: "arrow.left.arrow.right", title: "Chuyển tiền", color: Color.brandSecondary, action: "transfer"),
|
||||
ServiceItem(icon: "tag.fill", title: "Khuyến mãi", color: Color.warning, action: "promo"),
|
||||
ServiceItem(icon: "plus.circle.fill", title: "Nạp tiền", color: Color.success, action: "topup"),
|
||||
ServiceItem(icon: "ticket.fill", title: "Đặt vé", color: Color.brandPrimary, action: "tickets"),
|
||||
ServiceItem(icon: "ellipsis", title: "Thêm", color: Color.textSecondary, action: "more")
|
||||
]
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ struct ServiceGridItem: View {
|
||||
Text(service.title)
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.primary)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,9 @@ struct ServiceGridItem: View {
|
||||
// MARK: - Preview
|
||||
|
||||
#Preview {
|
||||
ServiceGrid()
|
||||
.padding()
|
||||
ZStack {
|
||||
Color.appBackground.ignoresSafeArea()
|
||||
ServiceGrid()
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// WalletCard.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Wallet card with balance and quick actions
|
||||
// Card ví với số dư và quick actions
|
||||
// Wallet card with balance and quick actions - Pastel Yellow Theme
|
||||
// Card ví với số dư và quick actions - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -59,17 +59,24 @@ struct WalletCard: View {
|
||||
}
|
||||
.padding(20)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Color(red: 0.4, green: 0.5, blue: 0.92),
|
||||
Color(red: 0.47, green: 0.3, blue: 0.64)
|
||||
],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
ZStack {
|
||||
// Main gradient
|
||||
AppTheme.warmGradient
|
||||
|
||||
// Decorative circles
|
||||
Circle()
|
||||
.fill(Color.white.opacity(0.1))
|
||||
.frame(width: 150, height: 150)
|
||||
.offset(x: -80, y: -60)
|
||||
|
||||
Circle()
|
||||
.fill(Color.white.opacity(0.08))
|
||||
.frame(width: 100, height: 100)
|
||||
.offset(x: 120, y: 40)
|
||||
}
|
||||
)
|
||||
.cornerRadius(20)
|
||||
.shadow(color: Color(red: 0.4, green: 0.3, blue: 0.7).opacity(0.4), radius: 15, x: 0, y: 10)
|
||||
.shadow(color: AppTheme.buttonShadow, radius: 20, x: 0, y: 12)
|
||||
}
|
||||
|
||||
// MARK: - Subviews
|
||||
@@ -81,17 +88,17 @@ struct WalletCard: View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("Số dư khả dụng")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
.foregroundStyle(Color.textPrimary.opacity(0.7))
|
||||
|
||||
HStack(spacing: 8) {
|
||||
if isBalanceHidden {
|
||||
Text("••••••••")
|
||||
.font(.system(size: 28, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
} else {
|
||||
Text(String.formatVND(balance))
|
||||
.font(.system(size: 28, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
|
||||
Button {
|
||||
@@ -101,7 +108,7 @@ struct WalletCard: View {
|
||||
} label: {
|
||||
Image(systemName: isBalanceHidden ? "eye.slash.fill" : "eye.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white.opacity(0.8))
|
||||
.foregroundStyle(Color.textPrimary.opacity(0.6))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,16 +120,16 @@ struct WalletCard: View {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "star.circle.fill")
|
||||
.foregroundStyle(.yellow)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
Text("1,250")
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Text("điểm thưởng")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.white.opacity(0.7))
|
||||
.foregroundStyle(Color.textPrimary.opacity(0.6))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,18 +145,18 @@ struct WalletCard: View {
|
||||
VStack(spacing: 8) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(.white.opacity(0.2))
|
||||
.fill(Color.white.opacity(0.4))
|
||||
.frame(width: 48, height: 48)
|
||||
|
||||
Image(systemName: action.icon)
|
||||
.font(.title2)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
|
||||
Text(action.rawValue)
|
||||
.font(.caption)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -162,7 +169,7 @@ struct WalletCard: View {
|
||||
|
||||
#Preview {
|
||||
ZStack {
|
||||
Color.gray.opacity(0.1).ignoresSafeArea()
|
||||
Color.appBackground.ignoresSafeArea()
|
||||
WalletCard(balance: 1_250_000)
|
||||
.padding()
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// HomeView.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Super App style home screen
|
||||
// Màn hình home phong cách Super App
|
||||
// Super App style home screen - Pastel Yellow Theme
|
||||
// Màn hình home phong cách Super App - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -48,6 +48,7 @@ struct HomeView: View {
|
||||
Text("Dịch vụ")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
ServiceGrid { service in
|
||||
@@ -63,6 +64,7 @@ struct HomeView: View {
|
||||
Text("Ưu đãi hấp dẫn")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
|
||||
Spacer()
|
||||
|
||||
@@ -70,7 +72,7 @@ struct HomeView: View {
|
||||
// Show all promos
|
||||
}
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.blue)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
|
||||
@@ -90,7 +92,7 @@ struct HomeView: View {
|
||||
}
|
||||
.padding(.top, 10)
|
||||
}
|
||||
.background(Color(red: 0.96, green: 0.97, blue: 0.98))
|
||||
.background(Color.appBackground)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
@@ -101,7 +103,7 @@ struct HomeView: View {
|
||||
} label: {
|
||||
Image(systemName: "qrcode.viewfinder")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.primary)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
|
||||
// Notifications
|
||||
@@ -110,7 +112,7 @@ struct HomeView: View {
|
||||
} label: {
|
||||
Image(systemName: "bell.badge")
|
||||
.font(.title3)
|
||||
.foregroundStyle(.primary)
|
||||
.foregroundStyle(Color.brandAccent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,11 +133,12 @@ struct HomeView: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(greetingText)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
|
||||
Text(authManager.currentUser?.name ?? "Người dùng")
|
||||
.font(.title3)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -161,6 +164,7 @@ struct HomeView: View {
|
||||
}
|
||||
.frame(width: 50, height: 50)
|
||||
.clipShape(Circle())
|
||||
.shadow(color: AppTheme.cardShadow, radius: 5, x: 0, y: 2)
|
||||
}
|
||||
|
||||
/// Avatar placeholder
|
||||
@@ -168,18 +172,12 @@ struct HomeView: View {
|
||||
private var avatarPlaceholder: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [.blue, .purple],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.fill(AppTheme.primaryGradient)
|
||||
|
||||
Text(authManager.currentUser?.initials ?? "U")
|
||||
.font(.headline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +187,11 @@ struct HomeView: View {
|
||||
let hour = Calendar.current.component(.hour, from: Date())
|
||||
switch hour {
|
||||
case 5..<12:
|
||||
return "Chào buổi sáng 👋"
|
||||
return "Chào buổi sáng"
|
||||
case 12..<18:
|
||||
return "Chào buổi chiều ☀️"
|
||||
return "Chào buổi chiều"
|
||||
default:
|
||||
return "Chào buổi tối 🌙"
|
||||
return "Chào buổi tối"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,11 +79,11 @@ struct ProfileView: View {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "checkmark.seal.fill")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.green)
|
||||
.foregroundStyle(Color.success)
|
||||
|
||||
Text("profile_verified".localized)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.green)
|
||||
.foregroundStyle(Color.success)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,18 +124,12 @@ struct ProfileView: View {
|
||||
private var avatarPlaceholder: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [.blue, .purple],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.fill(AppTheme.primaryGradient)
|
||||
|
||||
Text(viewModel.user?.initials ?? "GU")
|
||||
.font(.title2)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,15 +155,15 @@ struct ProfileView: View {
|
||||
Image(systemName: item.icon)
|
||||
.font(.body)
|
||||
.foregroundStyle(
|
||||
item.action == .logout ? .red :
|
||||
item.action == .apiDebugger ? .orange :
|
||||
.accentColor
|
||||
item.action == .logout ? Color.error :
|
||||
item.action == .apiDebugger ? Color.warning :
|
||||
Color.brandAccent
|
||||
)
|
||||
.frame(width: 24)
|
||||
|
||||
// Title
|
||||
Text(item.title)
|
||||
.foregroundStyle(item.action == .logout ? .red : .primary)
|
||||
.foregroundStyle(item.action == .logout ? Color.error : Color.textPrimary)
|
||||
|
||||
Spacer()
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// WelcomeView.swift
|
||||
// AppClientBaseSwift
|
||||
//
|
||||
// Onboarding/Welcome screen view
|
||||
// View màn hình Onboarding/Welcome
|
||||
// Onboarding/Welcome screen view - Pastel Yellow Theme
|
||||
// View màn hình Onboarding/Welcome - Theme Pastel Vàng
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
@@ -48,25 +48,46 @@ struct WelcomeView: View {
|
||||
// MARK: - Body
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
// Page content
|
||||
// Nội dung trang
|
||||
TabView(selection: $currentPage) {
|
||||
ForEach(0..<pages.count, id: \.self) { index in
|
||||
onboardingPage(index: index)
|
||||
.tag(index)
|
||||
}
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
.animation(.easeInOut, value: currentPage)
|
||||
ZStack {
|
||||
// Background
|
||||
Color.appBackground
|
||||
.ignoresSafeArea()
|
||||
|
||||
// Bottom section
|
||||
// Phần dưới
|
||||
bottomSection
|
||||
.padding(.horizontal, DesignSystem.spacingLG)
|
||||
.padding(.bottom, DesignSystem.spacingXL)
|
||||
// Decorative circles
|
||||
GeometryReader { geometry in
|
||||
Circle()
|
||||
.fill(AppTheme.softGradient)
|
||||
.frame(width: 300, height: 300)
|
||||
.blur(radius: 60)
|
||||
.offset(x: -100, y: 100)
|
||||
|
||||
Circle()
|
||||
.fill(Color.brandSecondary.opacity(0.2))
|
||||
.frame(width: 200, height: 200)
|
||||
.blur(radius: 40)
|
||||
.offset(x: geometry.size.width - 80, y: geometry.size.height - 300)
|
||||
}
|
||||
.ignoresSafeArea()
|
||||
|
||||
VStack(spacing: 0) {
|
||||
// Page content
|
||||
// Nội dung trang
|
||||
TabView(selection: $currentPage) {
|
||||
ForEach(0..<pages.count, id: \.self) { index in
|
||||
onboardingPage(index: index)
|
||||
.tag(index)
|
||||
}
|
||||
}
|
||||
.tabViewStyle(.page(indexDisplayMode: .never))
|
||||
.animation(.easeInOut, value: currentPage)
|
||||
|
||||
// Bottom section
|
||||
// Phần dưới
|
||||
bottomSection
|
||||
.padding(.horizontal, DesignSystem.spacingLG)
|
||||
.padding(.bottom, DesignSystem.spacingXL)
|
||||
}
|
||||
}
|
||||
.ignoresSafeArea(edges: .top)
|
||||
}
|
||||
|
||||
// MARK: - Subviews
|
||||
@@ -81,20 +102,14 @@ struct WelcomeView: View {
|
||||
// Icon
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [.blue.opacity(0.8), .purple.opacity(0.8)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.fill(AppTheme.primaryGradient)
|
||||
.frame(width: 140, height: 140)
|
||||
|
||||
Image(systemName: pages[index].icon)
|
||||
.font(.system(size: 60))
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
}
|
||||
.shadow(color: .blue.opacity(0.3), radius: 20, x: 0, y: 10)
|
||||
.shadow(color: AppTheme.cardShadow, radius: 25, x: 0, y: 12)
|
||||
|
||||
// Text content
|
||||
// Nội dung text
|
||||
@@ -102,11 +117,12 @@ struct WelcomeView: View {
|
||||
Text(pages[index].title)
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
Text(pages[index].description)
|
||||
.font(.body)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(3)
|
||||
}
|
||||
@@ -126,7 +142,7 @@ struct WelcomeView: View {
|
||||
HStack(spacing: DesignSystem.spacingSM) {
|
||||
ForEach(0..<pages.count, id: \.self) { index in
|
||||
Circle()
|
||||
.fill(index == currentPage ? Color.accentColor : Color.gray.opacity(0.3))
|
||||
.fill(index == currentPage ? Color.brandAccent : Color.textSecondary.opacity(0.3))
|
||||
.frame(
|
||||
width: index == currentPage ? 10 : 8,
|
||||
height: index == currentPage ? 10 : 8
|
||||
@@ -148,7 +164,7 @@ struct WelcomeView: View {
|
||||
} label: {
|
||||
Text("common_back".localized)
|
||||
.font(.headline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
}
|
||||
@@ -171,17 +187,12 @@ struct WelcomeView: View {
|
||||
)
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(Color.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, DesignSystem.spacingMD)
|
||||
.background(
|
||||
LinearGradient(
|
||||
colors: [.blue, .purple],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
)
|
||||
.background(AppTheme.warmGradient)
|
||||
.cornerRadius(DesignSystem.cornerRadiusMD)
|
||||
.shadow(color: AppTheme.buttonShadow, radius: 10, x: 0, y: 5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +204,7 @@ struct WelcomeView: View {
|
||||
} label: {
|
||||
Text("common_skip".localized)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.foregroundStyle(Color.textSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user