Skip to content

Commit c69d970

Browse files
authored
Merge pull request #47 from YAPP-Github/TNT-205-alarmPage
[TNT-205] AlarmCheck ํ™”๋ฉด ์ž‘์„ฑ
2 parents 9dcef4f + 8a41354 commit c69d970

File tree

7 files changed

+298
-0
lines changed

7 files changed

+298
-0
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// AlarmItemEntity.swift
3+
// Domain
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// ์•Œ๋žŒ ํ™•์ธ์‹œ ์‚ฌ์šฉ๋˜๋Š” ์•Œ๋žŒ ์ •๋ณด ๊ตฌ์กฐ์ฒด
12+
public struct AlarmItemEntity: Equatable {
13+
/// ์•Œ๋žŒ id
14+
public let alarmId: Int
15+
/// ์•Œ๋žŒ ํƒ€์ž…
16+
public let alarmType: AlarmType
17+
/// ์•Œ๋žŒ ๋„์ฐฉ ์‹œ๊ฐ
18+
public let alarmDate: Date
19+
/// ์•Œ๋žŒ ํ™•์ธ ์—ฌ๋ถ€
20+
public let alarmSeenBefore: Bool
21+
22+
public init(
23+
alarmId: Int,
24+
alarmType: AlarmType,
25+
alarmDate: Date,
26+
alarmSeenBefore: Bool
27+
) {
28+
self.alarmId = alarmId
29+
self.alarmType = alarmType
30+
self.alarmDate = alarmDate
31+
self.alarmSeenBefore = alarmSeenBefore
32+
}
33+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// AlarmType.swift
3+
// Domain
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/// ์•ฑ์—์„œ ์กด์žฌํ•˜๋Š” ์•Œ๋žŒ ์œ ํ˜•์„ ์ •์˜ํ•œ ์—ด๊ฑฐํ˜•
12+
public enum AlarmType: Equatable {
13+
/// ํŠธ๋ ˆ์ด๋‹ˆ ์—ฐ๊ฒฐ ์™„๋ฃŒ
14+
case traineeConnected(name: String)
15+
/// ํŠธ๋ ˆ์ด๋‹ˆ ์—ฐ๊ฒฐ ํ•ด์ œ
16+
case traineeDisconnected(name: String)
17+
/// ํŠธ๋ ˆ์ด๋„ˆ ์—ฐ๊ฒฐ ํ•ด์ œ
18+
case trainerDisconnected(name: String)
19+
}

โ€ŽTnT/Projects/Domain/Sources/Policy/TDateFormat.swiftโ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public enum TDateFormat: String {
2222
case yyyyMMddSlash = "yyyy/MM/dd"
2323
/// "yyyy.MM.dd"
2424
case yyyyMMddDot = "yyyy.MM.dd"
25+
/// ""M์›” d์ผ"
26+
case M์›”_d์ผ = "M์›” d์ผ"
2527
/// "01์›” 10์ผ ํ™”์š”์ผ"
2628
case MM์›”_dd์ผ_EEEE = "MM์›” dd์ผ EEEE"
2729
/// "EE"
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//
2+
// AlarmCheckFeature.swift
3+
// Presentation
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import ComposableArchitecture
11+
12+
import Domain
13+
import DesignSystem
14+
15+
@Reducer
16+
public struct AlarmCheckFeature {
17+
18+
@ObservableState
19+
public struct State: Equatable {
20+
// MARK: Data related state
21+
/// ์œ ์ € ํƒ€์ž…
22+
var userType: UserType
23+
/// ์•Œ๋žŒ ์ •๋ณด ๋ชฉ๋ก
24+
var alarmList: [AlarmItemEntity]
25+
26+
/// `AlarmCheckFeature.State`์˜ ์ƒ์„ฑ์ž
27+
/// - Parameters:
28+
/// - userType: ์œ ์ € ํƒ€์ž…
29+
/// - alarmList: ์œ ์ €์—๊ฒŒ ๋„์ฐฉํ•œ ์•Œ๋žŒ ๋ชฉ๋ก (๊ธฐ๋ณธ๊ฐ’: `[]`)
30+
public init(
31+
userType: UserType,
32+
alarmList: [AlarmItemEntity] = []
33+
) {
34+
self.userType = userType
35+
self.alarmList = alarmList
36+
}
37+
}
38+
39+
@Dependency(\.dismiss) private var dismiss
40+
41+
public enum Action: Sendable, ViewAction {
42+
/// ๋ทฐ์—์„œ ๋ฐœ์ƒํ•œ ์•ก์…˜์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
43+
case view(View)
44+
/// ๋„ค๋น„๊ฒŒ์ด์…˜ ์—ฌ๋ถ€ ์„ค์ •
45+
case setNavigating
46+
47+
@CasePathable
48+
public enum View: Sendable, BindableAction {
49+
/// ๋ฐ”์ธ๋”ฉ ์•ก์…˜ ์ฒ˜๋ฆฌ
50+
case binding(BindingAction<State>)
51+
/// ์•Œ๋žŒ ์•„์ดํ…œ ํƒญ ๋˜์—ˆ์„ ๋•Œ
52+
case tapAlarmItem(Int)
53+
/// ๋„ค๋น„๊ฒŒ์ด์…˜ back ๋ฒ„ํŠผ ํƒญ ๋˜์—ˆ์„ ๋•Œ
54+
case tapNavBackButton
55+
}
56+
}
57+
58+
public init() {}
59+
60+
public var body: some ReducerOf<Self> {
61+
BindingReducer(action: \.view)
62+
63+
Reduce { state, action in
64+
switch action {
65+
case .view(let action):
66+
switch action {
67+
case .binding:
68+
return .none
69+
70+
case .tapAlarmItem(let id):
71+
print("alarmId: \(id)")
72+
return .none
73+
74+
case .tapNavBackButton:
75+
return .run { _ in
76+
// TODO: ์„œ๋ฒ„ API ๋ช…์„ธ ๋‚˜์˜ค๋ฉด ์—ฐ๊ฒฐ
77+
// ํ˜„์žฌ ๋ชจ๋“  ์•Œ๋žŒ ํ™•์ธ ํ‘œ์‹œ
78+
await self.dismiss()
79+
}
80+
}
81+
case .setNavigating:
82+
return .none
83+
}
84+
}
85+
}
86+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// AlarmCheckView.swift
3+
// Presentation
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import SwiftUI
10+
import ComposableArchitecture
11+
12+
import Domain
13+
import DesignSystem
14+
15+
/// ์•Œ๋žŒ ๋ชฉ๋ก์„ ์ž…๋ ฅํ•˜๋Š” ํ™”๋ฉด
16+
/// ์œ ์ €์—๊ฒŒ ๋„์ฐฉํ•œ ์•Œ๋žŒ์„ ํ‘œ์‹œ - ์œ ์ € ํƒ€์ž…์— ๋”ฐ๋ผ ๋ถ„๋ฅ˜
17+
@ViewAction(for: AlarmCheckFeature.self)
18+
public struct AlarmCheckView: View {
19+
20+
@Bindable public var store: StoreOf<AlarmCheckFeature>
21+
@Environment(\.dismiss) var dismiss: DismissAction
22+
23+
public init(store: StoreOf<AlarmCheckFeature>) {
24+
self.store = store
25+
}
26+
27+
public var body: some View {
28+
VStack(spacing: 0) {
29+
TNavigation(
30+
type: .LButtonWithTitle(leftImage: .icnArrowLeft, centerTitle: "์•Œ๋ฆผ"),
31+
leftAction: { send(.tapNavBackButton) }
32+
)
33+
34+
ScrollView {
35+
AlarmList()
36+
Spacer()
37+
}
38+
}
39+
.navigationBarBackButtonHidden(true)
40+
}
41+
42+
// MARK: - Sections
43+
@ViewBuilder
44+
private func AlarmList() -> some View {
45+
VStack(spacing: 0) {
46+
ForEach(store.alarmList, id: \.alarmId) { item in
47+
AlarmListItem(
48+
alarmTypeText: item.alarmTypeText,
49+
alarmMainText: item.alarmMainText,
50+
alarmTimeText: item.alarmDate.timeAgoDisplay(),
51+
alarmSeenBefore: item.alarmSeenBefore
52+
)
53+
}
54+
}
55+
}
56+
}
57+
58+
private extension AlarmCheckView {
59+
struct AlarmListItem: View {
60+
/// ์—ฐ๊ฒฐ ์™„๋ฃŒ, ์—ฐ๊ฒฐ ํ•ด์ œ ๋“ฑ
61+
let alarmTypeText: String
62+
/// ์•Œ๋žŒ ๋ณธ๋ฌธ
63+
let alarmMainText: String
64+
/// ์•Œ๋žŒ ์‹œ๊ฐ
65+
let alarmTimeText: String
66+
/// ์•Œ๋žŒ ํ™•์ธ ์—ฌ๋ถ€
67+
let alarmSeenBefore: Bool
68+
69+
var body: some View {
70+
HStack(spacing: 16) {
71+
Image(.icnBombEmpty)
72+
.resizable()
73+
.scaledToFit()
74+
.frame(width: 32, height: 32)
75+
76+
VStack(alignment: .leading, spacing: 4) {
77+
HStack {
78+
Text(alarmTypeText)
79+
.typographyStyle(.label1Bold, with: .neutral400)
80+
.padding(.bottom, 3)
81+
Spacer()
82+
Text(alarmTimeText)
83+
.typographyStyle(.label1Medium, with: .neutral400)
84+
}
85+
86+
Text(alarmMainText)
87+
.typographyStyle(.body2Medium, with: .neutral800)
88+
}
89+
}
90+
.padding(20)
91+
.background(alarmSeenBefore ? Color.common0 : Color.neutral100)
92+
}
93+
}
94+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// Date+.swift
3+
// Presentation
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
import Domain
12+
13+
extension Date {
14+
func timeAgoDisplay() -> String {
15+
let now = Date()
16+
let calendar = Calendar.current
17+
let components = calendar.dateComponents([.second, .minute, .hour, .day], from: self, to: now)
18+
19+
if let day = components.day, day >= 1 {
20+
return TDateFormatUtility.formatter(for: .M์›”_d์ผ).string(from: self)
21+
} else if let hour = components.hour, hour >= 1 {
22+
return "\(hour)์‹œ๊ฐ„ ์ „"
23+
} else if let minute = components.minute, minute >= 1 {
24+
return "\(minute)๋ถ„ ์ „"
25+
} else {
26+
return "๋ฐฉ๊ธˆ"
27+
}
28+
}
29+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// AlarmItemEntity.swift
3+
// Presentation
4+
//
5+
// Created by ๋ฐ•๋ฏผ์„œ on 2/2/25.
6+
// Copyright ยฉ 2025 yapp25thTeamTnT. All rights reserved.
7+
//
8+
9+
import Domain
10+
11+
extension AlarmItemEntity {
12+
/// ์—ฐ๊ฒฐ ์™„๋ฃŒ, ์—ฐ๊ฒฐ ํ•ด์ œ ๋“ฑ
13+
var alarmTypeText: String {
14+
switch self.alarmType {
15+
case .traineeConnected:
16+
return "ํŠธ๋ ˆ์ด๋‹ˆ ์—ฐ๊ฒฐ ์™„๋ฃŒ"
17+
case .traineeDisconnected:
18+
return "ํŠธ๋ ˆ์ด๋‹ˆ ์—ฐ๊ฒฐ ํ•ด์ œ"
19+
case .trainerDisconnected:
20+
return "ํŠธ๋ ˆ์ด๋„ˆ ์—ฐ๊ฒฐ ํ•ด์ œ"
21+
}
22+
}
23+
24+
/// ์•Œ๋žŒ ๋ณธ๋ฌธ
25+
var alarmMainText: String {
26+
switch self.alarmType {
27+
case .traineeConnected(let name):
28+
return "\(name) ํšŒ์›๊ณผ ์—ฐ๊ฒฐ๋˜์—ˆ์–ด์š”"
29+
case .traineeDisconnected(let name):
30+
return "\(name) ํšŒ์›์ด ์—ฐ๊ฒฐ์„ ๋Š์—ˆ์–ด์š”"
31+
case .trainerDisconnected(let name):
32+
return "\(name) ํŠธ๋ ˆ์ด๋„ˆ๊ฐ€ ์—ฐ๊ฒฐ์„ ๋Š์—ˆ์–ด์š”"
33+
}
34+
}
35+
}

0 commit comments

Comments
ย (0)