Hendi hace 3 años
padre
commit
e704f8aced

+ 45 - 0
Sirani/Model/Label.swift

@@ -0,0 +1,45 @@
+//
+//  Label.swift
+//  Sirani
+//
+//  Created by Hendi on 20/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import Foundation
+
+struct Label {
+    private(set) public var nomor: String
+    private(set) public var cust_name: String
+    private(set) public var eqp_name: String
+    private(set) public var lic_id: Int
+    private(set) public var brand: String
+    private(set) public var lic_date: String
+    private(set) public var lic_no: String
+    private(set) public var exp_date: String
+    private(set) public var models: String
+    private(set) public var plg_id: Int
+    private(set) public var apl_id: Int
+    private(set) public var negara: String
+    private(set) public var model: String
+    private(set) public var merk: String
+    private(set) public var berlaku: String
+    
+    init (nomor: String, cust_name: String, eqp_name: String, lic_id: Int, brand: String, lic_date: String, lic_no: String, exp_date: String, models: String, plg_id: Int, apl_id: Int, negara: String, model: String, merk: String, berlaku: String) {
+        self.nomor = nomor
+        self.cust_name = cust_name
+        self.eqp_name = eqp_name
+        self.lic_id = lic_id
+        self.brand = brand
+        self.lic_date = lic_date
+        self.lic_no = lic_no
+        self.exp_date = exp_date
+        self.models = models
+        self.plg_id = plg_id
+        self.apl_id = apl_id
+        self.merk = merk
+        self.model = model
+        self.negara = negara
+        self.berlaku = berlaku
+    }
+}

+ 112 - 0
Sirani/View/Label/LabelCell.swift

@@ -0,0 +1,112 @@
+//
+//  LabelCell.swift
+//  Sirani
+//
+//  Created by Hendi on 20/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import UIKit
+import MaterialComponents.MaterialCards
+
+protocol LabelCellDelegate: NSObjectProtocol  {
+    func openLabelDetail(data : Label?)
+}
+
+class LabelCell: UITableViewCell {
+    
+    @IBOutlet weak var card: MDCCard!
+    @IBOutlet weak var lblNomor: UILabel!
+    @IBOutlet weak var lblPemohon: UILabel!
+    @IBOutlet weak var lblPerangkat: UILabel!
+    @IBOutlet weak var lblMerk: UILabel!
+    @IBOutlet weak var lblModel: UILabel!
+    @IBOutlet weak var lblBrand: UILabel!
+    @IBOutlet weak var lblBerlakuDate: UILabel!
+    @IBOutlet weak var lblBerakhirDate: UILabel!
+    @IBOutlet weak var lblStatus: UILabel!
+
+    var cellDelegate: LabelCellDelegate?
+    var dataLabel: Label?
+    
+    func updateViews(labelData: Label) {
+        card.isInteractable = true
+        
+        let gesture = UITapGestureRecognizer(target: self, action:  #selector(self.onItemTap))
+        card.addGestureRecognizer(gesture)
+        
+        lblNomor.text = labelData.nomor
+        lblPemohon.text = labelData.cust_name
+        lblPerangkat.text = labelData.eqp_name
+        lblMerk.text = labelData.merk
+        lblModel.text = labelData.model
+        
+        var brand = ""
+        if labelData.brand != "null" {
+            brand = labelData.brand
+        }
+        lblBrand.text = brand
+        
+        var expDate = ""
+        
+        if (labelData.exp_date == "null" || labelData.exp_date.isEmpty) {
+            expDate = ""
+        } else {
+            let inputFormatter = DateFormatter()
+            inputFormatter.dateFormat = "yyyy-MM-dd"
+            let expDateString = String(labelData.exp_date.prefix(10))
+            
+            if let output = inputFormatter.date(from: expDateString) {
+                let outputFormatter = DateFormatter()
+                outputFormatter.dateFormat = "dd MMM yyyy"
+                expDate = outputFormatter.string(from: output)
+            }
+        }
+        
+        lblBerakhirDate.text = expDate
+        
+        var licDate = ""
+        if (labelData.lic_date == "null" || labelData.lic_date.isEmpty) {
+            licDate = ""
+        } else {
+            let inputFormatter = DateFormatter()
+            inputFormatter.dateFormat = "yyyy-MM-dd"
+            let expDateString = String(labelData.lic_date.prefix(10))
+            
+            if let output = inputFormatter.date(from: expDateString) {
+                let outputFormatter = DateFormatter()
+                outputFormatter.dateFormat = "dd MMM yyyy"
+                licDate = outputFormatter.string(from: output)
+            }
+        }
+        lblBerlakuDate.text = licDate
+        
+        var status = ""
+
+        lblStatus?.layer.cornerRadius = 6
+        lblStatus?.layer.masksToBounds = true
+        
+        switch labelData.berlaku {
+            case "0":
+                status = "Tidak Berlaku"
+                lblStatus.backgroundColor = UIColor.red
+            case "1":
+                status = "Berlaku"
+                lblStatus.backgroundColor = UIColor.green
+            case "3":
+                status = "Revisi"
+                lblStatus.backgroundColor = UIColor.orange
+            default:
+                lblStatus.backgroundColor = UIColor.white
+        }
+        
+        lblStatus.text = status
+        
+        self.dataLabel = labelData
+    }
+    
+    @objc func onItemTap(sender : UITapGestureRecognizer) {
+        cellDelegate?.openLabelDetail(data: dataLabel)
+    }
+
+}

+ 122 - 0
Sirani/View/Label/LabelPresenter.swift

@@ -0,0 +1,122 @@
+//
+//  LabelPresenter.swift
+//  Sirani
+//
+//  Created by Hendi on 20/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import Foundation
+import SwiftyJSON
+
+class LabelPresenter {
+    
+    private let siraniApiServices: SiraniApiServices
+    weak private var view: LabelViewDelegate?
+    
+    init(siraniApiServices: SiraniApiServices) {
+        self.siraniApiServices = siraniApiServices
+    }
+    
+    func attachView(view: LabelViewDelegate?) {
+        self.view = view
+    }
+    
+    func detachView() {
+        self.view = nil
+    }
+    
+    func loadData(status: String, limit: Int, page: Int, search: String) {
+        self.view?.hideNoDataLayout()
+        self.view?.showLoading()
+        
+        var label = [Label]()
+        siraniApiServices.postToGetSertifikatPrivate(user_id: UserState.instance.getUserId(), cust_id: UserState.instance.getCustId(), role_id: UserState.instance.getRoleId(), status: status, limit: limit, page: page, search: search).responseJSON { response in
+            if response.error == nil {
+                guard let data = response.data else { return }
+                if let json = try? JSON(data: data) {
+                    if let data = json["data"].array {
+                        if data.count > 0 {
+                            for item in data {
+                                let labelData = Label(
+                                    nomor: item["nomor"].stringValue,
+                                    cust_name: item["cust_name"].stringValue,
+                                    eqp_name: item["eqp_name"].stringValue,
+                                    lic_id: item["lic_id"].intValue,
+                                    brand: item["brand"].stringValue,
+                                    lic_date: item["lic_date"].stringValue,
+                                    lic_no: item["lic_no"].stringValue,
+                                    exp_date: item["exp_date"].stringValue,
+                                    models: item["models"].stringValue,
+                                    plg_id: item["plg_id"].intValue,
+                                    apl_id: item["apl_id"].intValue,
+                                    negara: item["negara"].stringValue,
+                                    model: item["model"].stringValue,
+                                    merk: item["merk"].stringValue,
+                                    berlaku: item["berlaku"].stringValue
+                                )
+                                label.append(labelData)
+                            }
+                            
+                            self.setDataList(dataList: label)
+                        } else {
+                            if page > 1 {
+                                self.setDataList(dataList: label)
+                            } else {
+                                self.showNoData()
+                            }
+                        }
+                    } else {
+                        self.showNoData()
+                    }
+                } else {
+                    self.showNoData()
+                }
+            } else {
+                self.showNoData()
+            }
+        }
+    }
+    
+    func downloadFile(urlWeb: String, filename: String) {
+        self.view?.showLoading()
+        
+        siraniApiServices.downloadFile(urlWeb: urlWeb, filename: filename).responseData { response in
+            if let urlFile = response.fileURL {
+                self.onDownloadSuccess(urlFile: urlFile)
+            } else {
+                self.onDownloadFailed()
+            }
+        }
+        
+    }
+    
+    // other functions
+    private func setDataList(dataList: [Label]) {
+        self.view?.setDataList(dataList: dataList)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
+            self.view?.hideLoading()
+        }
+    }
+    
+    private func showNoData() {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+            self.view?.showNoDataLayout()
+            self.view?.hideLoading()
+        }
+    }
+    
+    private func onDownloadSuccess(urlFile: URL) {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+            self.view?.onDownloadSuccess(urlFile: urlFile)
+            self.view?.hideLoading()
+        }
+    }
+    
+    private func onDownloadFailed() {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
+            self.view?.onDownloadFailed()
+            self.view?.hideLoading()
+        }
+    }
+}

+ 279 - 0
Sirani/View/Label/LabelVC.swift

@@ -0,0 +1,279 @@
+//
+//  LabelVC.swift
+//  Sirani
+//
+//  Created by Hendi on 20/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import UIKit
+
+class LabelVC: UIViewController {
+    
+    // Outlets
+    @IBOutlet weak var labelTV: UITableView!
+    @IBOutlet weak var noDataLayout: UIView!
+    @IBOutlet weak var progressLayout: UIView!
+    @IBOutlet weak var progress: UIActivityIndicatorView!
+    @IBOutlet weak var subTitleLabel: UILabel!
+    
+    // View Id
+    private let cellId = "LabelCellId"
+    
+    // Segue Id
+    private let unwindToHomeSegueId = "unwindLabelToHomeSegueId"
+    private let searchSegueId = "searchLabelSegueId"
+    
+    // Initialize
+    private var presenter: LabelPresenter!
+    private var dataList: [Label] =  [Label]()
+    private var documentInteractorController: UIDocumentInteractionController!
+    
+    // Request Params
+    private var limit: Int = 5
+    private var page: Int = 1
+    private var status: String = "label_all"
+    private var search: String = ""
+    
+    // Others
+    private var loading = false
+    private var lastDataLoadedEmpty = false
+    private var loadMore = false
+    
+    // View Lifecycles
+    override var preferredStatusBarStyle: UIStatusBarStyle {
+        return .lightContent
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        labelTV.delegate = self
+        labelTV.dataSource = self
+        
+        presenter = LabelPresenter(siraniApiServices: SiraniApiServices())
+        presenter.attachView(view: self)
+        loadData()
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        presenter.attachView(view: self)
+    }
+    
+    override func viewDidDisappear(_ animated: Bool) {
+        presenter.detachView()
+    }
+    
+    // private functions
+    private func loadData() {
+        if !loading && !lastDataLoadedEmpty {
+            if ConnectionHelper.isConnectedToNetwork() {
+                presenter.loadData(status: status, limit: limit, page: page, search: search)
+            } else {
+                showNoConnection()
+            }
+        }
+    }
+    
+    private func downloadFile(urlWeb: String, filename: String) {
+        if !loading {
+            if ConnectionHelper.isConnectedToNetwork() {
+                presenter.downloadFile(urlWeb: urlWeb, filename: filename)
+            } else {
+                showNoConnection()
+            }
+        }
+    }
+    
+    // Actions
+    @IBAction func onBtnBackTap(_ sender: Any) {
+        performSegue(withIdentifier: unwindToHomeSegueId, sender: self)
+    }
+    
+    @IBAction func onBtnSearchTap(_ sender: Any) {
+        performSegue(withIdentifier: searchSegueId, sender: self)
+    }
+    
+    @IBAction func unwindToLabel(segue: UIStoryboardSegue) {}
+    
+}
+
+// View Delegate Protocols
+extension LabelVC: LabelViewDelegate {
+    
+    func showNoConnection() {
+        let alertNoConnection = UIAlertController(
+            title: Constants.instance.ALERT_NO_CONNECTION_TITLE,
+            message: Constants.instance.ALERT_NO_CONNECTION_MSG,
+            preferredStyle: .alert
+        )
+        alertNoConnection.addAction(
+            UIAlertAction(title: Constants.instance.ALERT_NO_CONNECTION_YESS, style: .default, handler: nil)
+        )
+        self.present(alertNoConnection, animated: true, completion: nil)
+    }
+    
+    func showLoading() {
+        loading = true
+        progress.startAnimating()
+        progressLayout.show()
+    }
+    
+    func hideLoading() {
+        loading = false
+        progress.stopAnimating()
+        progressLayout.hide()
+    }
+    
+    func showNoDataLayout() {
+        if page == 1 {
+            noDataLayout.show()
+        }
+    }
+    
+    func hideNoDataLayout() {
+        noDataLayout.hide()
+    }
+    
+    func setDataList(dataList: [Label]) {
+        
+        if page == 1 {
+            noDataLayout.hide()
+        }
+        
+        if dataList.count > 0 {
+            
+            for data in dataList {
+                self.dataList.append(data)
+                labelTV.beginUpdates()
+                labelTV.insertRows(
+                    at: [IndexPath(row: self.dataList.count-1, section: 0)],
+                    with: .none
+                )
+                labelTV.endUpdates()
+            }
+            
+            page += 1
+            loadMore = true
+        } else {
+            lastDataLoadedEmpty = true
+            loadMore = false
+        }
+    }
+    
+    func onDownloadSuccess(urlFile: URL) {
+        self.documentInteractorController = UIDocumentInteractionController()
+        self.documentInteractorController.delegate = self
+        self.documentInteractorController.url = urlFile
+        self.documentInteractorController.presentPreview(animated: true)
+    }
+    
+    func onDownloadFailed() {
+        let alertNoConnection = UIAlertController(
+            title: Constants.instance.ALERT_NO_CONNECTION_TITLE,
+            message: "Download Gagal",
+            preferredStyle: .alert
+        )
+        alertNoConnection.addAction(
+            UIAlertAction(title: Constants.instance.ALERT_NO_CONNECTION_YESS, style: .default, handler: nil)
+        )
+        self.present(alertNoConnection, animated: true, completion: nil)
+    }
+}
+
+// UITableController Protocols
+extension LabelVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate, LabelCellDelegate {
+    func openLabelDetail(data: Label?) {
+        print(data?.apl_id)
+    }
+
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return dataList.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        if tableView == labelTV {
+            if let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? LabelCell {
+
+                let data = dataList[indexPath.row]
+                cell.cellDelegate = self
+//                cell.btnDwnldPeringatan.tag = indexPath.row
+//                cell.btnDownloadQrCode.tag = indexPath.row
+//                cell.btnDownloadLabel.tag = indexPath.row
+                cell.updateViews(labelData: data)
+
+                return cell
+            } else {
+                return LabelCell()
+            }
+        } else {
+            return LabelCell()
+        }
+    }
+
+    func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        let threshold = -5
+        let contentOffset = scrollView.contentOffset.y
+        let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;
+
+        if loadMore && !loading && (Int(maximumOffset - contentOffset) <= threshold) {
+            loadData()
+        }
+    }
+
+//    func download(urlWeb: String?) {
+//        if urlWeb != nil {
+//            var filename = "file.pdf"
+//            let values = urlWeb!.split(separator: "/")
+//            if values.count > 0 {
+//                filename = String(values[values.count-1])
+//            }
+//
+//            self.downloadFile(urlWeb: urlWeb!, filename: filename)
+//        }
+//    }
+
+}
+
+// DocumentInteractionController
+extension LabelVC: UIDocumentInteractionControllerDelegate {
+    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
+        guard let navVC = self.navigationController else {
+            return self
+        }
+        return navVC
+    }
+}
+
+// Search Delegate Protocols
+extension LabelVC: SearchLabelDelegate {
+    func onSearch(search: String, status: String, jenisLabel: String) {
+        presenter.attachView(view: self)
+
+        loadMore = false
+        loading = false
+        lastDataLoadedEmpty = false
+
+        self.limit = 5
+        self.page = 1
+        self.search = search
+        self.status = status
+        
+        self.subTitleLabel.text = jenisLabel
+
+        dataList.removeAll()
+        labelTV.reloadData()
+
+        loadData()
+    }
+
+    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
+        if segue.identifier == searchSegueId {
+            let destinationVC: SearchLabelVC = segue.destination as! SearchLabelVC
+            destinationVC.delegate = self
+        }
+    }
+    
+}
+
+

+ 20 - 0
Sirani/View/Label/LabelViewDelegate.swift

@@ -0,0 +1,20 @@
+//
+//  LabelViewDelegate.swift
+//  Sirani
+//
+//  Created by Hendi on 20/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import Foundation
+
+protocol LabelViewDelegate: NSObjectProtocol {
+    func showNoConnection()
+    func showLoading()
+    func hideLoading()
+    func showNoDataLayout()
+    func hideNoDataLayout()
+    func setDataList(dataList: [Label])
+    func onDownloadSuccess(urlFile: URL)
+    func onDownloadFailed()
+}

+ 146 - 1
Sirani/View/Label/SearchLabelVC.swift

@@ -6,4 +6,149 @@
 //  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
 //
 
-import Foundation
+import UIKit
+
+protocol SearchLabelDelegate {
+    func onSearch(search: String, status: String, jenisLabel: String)
+}
+
+class SearchLabelVC: UIViewController {
+    
+    // Outlets / Views
+    @IBOutlet weak var inputSearch: RoundTextField!
+    @IBOutlet weak var optionsLayoutTV: UITableView!
+    @IBOutlet weak var optionsLayout: UIView!
+    @IBOutlet weak var optionsBg: UIView!
+    @IBOutlet weak var btnStatusOptions: UIButton!
+    
+    // Initialize
+    var delegate: SearchLabelDelegate?
+    
+    // constants vallue data
+    private let jenisSertifikatOptionItems = [
+        OptionItem(label: "Semua", value: "label_all"),
+        OptionItem(label: "Sudah Upload", value: "label_sudah_upload"),
+        OptionItem(label: "Belum Upload", value: "label_belum_upload")
+    ]
+    private let OPTION_JENIS_SERTIFIKAT_ID = 1
+    
+    // Others
+    private var selectedOptions = 0
+    private var selectedOptionItems = [OptionItem]()
+    private var optionsShown = false
+    
+    // Result Values
+    private var search = ""
+    private var status = ""
+    private var jenisLabel = "Semua"
+    
+    // View Lifecycles
+    override var preferredStatusBarStyle: UIStatusBarStyle {
+        return .lightContent
+    }
+    
+    override func viewWillAppear(_ animated: Bool) {
+        btnStatusOptions.layer.masksToBounds = false
+        btnStatusOptions.layer.shadowColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
+        btnStatusOptions.layer.shadowOpacity = 0.5
+        btnStatusOptions.layer.shadowOffset = CGSize(width: 1, height: 1)
+        btnStatusOptions.layer.shadowRadius = 1
+        status = jenisSertifikatOptionItems[0].value as! String
+        jenisLabel = jenisSertifikatOptionItems[0].label
+        btnStatusOptions.setTitle(jenisLabel, for: .normal)
+        
+        hideOptions()
+        optionsLayoutTV.bringSubviewToFront(optionsBg)
+        let onOptionsBgTapped = UITapGestureRecognizer(target: self, action: #selector(hideOptions))
+        optionsBg.addGestureRecognizer(onOptionsBgTapped)
+    }
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        
+        optionsLayoutTV.delegate = self
+        optionsLayoutTV.dataSource = self
+        optionsLayoutTV.tableFooterView = UIView()
+    }
+    
+    @IBAction func onInputSearchKeyTap(_ sender: Any) {
+        view.endEditing(true)
+    }
+    
+    @IBAction func unwindToLabel(_ sender: Any) {
+        if optionsShown {
+            hideOptions()
+        } else {
+            performSegue(withIdentifier: "unwindSearchToLabelSegueId", sender: self)
+        }
+    }
+        
+    @IBAction func onJenisSertifikatOptionsTapped(_ sender: Any) {
+        selectedOptions = OPTION_JENIS_SERTIFIKAT_ID
+        selectedOptionItems.removeAll()
+        selectedOptionItems += jenisSertifikatOptionItems
+        optionsLayoutTV.reloadData()
+        showOptions()
+    }
+    
+    @IBAction func onResetTapped(_ sender: Any) {
+        search = ""
+        status = ""
+        jenisLabel = "Semua"
+        
+        inputSearch.text = ""
+        btnStatusOptions.setTitle(jenisLabel, for: .normal)
+    }
+    
+    @IBAction func onSearchTapped(_ sender: Any) {
+        if let searchText = inputSearch.getText() {
+            search = searchText
+        }
+        print("TEST : ")
+        print("SEARCH \(search)")
+        print("STATUS \(status)")
+        print("JENIS \(jenisLabel)")
+        
+        self.delegate?.onSearch(search: search, status: status, jenisLabel: jenisLabel)
+        performSegue(withIdentifier: "unwindSearchToLabelSegueId", sender: self)
+    }
+    
+    private func showOptions() {
+        optionsLayout.show()
+        optionsShown = true
+    }
+    
+    @objc private func hideOptions() {
+        optionsLayout.hide()
+        optionsShown = false
+    }
+
+}
+
+extension SearchLabelVC: UITableViewDelegate, UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return selectedOptionItems.count
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        if let cell = tableView.dequeueReusableCell(withIdentifier: "searchLabelCell", for: indexPath) as? OptionsCell {
+            
+            let optionItem = selectedOptionItems[indexPath.row]
+            cell.updateViews(item: optionItem)
+            
+            return cell
+        } else {
+            return OptionsCell()
+        }
+    }
+    
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+        if selectedOptions == OPTION_JENIS_SERTIFIKAT_ID {
+            status = selectedOptionItems[indexPath.row].value as! String
+            jenisLabel = selectedOptionItems[indexPath.row].label
+            btnStatusOptions.setTitle(jenisLabel, for: .normal)
+        }
+        
+        hideOptions()
+    }
+}