Skip to content

Commit 090a6d4

Browse files
authored
Feat/report (#69) 제보하기 히스토리 화면 구현 (1차)
* feat: 제보하기 엔티티 구현 * refactor: 코드 리뷰 반영
1 parent e7740ce commit 090a6d4

16 files changed

Lines changed: 990 additions & 15 deletions

Projects/DataSource/Sources/Repository/LocationRepository.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ final class LocationRepository: NSObject, LocationRepositoryProtocol {
3737
}
3838

3939
func fetchAddress(coordinate: LocationEntity) async throws -> LocationEntity? {
40-
let endpoint = LocationEndpoint.fetchAddress(longitude: coordinate.longitude, latitude: coordinate.latitude)
40+
guard
41+
let longitude = coordinate.longitude,
42+
let latitude = coordinate.latitude
43+
else { return nil }
44+
45+
let endpoint = LocationEndpoint.fetchAddress(longitude: longitude, latitude: latitude)
4146

4247
guard let response = try await networkService.request(endpoint: endpoint, type: KakaoLocationResponseDTO.self)
4348
else { return nil }
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// ReportProgress.swift
3+
// Domain
4+
//
5+
// Created by 이동현 on 11/15/25.
6+
//
7+
8+
public enum ReportProgress: CaseIterable {
9+
case entire
10+
case received
11+
case inProgress
12+
case completed
13+
14+
public var description: String {
15+
switch self {
16+
case .entire:
17+
"전체"
18+
case .received:
19+
"제보 완료"
20+
case .inProgress:
21+
"처리 중"
22+
case .completed:
23+
"처리 완료"
24+
}
25+
}
26+
}

Projects/Domain/Sources/Entity/LocationEntity.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
//
77

88
public struct LocationEntity {
9-
public let longitude: Double
10-
public let latitude: Double
9+
public let longitude: Double?
10+
public let latitude: Double?
1111
public let address: String?
1212

1313
public init(
14-
longitude: Double,
15-
latitude: Double,
14+
longitude: Double?,
15+
latitude: Double?,
1616
address: String?
1717
) {
1818
self.longitude = longitude

Projects/Domain/Sources/Entity/ReportEntity.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,33 @@
66
//
77

88
public struct ReportEntity {
9+
public let id: Int
10+
public let title: String
11+
public let date: String?
12+
public let type: ReportType
13+
public let progress: ReportProgress
14+
public let content: String?
15+
public let location: LocationEntity
16+
public let photoUrls: [String]
17+
18+
public init(
19+
id: Int,
20+
title: String,
21+
date: String?,
22+
type: ReportType,
23+
progress: ReportProgress,
24+
content: String?,
25+
location: LocationEntity,
26+
photoUrls: [String]
27+
) {
28+
self.id = id
29+
self.title = title
30+
self.date = date
31+
self.type = type
32+
self.progress = progress
33+
self.content = content
34+
self.location = location
35+
self.photoUrls = photoUrls
36+
}
937

1038
}

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public struct PresentationDependencyAssembler: DependencyAssemblerProtocol {
128128
return ReportViewModel(reportUseCase: reportUseCase)
129129
}
130130

131+
DIContainer.shared
132+
.register(type: ReportListHistoryViewModel.self) { container in
133+
return ReportListHistoryViewModel()
134+
}
135+
131136
DIContainer.shared.register(type: ReportDetailViewModel.self) { container in
132137
return ReportDetailViewModel()
133138
}

Projects/Presentation/Sources/Report/Model/PhotoItem.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
import Foundation
99

10-
public struct PhotoItem: Hashable {
11-
public let id: UUID
12-
public let data: Data
10+
struct PhotoItem: Hashable {
11+
let id: UUID
12+
let data: Data
1313

14-
public init(id: UUID = .init(), data: Data) {
14+
init(id: UUID = .init(), data: Data) {
1515
self.id = id
1616
self.data = data
1717
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//
2+
// ReportHistoryItem.swift
3+
// Presentation
4+
//
5+
// Created by 이동현 on 11/19/25.
6+
//
7+
8+
import Domain
9+
10+
struct ReportHistoryItem: Hashable {
11+
let id: Int
12+
let title: String
13+
let thumbnailUrl: String
14+
let date: String
15+
let type: ReportType
16+
let progress: ReportProgress
17+
let location: String
18+
}
19+
20+
extension ReportHistoryItem {
21+
static var dummyData: [ReportHistoryItem] {
22+
let types = ReportType.allCases
23+
let progresses = ReportProgress.allCases.filter { $0 != .entire }
24+
25+
func makeItem(
26+
id: Int,
27+
title: String,
28+
date: String,
29+
location: String,
30+
typeIndex: Int,
31+
progressIndex: Int
32+
) -> ReportHistoryItem {
33+
let type = types[typeIndex % types.count]
34+
let progress = progresses[progressIndex % progresses.count]
35+
36+
return ReportHistoryItem(
37+
id: id,
38+
title: title,
39+
thumbnailUrl: "https://picsum.photos/200",
40+
date: date,
41+
type: type,
42+
progress: progress,
43+
location: location
44+
)
45+
}
46+
47+
return [
48+
makeItem(
49+
id: 1,
50+
title: "횡단보도 앞 가로등 불이 꺼져 있어요",
51+
date: "2025-11-19월",
52+
location: "서울시 강남구 삼성동",
53+
typeIndex: 0,
54+
progressIndex: 0
55+
),
56+
makeItem(
57+
id: 2,
58+
title: "지하철 역사 계단 난간이 파손되었어요",
59+
date: "2025-11-19월",
60+
location: "서울시 송파구 잠실동",
61+
typeIndex: 1,
62+
progressIndex: 1
63+
),
64+
65+
// 다른 날짜
66+
makeItem(
67+
id: 3,
68+
title: "공원 놀이터 바닥이 파여 있어 위험해요",
69+
date: "2025-11-18월",
70+
location: "서울시 마포구 상암동",
71+
typeIndex: 2,
72+
progressIndex: 2
73+
),
74+
makeItem(
75+
id: 4,
76+
title: "버스 정류장 안내판 조명이 나갔습니다",
77+
date: "2025-11-18월",
78+
location: "서울시 서초구 서초동",
79+
typeIndex: 3,
80+
progressIndex: 0
81+
),
82+
83+
makeItem(
84+
id: 5,
85+
title: "자전거 도로에 불법 주차된 차량이 있어요",
86+
date: "2025-11-17월",
87+
location: "서울시 노원구 공릉동",
88+
typeIndex: 1,
89+
progressIndex: 1
90+
),
91+
makeItem(
92+
id: 6,
93+
title: "보도블럭이 들떠서 걸려 넘어질 위험이 있어요",
94+
date: "2025-11-16월",
95+
location: "서울시 종로구 종로1가",
96+
typeIndex: 0,
97+
progressIndex: 2
98+
),
99+
100+
makeItem(
101+
id: 7,
102+
title: "교차로 신호등이 고장난 것 같습니다",
103+
date: "2025-11-16월",
104+
location: "서울시 동작구 사당동",
105+
typeIndex: 2,
106+
progressIndex: 0
107+
),
108+
makeItem(
109+
id: 8,
110+
title: "지하차도에 물이 고여 있어요",
111+
date: "2025-11-15월",
112+
location: "서울시 영등포구 영등포동",
113+
typeIndex: 3,
114+
progressIndex: 1
115+
)
116+
]
117+
}
118+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// ReportProgressItem.swift
3+
// Presentation
4+
//
5+
// Created by 이동현 on 11/17/25.
6+
//
7+
8+
import Domain
9+
import Foundation
10+
11+
struct ReportProgressItem: Hashable {
12+
let uuid: UUID
13+
let progress: ReportProgress
14+
let count: Int
15+
var isSelected: Bool
16+
}

Projects/Presentation/Sources/Report/View/Component/ReportCategoryTableViewCell.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,13 @@ extension ReportType {
106106
var iconImage: UIImage? {
107107
switch self {
108108
case .transportation:
109-
BitnagilIcon.restIcon
109+
BitnagilIcon.carIcon
110110
case .lamp:
111-
BitnagilIcon.outsideIcon
111+
BitnagilIcon.lightIcon
112112
case .water:
113-
BitnagilIcon.wakeupIcon
113+
BitnagilIcon.waterIcon
114114
case .convenience:
115-
BitnagilIcon.growIcon
115+
BitnagilIcon.hammerIcon
116116
}
117117
}
118118
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//
2+
// ReportHistoryTableHeaderView.swift
3+
// Presentation
4+
//
5+
// Created by 이동현 on 11/15/25.
6+
//
7+
8+
import SnapKit
9+
import UIKit
10+
11+
final class ReportHistoryTableHeaderView: UITableViewHeaderFooterView {
12+
private enum Layout {
13+
static let labelHeight: CGFloat = 20
14+
static let weekLabelLeadingSpacing: CGFloat = 2
15+
}
16+
17+
private let dateLabel = UILabel()
18+
private let weekLabel = UILabel()
19+
20+
override init(reuseIdentifier: String?) {
21+
super.init(reuseIdentifier: reuseIdentifier)
22+
23+
configureAttribute()
24+
configureLayout()
25+
}
26+
27+
required init?(coder: NSCoder) {
28+
fatalError("init(coder:) has not been implemented")
29+
}
30+
31+
private func configureAttribute() {
32+
backgroundColor = .clear
33+
34+
dateLabel.textColor = BitnagilColor.gray10
35+
dateLabel.font = BitnagilFont.init(style: .body2, weight: .semiBold).font
36+
37+
weekLabel.textColor = BitnagilColor.gray40
38+
weekLabel.font = BitnagilFont.init(style: .body2, weight: .medium).font
39+
}
40+
41+
private func configureLayout() {
42+
contentView.addSubview(dateLabel)
43+
contentView.addSubview(weekLabel)
44+
45+
dateLabel.snp.makeConstraints { make in
46+
make.leading.equalToSuperview()
47+
make.top.equalToSuperview()
48+
make.height.equalTo(Layout.labelHeight)
49+
}
50+
51+
weekLabel.snp.makeConstraints { make in
52+
make.top.equalToSuperview()
53+
54+
make.leading
55+
.equalTo(dateLabel.snp.trailing)
56+
.offset(Layout.weekLabelLeadingSpacing)
57+
58+
make.height.equalTo(Layout.labelHeight)
59+
}
60+
}
61+
62+
func configure(with dateString: String) {
63+
guard let weekCharacter = dateString.last else {
64+
dateLabel.text = dateString
65+
weekLabel.text = nil
66+
return
67+
}
68+
69+
let datePart = String(dateString.dropLast())
70+
dateLabel.text = datePart
71+
weekLabel.text = String(weekCharacter)
72+
}
73+
}

0 commit comments

Comments
 (0)