diff --git a/frontend/SideDish/SideDish.xcodeproj/project.pbxproj b/frontend/SideDish/SideDish.xcodeproj/project.pbxproj index 182e1c5e7..eb118696b 100644 --- a/frontend/SideDish/SideDish.xcodeproj/project.pbxproj +++ b/frontend/SideDish/SideDish.xcodeproj/project.pbxproj @@ -13,6 +13,10 @@ BD090659262E9B450094421D /* MenuCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BD090657262E9B450094421D /* MenuCell.xib */; }; BD09065E262EBD310094421D /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD09065D262EBD310094421D /* Menu.swift */; }; BD09066D262EC5120094421D /* Menus.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD09066C262EC5120094421D /* Menus.swift */; }; + BD26B1092637D8B100DEA046 /* DetailMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD26B1082637D8B100DEA046 /* DetailMenuViewController.swift */; }; + BD26B1222637E3D400DEA046 /* DetailMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD26B1212637E3D400DEA046 /* DetailMenu.swift */; }; + BD26B1272637F0A600DEA046 /* DetailMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD26B1262637F0A600DEA046 /* DetailMenuViewModel.swift */; }; + BD26B12C2638293C00DEA046 /* DetailScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD26B12B2638293C00DEA046 /* DetailScrollView.swift */; }; BD930B58262D6F5800906633 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD930B57262D6F5800906633 /* AppDelegate.swift */; }; BD930B5A262D6F5800906633 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD930B59262D6F5800906633 /* SceneDelegate.swift */; }; BD930B5C262D6F5800906633 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD930B5B262D6F5800906633 /* ViewController.swift */; }; @@ -21,16 +25,18 @@ BD930B64262D6F5900906633 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BD930B62262D6F5900906633 /* LaunchScreen.storyboard */; }; BD930B6F262D6F5900906633 /* SideDishTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD930B6E262D6F5900906633 /* SideDishTests.swift */; }; BD930B7A262D6F5900906633 /* SideDishUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD930B79262D6F5900906633 /* SideDishUITests.swift */; }; + BD9E7E64263A909F002956F2 /* OrderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9E7E63263A909F002956F2 /* OrderViewModel.swift */; }; BDFABA96262FF9FC00803154 /* Parsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFABA95262FF9FC00803154 /* Parsing.swift */; }; BDFABAA0262FFA1C00803154 /* Network.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDFABA9F262FFA1C00803154 /* Network.swift */; }; + CE04C03826393C54002D7D07 /* FetchDetailMenuUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE04C03726393C54002D7D07 /* FetchDetailMenuUseCase.swift */; }; CE29EB432632A0B700221658 /* MenusViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE29EB422632A0B700221658 /* MenusViewModel.swift */; }; - CE5905F826319C580085F570 /* MenuCellValidater.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5905F726319C580085F570 /* MenuCellValidater.swift */; }; CE5905FE26327DFE0085F570 /* MenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE5905FD26327DFE0085F570 /* MenuViewModel.swift */; }; CE8E2E4026313B3C007EEEBB /* JSONModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8E2E3F26313B3C007EEEBB /* JSONModel.swift */; }; CE8E2E4526313B48007EEEBB /* MainDiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8E2E4426313B48007EEEBB /* MainDiffableDataSource.swift */; }; CE8E2E4B26313C19007EEEBB /* MenuHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE8E2E4926313C19007EEEBB /* MenuHeaderView.swift */; }; CE8E2E4C26313C19007EEEBB /* MenuHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = CE8E2E4A26313C19007EEEBB /* MenuHeaderView.xib */; }; - CEAE6F302636AAFD00720625 /* FetchDataUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAE6F2F2636AAFD00720625 /* FetchDataUseCase.swift */; }; + CEAE6F302636AAFD00720625 /* FetchMenuResponseUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAE6F2F2636AAFD00720625 /* FetchMenuResponseUseCase.swift */; }; + CEB837A6263BD9A000AFF9C7 /* URLManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB837A5263BD9A000AFF9C7 /* URLManager.swift */; }; D913214002A3F533F01AF848 /* Pods_SideDishTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2BA6E291323BD92D963428EF /* Pods_SideDishTests.framework */; }; /* End PBXBuildFile section */ @@ -63,6 +69,10 @@ BD090657262E9B450094421D /* MenuCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuCell.xib; sourceTree = ""; }; BD09065D262EBD310094421D /* Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = ""; }; BD09066C262EC5120094421D /* Menus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Menus.swift; sourceTree = ""; }; + BD26B1082637D8B100DEA046 /* DetailMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailMenuViewController.swift; sourceTree = ""; }; + BD26B1212637E3D400DEA046 /* DetailMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailMenu.swift; sourceTree = ""; }; + BD26B1262637F0A600DEA046 /* DetailMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailMenuViewModel.swift; sourceTree = ""; }; + BD26B12B2638293C00DEA046 /* DetailScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailScrollView.swift; sourceTree = ""; }; BD930B54262D6F5800906633 /* SideDish.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SideDish.app; sourceTree = BUILT_PRODUCTS_DIR; }; BD930B57262D6F5800906633 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; BD930B59262D6F5800906633 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -77,17 +87,19 @@ BD930B75262D6F5900906633 /* SideDishUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SideDishUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; BD930B79262D6F5900906633 /* SideDishUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDishUITests.swift; sourceTree = ""; }; BD930B7B262D6F5900906633 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BD9E7E63263A909F002956F2 /* OrderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderViewModel.swift; sourceTree = ""; }; BDFABA95262FF9FC00803154 /* Parsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parsing.swift; sourceTree = ""; }; BDFABA9F262FFA1C00803154 /* Network.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Network.swift; sourceTree = ""; }; CB7E749E27E4F2FB199FD307 /* Pods-SideDish-SideDishUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SideDish-SideDishUITests.debug.xcconfig"; path = "Target Support Files/Pods-SideDish-SideDishUITests/Pods-SideDish-SideDishUITests.debug.xcconfig"; sourceTree = ""; }; + CE04C03726393C54002D7D07 /* FetchDetailMenuUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchDetailMenuUseCase.swift; sourceTree = ""; }; CE29EB422632A0B700221658 /* MenusViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenusViewModel.swift; sourceTree = ""; }; - CE5905F726319C580085F570 /* MenuCellValidater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuCellValidater.swift; sourceTree = ""; }; CE5905FD26327DFE0085F570 /* MenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewModel.swift; sourceTree = ""; }; CE8E2E3F26313B3C007EEEBB /* JSONModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONModel.swift; sourceTree = ""; }; CE8E2E4426313B48007EEEBB /* MainDiffableDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainDiffableDataSource.swift; sourceTree = ""; }; CE8E2E4926313C19007EEEBB /* MenuHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuHeaderView.swift; sourceTree = ""; }; CE8E2E4A26313C19007EEEBB /* MenuHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MenuHeaderView.xib; sourceTree = ""; }; - CEAE6F2F2636AAFD00720625 /* FetchDataUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchDataUseCase.swift; sourceTree = ""; }; + CEAE6F2F2636AAFD00720625 /* FetchMenuResponseUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchMenuResponseUseCase.swift; sourceTree = ""; }; + CEB837A5263BD9A000AFF9C7 /* URLManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLManager.swift; sourceTree = ""; }; F27206AA084B5CBF57CB7CC7 /* Pods-SideDishTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SideDishTests.debug.xcconfig"; path = "Target Support Files/Pods-SideDishTests/Pods-SideDishTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -135,7 +147,7 @@ BD090674262ED1CA0094421D /* DTO */, BD09065D262EBD310094421D /* Menu.swift */, BD09066C262EC5120094421D /* Menus.swift */, - CE5905F726319C580085F570 /* MenuCellValidater.swift */, + BD26B1212637E3D400DEA046 /* DetailMenu.swift */, ); path = Model; sourceTree = ""; @@ -143,6 +155,8 @@ BD090666262EBD7A0094421D /* Controller */ = { isa = PBXGroup; children = ( + BD930B5B262D6F5800906633 /* ViewController.swift */, + BD26B1082637D8B100DEA046 /* DetailMenuViewController.swift */, ); path = Controller; sourceTree = ""; @@ -155,6 +169,7 @@ BD090657262E9B450094421D /* MenuCell.xib */, CE8E2E4926313C19007EEEBB /* MenuHeaderView.swift */, CE8E2E4A26313C19007EEEBB /* MenuHeaderView.xib */, + BD26B12B2638293C00DEA046 /* DetailScrollView.swift */, ); path = View; sourceTree = ""; @@ -174,6 +189,7 @@ CE8E2E3F26313B3C007EEEBB /* JSONModel.swift */, BDFABA95262FF9FC00803154 /* Parsing.swift */, BDFABA9F262FFA1C00803154 /* Network.swift */, + CEB837A5263BD9A000AFF9C7 /* URLManager.swift */, ); path = DTO; sourceTree = ""; @@ -208,7 +224,6 @@ BD090667262EBD820094421D /* View */, BD090666262EBD7A0094421D /* Controller */, BD090662262EBD350094421D /* Model */, - BD930B5B262D6F5800906633 /* ViewController.swift */, BD930B5D262D6F5800906633 /* Main.storyboard */, BD930B62262D6F5900906633 /* LaunchScreen.storyboard */, BD930B60262D6F5900906633 /* Assets.xcassets */, @@ -240,7 +255,10 @@ children = ( CE5905FD26327DFE0085F570 /* MenuViewModel.swift */, CE29EB422632A0B700221658 /* MenusViewModel.swift */, - CEAE6F2F2636AAFD00720625 /* FetchDataUseCase.swift */, + CEAE6F2F2636AAFD00720625 /* FetchMenuResponseUseCase.swift */, + CE04C03726393C54002D7D07 /* FetchDetailMenuUseCase.swift */, + BD26B1262637F0A600DEA046 /* DetailMenuViewModel.swift */, + BD9E7E63263A909F002956F2 /* OrderViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -498,21 +516,27 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BD26B1272637F0A600DEA046 /* DetailMenuViewModel.swift in Sources */, + BD26B1222637E3D400DEA046 /* DetailMenu.swift in Sources */, + BD26B1092637D8B100DEA046 /* DetailMenuViewController.swift in Sources */, CE8E2E4526313B48007EEEBB /* MainDiffableDataSource.swift in Sources */, BDFABA96262FF9FC00803154 /* Parsing.swift in Sources */, BD930B5C262D6F5800906633 /* ViewController.swift in Sources */, BD930B58262D6F5800906633 /* AppDelegate.swift in Sources */, + CE04C03826393C54002D7D07 /* FetchDetailMenuUseCase.swift in Sources */, BD090658262E9B450094421D /* MenuCell.swift in Sources */, - CEAE6F302636AAFD00720625 /* FetchDataUseCase.swift in Sources */, + CEAE6F302636AAFD00720625 /* FetchMenuResponseUseCase.swift in Sources */, CE29EB432632A0B700221658 /* MenusViewModel.swift in Sources */, CE8E2E4026313B3C007EEEBB /* JSONModel.swift in Sources */, + BD9E7E64263A909F002956F2 /* OrderViewModel.swift in Sources */, CE8E2E4B26313C19007EEEBB /* MenuHeaderView.swift in Sources */, BDFABAA0262FFA1C00803154 /* Network.swift in Sources */, + CEB837A6263BD9A000AFF9C7 /* URLManager.swift in Sources */, BD930B5A262D6F5800906633 /* SceneDelegate.swift in Sources */, BD09066D262EC5120094421D /* Menus.swift in Sources */, + BD26B12C2638293C00DEA046 /* DetailScrollView.swift in Sources */, CE5905FE26327DFE0085F570 /* MenuViewModel.swift in Sources */, BD09065E262EBD310094421D /* Menu.swift in Sources */, - CE5905F826319C580085F570 /* MenuCellValidater.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/frontend/SideDish/SideDish.xcworkspace/xcuserdata/sonjuhyeong.xcuserdatad/UserInterfaceState.xcuserstate b/frontend/SideDish/SideDish.xcworkspace/xcuserdata/sonjuhyeong.xcuserdatad/UserInterfaceState.xcuserstate index 730d95041..95ecbcb1f 100644 Binary files a/frontend/SideDish/SideDish.xcworkspace/xcuserdata/sonjuhyeong.xcuserdatad/UserInterfaceState.xcuserstate and b/frontend/SideDish/SideDish.xcworkspace/xcuserdata/sonjuhyeong.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/frontend/SideDish/SideDish/Base.lproj/Main.storyboard b/frontend/SideDish/SideDish/Base.lproj/Main.storyboard index f0751e22c..560a2eeb3 100644 --- a/frontend/SideDish/SideDish/Base.lproj/Main.storyboard +++ b/frontend/SideDish/SideDish/Base.lproj/Main.storyboard @@ -1,8 +1,10 @@ - + + - + + @@ -13,11 +15,11 @@ - + - + @@ -36,7 +38,7 @@ - + @@ -50,18 +52,458 @@ + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/SideDish/SideDish/Controller/DetailMenuViewController.swift b/frontend/SideDish/SideDish/Controller/DetailMenuViewController.swift new file mode 100644 index 000000000..17c2ce59e --- /dev/null +++ b/frontend/SideDish/SideDish/Controller/DetailMenuViewController.swift @@ -0,0 +1,149 @@ +// +// DetailMenuViewController.swift +// SideDish +// +// Created by sonjuhyeong on 2021/04/27. +// + +import UIKit +import Kingfisher + +class DetailMenuViewController: UIViewController { + + private var detailHash: String? + private var detailMenuViewModel = DetailMenuViewModel() + private var orderViewModel = OrderViewModel() + @IBOutlet weak var detailScrollView: DetailScrollView! + + + override func viewDidLoad() { + super.viewDidLoad() + NotificationCenter.default.addObserver(self, selector: #selector(configureViewModel), name: Notification.Name.fetchDetailMenu, object: detailMenuViewModel) + + self.detailScrollView.setCornerRadius() + self.detailScrollView.setBorderWidth() + configureThumbnailImageSize() + } + + override func viewWillDisappear(_ animated: Bool) { + self.navigationController?.isNavigationBarHidden = true + } + + func receive(categoryId: Int, detailHash: String) { + self.detailMenuViewModel.send(categoryId: categoryId, detailHash: detailHash) + } + + @objc func configureViewModel() { + DispatchQueue.main.async { + self.checkStock() + self.navigationItem.title = self.detailMenuViewModel.title + self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(customView: UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: 50, height: 100)))) + self.detailScrollView.title.text = self.detailMenuViewModel.title + self.detailScrollView.body.text = self.detailMenuViewModel.productDescription + self.configureBadges(verified: self.detailMenuViewModel.badges) + self.detailScrollView.point.text = String(self.detailMenuViewModel.point) + self.detailScrollView.salePrice.text = self.detailMenuViewModel.normalPrice + self.detailScrollView.normalPrice.attributedText = self.detailMenuViewModel.salePrice + self.detailScrollView.deliveryInfo.text = self.detailMenuViewModel.deliveryInfo + self.detailScrollView.deliveryFee.text = self.detailMenuViewModel.deliveryFee + self.detailScrollView.totalPrice.text = self.detailMenuViewModel.normalPrice + self.configureThumbnailImage(imageStringArr: self.detailMenuViewModel.thumbImages) + self.configureDetailStackViewImage(imageStringArr: self.detailMenuViewModel.detailSection) + } + } + + func checkStock() { + if self.detailMenuViewModel.stock == 0 { + self.detailScrollView.orderButton.setTitle("일시 품절", for: .normal) + self.detailScrollView.orderButton.setTitleColor(.systemGray2, for: .normal) + self.detailScrollView.orderButton.backgroundColor = UIColor.systemGray5 + self.detailScrollView.orderButton.isEnabled = false + } + } + + func configureThumbnailImageSize() { + let width = self.view.frame.width + + self.detailScrollView.thumbnailImageWidthConstraint.constant = width + self.detailScrollView.thumbnailImageHeightConstraint.constant = width + } + + func configureBadges(verified: [Bool]) { + switch verified { + case [true, false]: + self.detailScrollView.launchingBadge.isHidden = true + case [false, true]: + self.detailScrollView.eventBadge.isHidden = true + case [false, false]: + self.detailScrollView.badgeView.isHidden = true + default: + return + } + } + + func configureThumbnailImage(imageStringArr: [String]) { + let width = self.detailScrollView.thumbnailImageWidthConstraint.constant + self.detailScrollView.thumbnailImageWidthConstraint.constant = self.detailScrollView.thumbnailImageWidthConstraint.constant * CGFloat(imageStringArr.count) + + for index in 0..) -> Void) { - guard let url = URL(string: url.rawValue) else { - print("The URL is inappropriate.") + static func sendRequest(url: URL?, completion: @escaping (Result) -> Void) { + guard let url = url else { return } session.dataTask(with: url) { data, response, error in @@ -34,4 +27,41 @@ class DataTaskManager { }.resume() } + static func sendDetailRequest(url: URL?, completion: @escaping (Result) -> Void) { + guard let url = url else { + print("The URL is inappropriate.") + return + } + session.dataTask(with: url) { data, response, error in + if let data = data { + guard let detailMenuList = ParsingManager.decodeData(type: DetailMenu.self, data: data) else { return } + completion(.success(detailMenuList)) + } else { + guard let error = error?.localizedDescription as? Error else { return } + completion(.failure(error)) + } + }.resume() + } + + static func orderPost(orderCount: OrderMenuRequest, url: URL?, completion: @escaping (Bool) -> Void) { + guard let url = url else { + print("The URL is inappropriate.") + return + } + var request = URLRequest(url: url) + request.httpMethod = "POST" + guard let encodingData = ParsingManager.encodeData(data: orderCount) else { return } + request.httpBody = encodingData + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + session.dataTask(with: request){ (_, response, _) in + guard let response = response as? HTTPURLResponse else { return } + if (200 ..< 299) ~= response.statusCode { + completion(true) + }else{ + completion(false) + } + }.resume() + } + } diff --git a/frontend/SideDish/SideDish/Model/DTO/URLManager.swift b/frontend/SideDish/SideDish/Model/DTO/URLManager.swift new file mode 100644 index 000000000..3d4216e07 --- /dev/null +++ b/frontend/SideDish/SideDish/Model/DTO/URLManager.swift @@ -0,0 +1,28 @@ + +import Foundation + +class URLManager { + + enum Url: String { + case main = "http://13.209.36.131:8080/17011000" + case soup = "http://13.209.36.131:8080/17011100" + case side = "http://13.209.36.131:8080/17011200" + case detail = "http://13.209.36.131:8080/detail" + } + + static func menu(of url: Url) -> URL? { + guard let url = URL(string: url.rawValue) else { + print("The URL is inappropriate.") + return nil + } + return url + } + + static func detailMenu(categoryId: Int, detailHash: String) -> URL? { + guard let url = URL(string: "\(Url.detail.rawValue)/\(categoryId)/\(detailHash)") else { + print("The URL is inappropriate.") + return nil + } + return url + } +} diff --git a/frontend/SideDish/SideDish/Model/DetailMenu.swift b/frontend/SideDish/SideDish/Model/DetailMenu.swift new file mode 100644 index 000000000..8f094a65f --- /dev/null +++ b/frontend/SideDish/SideDish/Model/DetailMenu.swift @@ -0,0 +1,40 @@ +// +// DetailMenu.swift +// SideDish +// +// Created by sonjuhyeong on 2021/04/27. +// + +import Foundation + +class DetailMenu: Decodable { + + private var detailHash: String + private var topImage: String + private(set) var thumbImages: [String] + private(set) var title: String + private(set) var productDescription: String + private(set) var point: Int + private(set) var deliveryInfo: String + private(set) var deliveryFee: String + private(set) var stock: Int + private(set) var prices: [String] + private(set) var badge: [String?] + private(set) var detailSection: [String] + + + init(detailHash: String, topImage: String, thumbImages: [String], title: String, productDescription: String, point: Int, deliveryInfo: String, deliveryFee: String, stock: Int, prices: [String], badges: [String?], detailSection: [String]) { + self.detailHash = detailHash + self.topImage = topImage + self.thumbImages = thumbImages + self.title = title + self.productDescription = productDescription + self.point = point + self.deliveryInfo = deliveryInfo + self.deliveryFee = deliveryFee + self.stock = stock + self.prices = prices + self.badge = badges + self.detailSection = detailSection + } +} diff --git a/frontend/SideDish/SideDish/Model/Menu.swift b/frontend/SideDish/SideDish/Model/Menu.swift index 5a5994e3d..00eb59d41 100644 --- a/frontend/SideDish/SideDish/Model/Menu.swift +++ b/frontend/SideDish/SideDish/Model/Menu.swift @@ -9,25 +9,25 @@ import Foundation class Menu: Decodable { - private var detailHash: String + private(set) var detailHash: String private(set) var image: String private var alt: String private var deliveryType: [String] private(set) var title: String private(set) var description: String - private(set) var nPrice: String? - private(set) var sPrice: String - private(set) var badge: [String]? + private(set) var normalPrice: Int? + private(set) var salePrice: Int + private(set) var badge: [String?] - init(detailHash: String, image: String, alt: String, deliveryType: [String], title: String, description: String, nPrice: String?, sPrice: String, badge: [String]?) { + init(detailHash: String, image: String, alt: String, deliveryType: [String], title: String, description: String, normalPrice: Int?, salePrice: Int, badge: [String?]) { self.detailHash = detailHash self.image = image self.alt = alt self.deliveryType = deliveryType self.title = title self.description = description - self.nPrice = nPrice - self.sPrice = sPrice + self.normalPrice = normalPrice + self.salePrice = salePrice self.badge = badge } } diff --git a/frontend/SideDish/SideDish/Model/MenuCellValidater.swift b/frontend/SideDish/SideDish/Model/MenuCellValidater.swift deleted file mode 100644 index 59006430a..000000000 --- a/frontend/SideDish/SideDish/Model/MenuCellValidater.swift +++ /dev/null @@ -1,16 +0,0 @@ - -import Foundation - -class MenuCellValidater { - - func validate(pastPrice: String?) -> NSMutableAttributedString { - if let pastPrice = pastPrice { - let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "\(pastPrice)원") - attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length)) - return attributeString - } else { - return NSMutableAttributedString(string: "") - } - } - -} diff --git a/frontend/SideDish/SideDish/View/DetailScrollView.swift b/frontend/SideDish/SideDish/View/DetailScrollView.swift new file mode 100644 index 000000000..e3f0ff69d --- /dev/null +++ b/frontend/SideDish/SideDish/View/DetailScrollView.swift @@ -0,0 +1,60 @@ +// +// DetailScrollView.swift +// SideDish +// +// Created by sonjuhyeong on 2021/04/27. +// + +import UIKit + +class DetailScrollView: UIScrollView { + + @IBOutlet weak var thumbnailImageWidthConstraint: NSLayoutConstraint! + @IBOutlet weak var thumbnailImageHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var thumbnailScrollView: UIScrollView! + @IBOutlet weak var thumbnailImage: UIImageView! + @IBOutlet weak var title: UILabel! + @IBOutlet weak var body: UILabel! + @IBOutlet weak var salePrice: UILabel! + @IBOutlet weak var normalPrice: UILabel! + @IBOutlet weak var badgeView: UIView! + @IBOutlet weak var eventBadge: UILabel! + @IBOutlet weak var launchingBadge: UILabel! + + @IBOutlet weak var point: UILabel! + @IBOutlet weak var deliveryInfo: UILabel! + @IBOutlet weak var deliveryFee: UILabel! + + @IBOutlet weak var orderCount: UILabel! + @IBOutlet weak var increaseButton: UIButton! + @IBOutlet weak var decreaseButton: UIButton! + + @IBOutlet weak var totalPrice: UILabel! + @IBOutlet weak var orderButton: UIButton! + + @IBOutlet weak var detailStackView: UIStackView! + + func setCornerRadius() { + self.eventBadge.layer.masksToBounds = true + self.eventBadge.layer.cornerRadius = 5 + + self.launchingBadge.layer.masksToBounds = true + self.launchingBadge.layer.cornerRadius = 5 + + self.orderButton.layer.cornerRadius = 5 + } + + func setBorderWidth() { + let gray = UIColor(red: 100/255, green: 100/255, blue: 100/255, alpha: 0.3).cgColor + + self.orderCount.layer.borderWidth = 1 + self.orderCount.layer.borderColor = gray + + self.increaseButton.layer.borderWidth = 1 + self.increaseButton.layer.borderColor = gray + + self.decreaseButton.layer.borderWidth = 1 + self.decreaseButton.layer.borderColor = gray + } + +} diff --git a/frontend/SideDish/SideDish/View/MainDiffableDataSource.swift b/frontend/SideDish/SideDish/View/MainDiffableDataSource.swift index 24b214c7f..f15105c45 100644 --- a/frontend/SideDish/SideDish/View/MainDiffableDataSource.swift +++ b/frontend/SideDish/SideDish/View/MainDiffableDataSource.swift @@ -10,8 +10,6 @@ class MainDiffableDataSource { case side = "식탁을 풍성하게 하는 정갈한 밑반찬" } - - private var menuCount = [0,0,0] private var dataSource: UICollectionViewDiffableDataSource! func setupDataSource(collectionView: UICollectionView) { @@ -25,7 +23,7 @@ class MainDiffableDataSource { setHeaderSnapShot() } - func setHeaderSnapShot() { + private func setHeaderSnapShot() { self.dataSource.supplementaryViewProvider = setupHeader(collectionView: kind: indexPath:) var snapshot = self.dataSource.snapshot() snapshot.appendSections(sectionTitle.allCases) @@ -38,46 +36,35 @@ class MainDiffableDataSource { snapshot.appendItems(soup, toSection: .soup) snapshot.appendItems(side, toSection: .side) - menuCount[0] = snapshot.numberOfItems(inSection: .main) - menuCount[1] = snapshot.numberOfItems(inSection: .soup) - menuCount[2] = snapshot.numberOfItems(inSection: .side) - self.dataSource.apply(snapshot, animatingDifferences: true) } - func setupHeader(collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? { - let menuHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "menuHeaderView", for: indexPath) as! MenuHeaderView + private func setupHeader(collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? { + guard let menuHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "menuHeaderView", for: indexPath) as? MenuHeaderView else { + return MenuHeaderView() + } let section = self.dataSource.snapshot().sectionIdentifiers[indexPath.section] menuHeader.configureLabelName(text: section.rawValue) - let tap = CustomTapGestureRecognizer(target: self, action: #selector(labelPressed), cellCount: returnCount(section: indexPath.section)) - collectionView.addGestureRecognizer(tap) + let tap = CustomTapGestureRecognizer(target: self, action: #selector(labelPressed), section: section) + menuHeader.addGestureRecognizer(tap) return menuHeader } @objc private func labelPressed(_ sender: CustomTapGestureRecognizer) { - Toast(text: "\(sender.cellCount)개 상품이 등록되어 있습니다.").show() + let cellCount = self.dataSource.snapshot().numberOfItems(inSection: sender.section) + Toast(text: "\(cellCount)개 상품이 등록되어 있습니다.").show() } - - func returnCount(section: Int) -> Int { - if section == 0 { - return menuCount[0] - } else if section == 1 { - return menuCount[1] - } else if section == 2 { - return menuCount[2] - } - return 0 - } - } + class CustomTapGestureRecognizer: UITapGestureRecognizer { - private(set) var cellCount: Int - init(target: Any?, action: Selector?, cellCount: Int) { - self.cellCount = cellCount + private(set) var section: MainDiffableDataSource.sectionTitle + + init(target: Any?, action: Selector?, section: MainDiffableDataSource.sectionTitle) { + self.section = section super.init(target: target, action: action) } } diff --git a/frontend/SideDish/SideDish/View/MenuCell.swift b/frontend/SideDish/SideDish/View/MenuCell.swift index 61a2e20ca..b2f109b05 100644 --- a/frontend/SideDish/SideDish/View/MenuCell.swift +++ b/frontend/SideDish/SideDish/View/MenuCell.swift @@ -10,6 +10,7 @@ import Kingfisher class MenuCell: UICollectionViewCell { + @IBOutlet weak var launchingBadgeConstraint: NSLayoutConstraint! @IBOutlet weak var cellView: UIView! @IBOutlet weak var thumbnailImage: UIImageView! @IBOutlet weak var titleLabel: UILabel! @@ -21,15 +22,17 @@ class MenuCell: UICollectionViewCell { @IBOutlet weak var contentStackView: UIStackView! @IBOutlet weak var badgeView: UIView! - private let menuCellValidater = MenuCellValidater() - override func awakeFromNib() { super.awakeFromNib() setupBadgeShape() } - private func setupBadgeLabelConstraint() { - + override func prepareForReuse() { + self.eventLabel.isHidden = false + self.launchingLabel.isHidden = false + self.badgeView.isHidden = false + self.eventLabel.widthAnchor.constraint(equalToConstant: 72).isActive = true + self.launchingBadgeConstraint.constant = 76 } private func setupBadgeShape() { @@ -42,28 +45,19 @@ class MenuCell: UICollectionViewCell { func configure(menu: MenuViewModel) { self.titleLabel.text = menu.title self.bodyLabel.text = menu.body - self.currentPriceLabel.text = menu.sPrice + self.currentPriceLabel.text = "\(menu.sPrice)원" self.pastPriceLabel.attributedText = menu.nPrice guard let url = URL(string: menu.image) else { return } self.thumbnailImage.kf.setImage(with: url) - badgeViewInitialSetup() setBadge(menu: menu) } - private func badgeViewInitialSetup() { - self.eventLabel.isHidden = false - self.launchingLabel.isHidden = false - self.badgeView.isHidden = false - self.eventLabel.widthAnchor.constraint(equalToConstant: 72).isActive = true - self.launchingLabel.leftAnchor.constraint(equalTo: self.contentStackView.leftAnchor, constant: 76).isActive = true - } - func setBadge(menu: MenuViewModel) { if menu.verifyBadges(badges: menu.badges) == [true, false] { self.launchingLabel.isHidden = true } else if menu.verifyBadges(badges: menu.badges) == [false, true] { self.eventLabel.isHidden = true - self.launchingLabel.leftAnchor.constraint(equalTo: contentStackView.leftAnchor, constant: 0).isActive = true + self.launchingBadgeConstraint.constant = 0 } else if menu.verifyBadges(badges: menu.badges) == [false, false] { self.badgeView.isHidden = true } else { diff --git a/frontend/SideDish/SideDish/View/MenuCell.xib b/frontend/SideDish/SideDish/View/MenuCell.xib index e13aa3b3f..22e542c9f 100644 --- a/frontend/SideDish/SideDish/View/MenuCell.xib +++ b/frontend/SideDish/SideDish/View/MenuCell.xib @@ -1,8 +1,8 @@ - + - + @@ -104,12 +104,12 @@ - + @@ -147,6 +147,7 @@ + diff --git a/frontend/SideDish/SideDish/View/MenuHeaderView.swift b/frontend/SideDish/SideDish/View/MenuHeaderView.swift index 81481c1dd..b58fb0c62 100644 --- a/frontend/SideDish/SideDish/View/MenuHeaderView.swift +++ b/frontend/SideDish/SideDish/View/MenuHeaderView.swift @@ -3,12 +3,10 @@ import UIKit class MenuHeaderView: UICollectionReusableView { - @IBOutlet weak var headerLabel: UILabel! func configureLabelName(text: String) { self.headerLabel.text = text - } } diff --git a/frontend/SideDish/SideDish/View/MenuHeaderView.xib b/frontend/SideDish/SideDish/View/MenuHeaderView.xib index 9a8260aa8..b568208bd 100644 --- a/frontend/SideDish/SideDish/View/MenuHeaderView.xib +++ b/frontend/SideDish/SideDish/View/MenuHeaderView.xib @@ -1,8 +1,8 @@ - + - + diff --git a/frontend/SideDish/SideDish/ViewController.swift b/frontend/SideDish/SideDish/ViewController.swift deleted file mode 100644 index c2908618c..000000000 --- a/frontend/SideDish/SideDish/ViewController.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// ViewController.swift -// SideDish -// -// Created by sonjuhyeong on 2021/04/19. -// - -import UIKit - -class ViewController: UIViewController { - - @IBOutlet weak var sideDishCollectionView: UICollectionView! - - private var dataSource = MainDiffableDataSource() - private var menusViewModel = MenusViewModel() - - override func viewDidLoad() { - super.viewDidLoad() - NotificationCenter.default.addObserver(self, selector: #selector(sendMenuList), name: MenusViewModel.changeMenu, object: menusViewModel) - configureCollectionView() - menusViewModel.fetchData() - } - - private func configureCollectionView() { - let nibName = UINib(nibName: "MenuCell", bundle: .none) - sideDishCollectionView.register(nibName, forCellWithReuseIdentifier: "menuCell") - - let nibHeaderName = UINib(nibName: "MenuHeaderView", bundle: .none) - sideDishCollectionView.register(nibHeaderName, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "menuHeaderView") - - self.dataSource.setupDataSource(collectionView: self.sideDishCollectionView) - } - - @objc private func sendMenuList() { - self.dataSource.applySnapshot(main: menusViewModel.giveMenus(section: .main), soup: menusViewModel.giveMenus(section: .soup), side: menusViewModel.giveMenus(section: .side)) - } -} diff --git a/frontend/SideDish/SideDish/ViewModel/DetailMenuViewModel.swift b/frontend/SideDish/SideDish/ViewModel/DetailMenuViewModel.swift new file mode 100644 index 000000000..381e402a2 --- /dev/null +++ b/frontend/SideDish/SideDish/ViewModel/DetailMenuViewModel.swift @@ -0,0 +1,98 @@ + +import Foundation + +class DetailMenuViewModel { + + private(set) var categoryId: Int + private(set) var detailHash: String + private(set) var thumbImages: [String] + private(set) var title: String + private(set) var productDescription: String + private(set) var salePrice: NSAttributedString + private(set) var normalPrice: String + private(set) var badges: [Bool] + private(set) var point: Int + private(set) var deliveryInfo: String + private(set) var deliveryFee: String + private(set) var stock: Int + private(set) var detailSection: [String] + + private let fetchDetailDataUseCase = FetchDetailMenuUseCase() + + init() { + self.categoryId = 0 + self.detailHash = "" + self.thumbImages = [] + self.title = "" + self.productDescription = "" + self.salePrice = NSMutableAttributedString(string: "") + self.normalPrice = "" + self.badges = [] + self.point = 0 + self.deliveryInfo = "" + self.deliveryFee = "" + self.stock = 0 + self.detailSection = [] + } + + func send(categoryId: Int, detailHash: String) { + self.categoryId = categoryId + self.detailHash = detailHash + self.fetchDetailDataUseCase.loadDetailMenu(url: URLManager.detailMenu(categoryId: categoryId, detailHash: detailHash)) { data in + self.configure(data: data) + NotificationCenter.default.post(name: Notification.Name.fetchDetailMenu, object: self) + } + } + // price 수정 + func configure(data: DetailMenu) { + self.thumbImages = data.thumbImages + self.title = data.title + self.productDescription = data.productDescription + self.salePrice = stringToAttributedString(data.prices) + self.normalPrice = "\(convertDecimal(string: data.prices[0]))원" + self.badges = verifyBadges(badges: data.badge != [nil] ? data.badge.map() { $0! } : []) + self.point = data.point + self.deliveryInfo = data.deliveryInfo + self.deliveryFee = data.deliveryFee + self.stock = data.stock + self.detailSection = data.detailSection + } + + private func stringToAttributedString(_ prices: [String]) -> NSAttributedString { + if prices.count == 2 { + let convertedPrice = convertDecimal(string: prices[1]) + let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "\(convertedPrice)원") + attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length)) + return attributeString + } else { + return NSAttributedString(string: "") + } + } + + func convertDecimal(string: String) -> String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + + let stringToInt = Int(string)! + return numberFormatter.string(from: NSNumber(value: stringToInt))! + } + + func normalPriceToInt() -> Int { + return Int(self.normalPrice.components(separatedBy: [",", "원"]).joined())! + } + + func verifyBadges(badges: [String]) -> [Bool] { + switch badges { + case ["이벤트특가"]: + return [true, false] + case ["론칭특가"]: + return [false, true] + case ["이벤트특가","론칭특가"]: + return [true, true] + case ["론칭특가","이벤트특가"]: + return [true, true] + default: + return [false, false] + } + } +} diff --git a/frontend/SideDish/SideDish/ViewModel/FetchDataUseCase.swift b/frontend/SideDish/SideDish/ViewModel/FetchDataUseCase.swift deleted file mode 100644 index 411097255..000000000 --- a/frontend/SideDish/SideDish/ViewModel/FetchDataUseCase.swift +++ /dev/null @@ -1,20 +0,0 @@ - -import Foundation - -protocol FetchMenuUseCase { - func loadMenu(of: DataTaskManager.Url ,closure: @escaping ([Menu]) -> (Void)) -} - -class FetchDataUseCase: FetchMenuUseCase { - func loadMenu(of url: DataTaskManager.Url, closure: @escaping ([Menu]) -> (Void)) { - DataTaskManager.sendRequest(url: url, completion: { (result) in - switch result { - case .success(let data): - closure(data.body) - case.failure(let error): - // 실패시 persistence 내에 있는 데이터 반환하기 - print(error.localizedDescription) - } - }) - } -} diff --git a/frontend/SideDish/SideDish/ViewModel/FetchDetailMenuUseCase.swift b/frontend/SideDish/SideDish/ViewModel/FetchDetailMenuUseCase.swift new file mode 100644 index 000000000..d6532aa26 --- /dev/null +++ b/frontend/SideDish/SideDish/ViewModel/FetchDetailMenuUseCase.swift @@ -0,0 +1,19 @@ + +import Foundation + +class FetchDetailMenuUseCase { + + func loadDetailMenu(url: URL?, closure: @escaping (DetailMenu) -> (Void)) { + APIRequestManager.sendDetailRequest(url: url, completion: { (result) in + switch result { + case .success(let data): + closure(data) + case.failure(let error): + // 실패시 persistence 내에 있는 데이터 반환하기 + print(error.localizedDescription) + } + }) + } + +} + diff --git a/frontend/SideDish/SideDish/ViewModel/FetchMenuResponseUseCase.swift b/frontend/SideDish/SideDish/ViewModel/FetchMenuResponseUseCase.swift new file mode 100644 index 000000000..0afde71e1 --- /dev/null +++ b/frontend/SideDish/SideDish/ViewModel/FetchMenuResponseUseCase.swift @@ -0,0 +1,21 @@ + +import Foundation + +protocol FetchMenuUseCase { + func loadMenu(of: URL? ,closure: @escaping ([Menu], Int) -> (Void)) +} + +class FetchMenuResponseUseCase: FetchMenuUseCase { + func loadMenu(of url: URL?, closure: @escaping ([Menu], Int) -> (Void)) { + APIRequestManager.sendRequest(url: url, completion: { (result) in + switch result { + case .success(let data): + closure(data.items, data.categoryId) + case.failure(let error): + // 실패시 persistence 내에 있는 데이터 반환하기 + print(error.localizedDescription) + } + }) + } +} + diff --git a/frontend/SideDish/SideDish/ViewModel/MenuViewModel.swift b/frontend/SideDish/SideDish/ViewModel/MenuViewModel.swift index cc6586e98..fb5789f66 100644 --- a/frontend/SideDish/SideDish/ViewModel/MenuViewModel.swift +++ b/frontend/SideDish/SideDish/ViewModel/MenuViewModel.swift @@ -2,6 +2,9 @@ import Foundation class MenuViewModel { + + private(set) var categoryId: Int + private(set) var hash: String private(set) var image: String private(set) var title: String private(set) var body: String @@ -9,7 +12,9 @@ class MenuViewModel { private(set) var nPrice: NSAttributedString private(set) var badges: [String] - init(image: String, title: String, body: String, sPrice: String, nPrice: NSAttributedString, badges: [String]) { + init(hash: String, image: String, title: String, body: String, sPrice: String, nPrice: NSAttributedString, badges: [String]) { + self.categoryId = 0 + self.hash = hash self.image = image self.title = title self.body = body @@ -32,6 +37,10 @@ class MenuViewModel { return [false, false] } } + + func configureCategoryId(categoryId: Int) { + self.categoryId = categoryId + } } extension MenuViewModel: Hashable, Equatable { diff --git a/frontend/SideDish/SideDish/ViewModel/MenusViewModel.swift b/frontend/SideDish/SideDish/ViewModel/MenusViewModel.swift index b537398e7..b96ac5da9 100644 --- a/frontend/SideDish/SideDish/ViewModel/MenusViewModel.swift +++ b/frontend/SideDish/SideDish/ViewModel/MenusViewModel.swift @@ -11,32 +11,35 @@ class MenusViewModel { self.mainViewModel = [] self.soupViewModel = [] self.sideViewModel = [] - self.fetchMenuUseCase = FetchDataUseCase() + self.fetchMenuUseCase = FetchMenuResponseUseCase() } func fetchData() { - self.fetchMenuUseCase.loadMenu(of: .main) { menuList in - self.add(menuList: menuList, section: .main) + self.fetchMenuUseCase.loadMenu(of: URLManager.menu(of: .main)) { menuList, categoryId in + self.add(menuList: menuList, categoryId: categoryId, section: .main) } - self.fetchMenuUseCase.loadMenu(of: .soup) { menuList in - self.add(menuList: menuList, section: .soup) + self.fetchMenuUseCase.loadMenu(of: URLManager.menu(of: .soup)) { menuList, categoryId in + self.add(menuList: menuList, categoryId: categoryId, section: .soup) } - self.fetchMenuUseCase.loadMenu(of: .side) { menuList in - self.add(menuList: menuList, section: .side) + self.fetchMenuUseCase.loadMenu(of: URLManager.menu(of: .side)) { menuList, categoryId in + self.add(menuList: menuList, categoryId: categoryId, section: .side) } } - func add(menuList: [Menu], section:MainDiffableDataSource.sectionTitle) { + func add(menuList: [Menu], categoryId: Int, section:MainDiffableDataSource.sectionTitle) { let viewModelList = matchingViewModel(menuList: menuList) switch section { case .main: self.mainViewModel = viewModelList + self.mainViewModel.forEach { $0.configureCategoryId(categoryId: categoryId) } case .soup: self.soupViewModel = viewModelList + self.soupViewModel.forEach { $0.configureCategoryId(categoryId: categoryId) } case .side: self.sideViewModel = viewModelList + self.sideViewModel.forEach { $0.configureCategoryId(categoryId: categoryId) } } - NotificationCenter.default.post(name: MenusViewModel.changeMenu, object: self) + NotificationCenter.default.post(name: Notification.Name.fetchMenu, object: self) } func giveMenus(section: MainDiffableDataSource.sectionTitle) -> [MenuViewModel] { @@ -50,18 +53,31 @@ class MenusViewModel { } } - func matchingViewModel(menuList: [Menu]) -> [MenuViewModel] { + private func matchingViewModel(menuList: [Menu]) -> [MenuViewModel] { + let viewModelList: [MenuViewModel] = menuList.map() { menu in - let nPrice = stringToAttributedString(menu.nPrice) - let viewModel = MenuViewModel(image: menu.image, title: menu.title, body: menu.description, sPrice: menu.sPrice, nPrice: nPrice, badges: menu.badge ?? []) + let price = menu.normalPrice == nil ? nil : String(menu.normalPrice!) + let nPrice = stringToAttributedString(price) + let badges = menu.badge == [nil] ? [] : menu.badge.map{ $0! } + let viewModel = MenuViewModel(hash: menu.detailHash, image: menu.image, title: menu.title, body: menu.description, sPrice: convertDecimal(string: String(menu.salePrice)), nPrice: nPrice, badges: badges) return viewModel + } return viewModelList } - func stringToAttributedString(_ price: String?) -> NSAttributedString { + func convertDecimal(string: String) -> String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + + let stringToInt = Int(string)! + return numberFormatter.string(from: NSNumber(value: stringToInt))! + } + + private func stringToAttributedString(_ price: String?) -> NSAttributedString { if let pastPrice = price { - let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "\(pastPrice)원") + let convertedPrice = convertDecimal(string: pastPrice) + let attributeString: NSMutableAttributedString = NSMutableAttributedString(string: "\(convertedPrice)원") attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length)) return attributeString } else { @@ -69,8 +85,35 @@ class MenusViewModel { } } + func returnHash(indexPath: IndexPath) -> String? { + switch indexPath.section { + case 0: + return mainViewModel[indexPath.row].hash + case 1: + return soupViewModel[indexPath.row].hash + case 2: + return sideViewModel[indexPath.row].hash + default: + return nil + } + } + + func returnCategoryId(indexPath: IndexPath) -> Int? { + switch indexPath.section { + case 0: + return mainViewModel[indexPath.row].categoryId + case 1: + return soupViewModel[indexPath.row].categoryId + case 2: + return sideViewModel[indexPath.row].categoryId + default: + return nil + } + } + } -extension MenusViewModel { - static let changeMenu = Notification.Name("changeMenu") +extension Notification.Name { + static let fetchMenu = Notification.Name("fetchMenu") + static let fetchDetailMenu = Notification.Name("fetchDetailMenu") } diff --git a/frontend/SideDish/SideDish/ViewModel/OrderViewModel.swift b/frontend/SideDish/SideDish/ViewModel/OrderViewModel.swift new file mode 100644 index 000000000..10571bee8 --- /dev/null +++ b/frontend/SideDish/SideDish/ViewModel/OrderViewModel.swift @@ -0,0 +1,33 @@ + +import Foundation + +class OrderViewModel { + + private(set) var orderCount: Int + private var detailHash: String + + init() { + self.orderCount = 1 + self.detailHash = "" + } + + func increaseOrderCount() { + self.orderCount += 1 + } + + func decreaseOrderCount() { + if self.orderCount > 0 { + self.orderCount -= 1 + } else { + self.orderCount = 0 + } + } + + func orderProduct(detailHash: String) { + self.detailHash = detailHash + } + + func isOrderAvailable(stock: Int) -> Bool { + return stock >= self.orderCount + } +}