Explorar el Código

list & search aplikasi

Hendi hace 3 años
padre
commit
4ad993c23f

+ 24 - 0
Sirani.xcodeproj/project.pbxproj

@@ -13,6 +13,12 @@
 		0485C6A22A4194030072B449 /* LabelCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485C6A12A4194030072B449 /* LabelCell.swift */; };
 		0485C6A42A41941B0072B449 /* LabelViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485C6A32A41941B0072B449 /* LabelViewDelegate.swift */; };
 		0485C6A62A4195B30072B449 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0485C6A52A4195B30072B449 /* Label.swift */; };
+		048A26552A42E5CA008CDB82 /* AplikasiVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A26542A42E5CA008CDB82 /* AplikasiVC.swift */; };
+		048A26572A42E5F8008CDB82 /* AplikasiViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A26562A42E5F8008CDB82 /* AplikasiViewDelegate.swift */; };
+		048A26592A42E612008CDB82 /* AplikasiPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A26582A42E612008CDB82 /* AplikasiPresenter.swift */; };
+		048A265B2A42E621008CDB82 /* AplikasiCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A265A2A42E621008CDB82 /* AplikasiCell.swift */; };
+		048A265D2A42E636008CDB82 /* SearchAplikasiVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A265C2A42E636008CDB82 /* SearchAplikasiVC.swift */; };
+		048A265F2A42F48F008CDB82 /* Aplikasi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048A265E2A42F48F008CDB82 /* Aplikasi.swift */; };
 		04CAAAD12A4007B9005DBD04 /* SertifikatNewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04CAAAD02A4007B9005DBD04 /* SertifikatNewCell.swift */; };
 		04CAAAD42A4042C4005DBD04 /* SearchSertifikatNewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04CAAAD32A4042C4005DBD04 /* SearchSertifikatNewVC.swift */; };
 		04D0FD7E2A31B3EF00DC89D1 /* SertifikatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D0FD7D2A31B3EF00DC89D1 /* SertifikatViewController.swift */; };
@@ -234,6 +240,12 @@
 		0485C6A12A4194030072B449 /* LabelCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelCell.swift; sourceTree = "<group>"; };
 		0485C6A32A41941B0072B449 /* LabelViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewDelegate.swift; sourceTree = "<group>"; };
 		0485C6A52A4195B30072B449 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
+		048A26542A42E5CA008CDB82 /* AplikasiVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AplikasiVC.swift; sourceTree = "<group>"; };
+		048A26562A42E5F8008CDB82 /* AplikasiViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AplikasiViewDelegate.swift; sourceTree = "<group>"; };
+		048A26582A42E612008CDB82 /* AplikasiPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AplikasiPresenter.swift; sourceTree = "<group>"; };
+		048A265A2A42E621008CDB82 /* AplikasiCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AplikasiCell.swift; sourceTree = "<group>"; };
+		048A265C2A42E636008CDB82 /* SearchAplikasiVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchAplikasiVC.swift; sourceTree = "<group>"; };
+		048A265E2A42F48F008CDB82 /* Aplikasi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Aplikasi.swift; sourceTree = "<group>"; };
 		04CAAAD02A4007B9005DBD04 /* SertifikatNewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SertifikatNewCell.swift; sourceTree = "<group>"; };
 		04CAAAD32A4042C4005DBD04 /* SearchSertifikatNewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSertifikatNewVC.swift; sourceTree = "<group>"; };
 		04D0FD7D2A31B3EF00DC89D1 /* SertifikatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SertifikatViewController.swift; sourceTree = "<group>"; };
@@ -875,6 +887,7 @@
 				D8854A07255F90D500D581DF /* FaqCategory.swift */,
 				D8164C2A255FD62C00A3AE48 /* FaqContent.swift */,
 				0485C6A52A4195B30072B449 /* Label.swift */,
+				048A265E2A42F48F008CDB82 /* Aplikasi.swift */,
 			);
 			path = Model;
 			sourceTree = "<group>";
@@ -998,6 +1011,11 @@
 		FCE9CA0C226C3B7B0023F6A6 /* Aplikasi */ = {
 			isa = PBXGroup;
 			children = (
+				048A26542A42E5CA008CDB82 /* AplikasiVC.swift */,
+				048A26562A42E5F8008CDB82 /* AplikasiViewDelegate.swift */,
+				048A26582A42E612008CDB82 /* AplikasiPresenter.swift */,
+				048A265A2A42E621008CDB82 /* AplikasiCell.swift */,
+				048A265C2A42E636008CDB82 /* SearchAplikasiVC.swift */,
 				FCE9CA0D226C3B920023F6A6 /* AplikasiDitolak */,
 				FCE9CA1A226CCF2E0023F6A6 /* AplikasiStatus */,
 			);
@@ -1367,6 +1385,7 @@
 				FCEE081C255F995D006DFDF5 /* FaqCategoryCell.swift in Sources */,
 				0485C6A42A41941B0072B449 /* LabelViewDelegate.swift in Sources */,
 				FCADBBEE2247127700F0B4C6 /* FaqVC.swift in Sources */,
+				048A265D2A42E636008CDB82 /* SearchAplikasiVC.swift in Sources */,
 				FC6A5D812271DC8F0096664E /* SearchNewsVC.swift in Sources */,
 				FCF7BB0B22636F0100532DC2 /* SertifikatSudahUploadPresenter.swift in Sources */,
 				FCE76ADF2266D3FB00DAD3CC /* SertifikatBelumUploadDetail.swift in Sources */,
@@ -1462,7 +1481,9 @@
 				D8854A0A255F979C00D581DF /* FaqViewDelegate.swift in Sources */,
 				FCF468F2225A2E1D00B9227A /* UserState.swift in Sources */,
 				FCD23BC92261EC950090B157 /* OdsAplikasiEntryViewDelegate.swift in Sources */,
+				048A26572A42E5F8008CDB82 /* AplikasiViewDelegate.swift in Sources */,
 				FCF7BAF92263324200532DC2 /* SurveyList.swift in Sources */,
+				048A265B2A42E621008CDB82 /* AplikasiCell.swift in Sources */,
 				0485C69E2A4193D20072B449 /* LabelPresenter.swift in Sources */,
 				FC6A5D8E227299F40096664E /* SertifikatTidakBerlaku.swift in Sources */,
 				FCE76AE42267650D00DAD3CC /* SearchOdsSp2OtorisasiVC.swift in Sources */,
@@ -1505,6 +1526,7 @@
 				D8164C2B255FD62C00A3AE48 /* FaqContent.swift in Sources */,
 				FCE9CA24226CCF920023F6A6 /* SearchAplikasiStatusVC.swift in Sources */,
 				FC6A5D8A2272998F0096664E /* SertifikatTidakBerlakuCell.swift in Sources */,
+				048A265F2A42F48F008CDB82 /* Aplikasi.swift in Sources */,
 				FCBAA3F422741556008AC73C /* DaftarRhuVC.swift in Sources */,
 				FC3DADBC225072C900CF4C81 /* SearchDaftarSertifikatVC.swift in Sources */,
 				FC74C514226B508E00E55F29 /* SearchSertifikatSudahUploadVC.swift in Sources */,
@@ -1512,6 +1534,7 @@
 				FC6B67992268974F0042656E /* OdsSp2BaruCell.swift in Sources */,
 				FCEE51F4225FF668000AE43B /* HasilSurveyViewDelegate.swift in Sources */,
 				FC6A5D88227299730096664E /* SertifikatTidakBerlakuPresenter.swift in Sources */,
+				048A26592A42E612008CDB82 /* AplikasiPresenter.swift in Sources */,
 				FCF7BAF32263143200532DC2 /* OdsSp2PrintCell.swift in Sources */,
 				FC8568F2225116100012F927 /* InformasiSertifikasiVC.swift in Sources */,
 				FC56AD7B2258DB6900D0EAE0 /* NewsDetail.swift in Sources */,
@@ -1535,6 +1558,7 @@
 				FC56AD73225886FE00D0EAE0 /* NewsCell.swift in Sources */,
 				FCECB8172254E53400A8F5A8 /* BalaiUjiPresenter.swift in Sources */,
 				FCBAA3E822740CC8008AC73C /* Sp2TambahanVC.swift in Sources */,
+				048A26552A42E5CA008CDB82 /* AplikasiVC.swift in Sources */,
 				FCF7BAE82262FCCE00532DC2 /* OdsSp2OtorasiViewDelegate.swift in Sources */,
 				FC8568F6225125C00012F927 /* DetailInformasiSertifikasiVC.swift in Sources */,
 				FC19C27A255AD503004E0912 /* NotificationListVC.swift in Sources */,

BIN
Sirani.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate


+ 25 - 1
Sirani/ApiServices/SiraniApiServices.swift

@@ -180,7 +180,7 @@ class SiraniApiServices {
             "search" : search
         ]
         
-        return AF.request(METHOD_GET_TARIF_SERTIFIKAT, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
+        return AF.request(METHOD_GET_TARIF_SERTIFIKAT_NEW, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
     }
     
     // Balai Uji -> BalaiUjiVC
@@ -680,6 +680,9 @@ class SiraniApiServices {
             "search" : search
         ]
         
+        print("url : \(METHOD_GET_SERTIFIKAT_PUBLIC)")
+        print("params : \(body)")
+        
         return AF.request(METHOD_GET_SERTIFIKAT_PUBLIC, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
     }
     
@@ -695,6 +698,27 @@ class SiraniApiServices {
             "search" : search
         ]
         
+        print("url : \(METHOD_GET_SERTIFIKAT_PRIVATE)")
+        print("params : \(body)")
+        
         return AF.request(METHOD_GET_SERTIFIKAT_PRIVATE, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
     }
+    
+    func postToGetAplikasi(user_id: Int, cust_id: Int, role_id: String, status: String, limit: Int, page: Int, search: String) -> DataRequest {
+        let headers: HTTPHeaders = ["Accept" : "Application/json; charset=utf-8"]
+        let body: Parameters = [
+            "user_id" : String(user_id),
+            "cust_id" : String(cust_id),
+            "role_id" : role_id,
+            "status" : status,
+            "limit" : String(limit),
+            "page" : String(page),
+            "search" : search
+        ]
+        
+        print("url : \(METHOD_GET_APLIKASI)")
+        print("params : \(body)")
+        
+        return AF.request(METHOD_GET_APLIKASI, method: .post, parameters: body, encoding: URLEncoding.default, headers: headers)
+    }
 }

+ 6 - 0
Sirani/ApiServices/UriConstants.swift

@@ -106,3 +106,9 @@ let METHOD_GET_NEWS_DETAIL_NEW = "\(HOST_API_NEW)\(API)\(VERSION)Flashnewsdetail
 let METHOD_GET_SERTIFIKAT_PRIVATE = "\(HOST_API_NEW)\(API)\(VERSION)SertifikatPrivate"
 let METHOD_GET_SERTIFIKAT_PUBLIC = "\(HOST_API_NEW)\(API)\(VERSION)SertifikatPublic"
 
+// APLIKASI
+let METHOD_GET_APLIKASI = "\(HOST_API_NEW)\(API)\(VERSION)Aplikasi"
+
+// TARIF SERTIFIKAT
+let METHOD_GET_TARIF_SERTIFIKAT_NEW = "\(HOST_API_NEW)\(API)\(VERSION)Tarifsertifikasi"
+

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 20390 - 19652
Sirani/Base.lproj/Main.storyboard


+ 48 - 0
Sirani/Model/Aplikasi.swift

@@ -0,0 +1,48 @@
+//
+//  Aplikasi.swift
+//  Sirani
+//
+//  Created by Hendi on 21/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import Foundation
+
+struct Aplikasi {
+    private(set) public var id: String
+    private(set) public var apl_id: Int
+    private(set) public var cust_id: Int
+    private(set) public var cust_name: String
+    private(set) public var id_izin: String
+    private(set) public var id_proyek: String
+    private(set) public var nib: String
+    private(set) public var resi_no: String
+    private(set) public var resi_date: String
+    private(set) public var eqp_name: String
+    private(set) public var merk: String
+    private(set) public var model: String
+    private(set) public var brand: String
+    private(set) public var disposisi: String
+    private(set) public var status: String
+    private(set) public var StatusStr: String
+    
+    init (id: String, apl_id: Int, cust_id: Int, cust_name: String, id_izin: String, id_proyek: String, nib: String, resi_no: String, resi_date: String, eqp_name: String, merk: String, model: String, brand: String, disposisi: String, status: String, StatusStr: String) {
+        self.id = id
+        self.apl_id = apl_id
+        self.cust_id = cust_id
+        self.cust_name = cust_name
+        self.id_izin = id_izin
+        self.id_proyek = id_proyek
+        self.nib = nib
+        self.resi_no = resi_no
+        self.resi_date = resi_date
+        self.eqp_name = eqp_name
+        self.merk = merk
+        self.model = model
+        self.brand = brand
+        self.disposisi = disposisi
+        self.status = status
+        self.StatusStr = StatusStr
+    }
+}
+

+ 2 - 2
Sirani/Utils/MenuListData.swift

@@ -23,8 +23,8 @@ class MenuListData {
     let moreMenuUtamaNewCellId = "moreMenuUtamaNewCellId"
     private let menuUtamaNewItems = [
         MenuItem(segueId: "SertifikatSegueId", icon: "menu-utama-daftar-sertifikat.png", name: "Sertifikat", totalNotif: 0),
-        MenuItem(segueId: "TarifSertifikatSegueId", icon: "menu-utama-tarif-sertifikat.png", name: "Aplikasi", totalNotif: 0),
-        MenuItem(segueId: "InformasiSertifikasiSegueId", icon: "menu-utama-informasi-sertifikat.png", name: "Label", totalNotif: 0)
+        MenuItem(segueId: "AplikasiSegueId", icon: "menu-utama-tarif-sertifikat.png", name: "Aplikasi", totalNotif: 0),
+        MenuItem(segueId: "LabelSegueId", icon: "menu-utama-informasi-sertifikat.png", name: "Label", totalNotif: 0)
     ]
     func getMenuUtamaNewData(userRoleId: String) -> [MenuItem] {
         return menuUtamaNewItems

+ 75 - 0
Sirani/View/Aplikasi/AplikasiCell.swift

@@ -0,0 +1,75 @@
+//
+//  AplikasiCell.swift
+//  Sirani
+//
+//  Created by Hendi on 21/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import UIKit
+import MaterialComponents.MaterialCards
+
+protocol AplikasiCellDelegate: NSObjectProtocol  {
+    func openAplikasiDetail(data : Aplikasi?)
+}
+
+class AplikasiCell: UITableViewCell {
+    
+    @IBOutlet weak var card: MDCCard!
+    @IBOutlet weak var lblIdIzin : UILabel!
+    @IBOutlet weak var lblAplId : UILabel!
+    @IBOutlet weak var lblResiNo : UILabel!
+    @IBOutlet weak var lblResiDate : UILabel!
+    @IBOutlet weak var lblCustName : UILabel!
+    @IBOutlet weak var lblEqpName : UILabel!
+    @IBOutlet weak var lblMerk : UILabel!
+    @IBOutlet weak var lblModel : UILabel!
+    @IBOutlet weak var lblBrand : UILabel!
+    @IBOutlet weak var lblDisposisi : UILabel!
+    @IBOutlet weak var lblStatusStr : UILabel!
+
+    var cellDelegate: AplikasiCellDelegate?
+    var dataAplikasi: Aplikasi?
+    
+    func updateViews(aplData: Aplikasi) {
+        card.isInteractable = true
+        
+        let gesture = UITapGestureRecognizer(target: self, action:  #selector(self.onItemTap))
+        card.addGestureRecognizer(gesture)
+        
+        lblIdIzin.text = aplData.id_izin
+        lblAplId.text = String(aplData.apl_id)
+        lblResiNo.text = aplData.resi_no
+        lblCustName.text = aplData.cust_name
+        lblEqpName.text = aplData.eqp_name
+        lblMerk.text = aplData.merk
+        lblModel.text = aplData.model
+        lblBrand.text = aplData.brand
+        lblDisposisi.text = aplData.disposisi
+        lblStatusStr.text = aplData.StatusStr
+        
+        var resiDate = ""
+        if (aplData.resi_date == "null" || aplData.resi_date.isEmpty) {
+            resiDate = ""
+        } else {
+            let inputFormatter = DateFormatter()
+            inputFormatter.dateFormat = "yyyy-MM-dd"
+            let expDateString = String(aplData.resi_date.prefix(10))
+            
+            if let output = inputFormatter.date(from: expDateString) {
+                let outputFormatter = DateFormatter()
+                outputFormatter.dateFormat = "dd MMM yyyy"
+                resiDate = outputFormatter.string(from: output)
+            }
+        }
+        lblResiDate.text = resiDate
+        
+        self.dataAplikasi = aplData
+    }
+    
+    @objc func onItemTap(sender : UITapGestureRecognizer) {
+        cellDelegate?.openAplikasiDetail(data: dataAplikasi)
+    }
+
+}
+

+ 125 - 0
Sirani/View/Aplikasi/AplikasiPresenter.swift

@@ -0,0 +1,125 @@
+//
+//  AplikasiPresenter.swift
+//  Sirani
+//
+//  Created by Hendi on 21/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import Foundation
+import SwiftyJSON
+
+class AplikasiPresenter {
+    
+    private let siraniApiServices: SiraniApiServices
+    weak private var view: AplikasiViewDelegate?
+    
+    init(siraniApiServices: SiraniApiServices) {
+        self.siraniApiServices = siraniApiServices
+    }
+    
+    func attachView(view: AplikasiViewDelegate?) {
+        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 = [Aplikasi]()
+        siraniApiServices.postToGetAplikasi(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 aplikasiData = Aplikasi(
+                                    id: item["id"].stringValue,
+                                    apl_id: item["apl_id"].intValue,
+                                    cust_id: item["cust_id"].intValue,
+                                    cust_name: item["cust_name"].stringValue,
+                                    id_izin: item["id_izin"].stringValue,
+                                    id_proyek: item["id_proyek"].stringValue,
+                                    nib: item["nib"].stringValue,
+                                    resi_no: item["resi_no"].stringValue,
+                                    resi_date: item["resi_date"].stringValue,
+                                    eqp_name: item["eqp_name"].stringValue,
+                                    merk: item["merk"].stringValue,
+                                    model: item["model"].stringValue,
+                                    brand: item["brand"].stringValue,
+                                    disposisi: item["disposisi"].stringValue,
+                                    status: item["status"].stringValue,
+                                    StatusStr: item["StatusStr"].stringValue)
+                
+                                label.append(aplikasiData)
+                            }
+                            
+                            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: [Aplikasi]) {
+        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()
+        }
+    }
+}
+

+ 280 - 0
Sirani/View/Aplikasi/AplikasiVC.swift

@@ -0,0 +1,280 @@
+//
+//  AplikasiVC.swift
+//  Sirani
+//
+//  Created by Hendi on 21/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import UIKit
+
+class AplikasiVC: UIViewController {
+    
+    // Outlets
+    @IBOutlet weak var aplikasiTV: 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 = "AplikasiCellId"
+    
+    // Segue Id
+    private let unwindToHomeSegueId = "unwindAplikasiToHomeSegueId"
+    private let searchSegueId = "searchAplikasiSegueId"
+    
+    // Initialize
+    private var presenter: AplikasiPresenter!
+    private var dataList: [Aplikasi] =  [Aplikasi]()
+    private var documentInteractorController: UIDocumentInteractionController!
+    
+    // Request Params
+    private var limit: Int = 5
+    private var page: Int = 1
+    private var status: String = "apl_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()
+        
+        aplikasiTV.delegate = self
+        aplikasiTV.dataSource = self
+        
+        presenter = AplikasiPresenter(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 unwindToAplikasi(segue: UIStoryboardSegue) {}
+    
+}
+
+// View Delegate Protocols
+extension AplikasiVC: AplikasiViewDelegate {
+    
+    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: [Aplikasi]) {
+        
+        if page == 1 {
+            noDataLayout.hide()
+        }
+        
+        if dataList.count > 0 {
+            
+            for data in dataList {
+                self.dataList.append(data)
+                aplikasiTV.beginUpdates()
+                aplikasiTV.insertRows(
+                    at: [IndexPath(row: self.dataList.count-1, section: 0)],
+                    with: .none
+                )
+                aplikasiTV.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 AplikasiVC: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate, AplikasiCellDelegate {
+    func openAplikasiDetail(data: Aplikasi?) {
+        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 == aplikasiTV {
+            if let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? AplikasiCell {
+
+                let data = dataList[indexPath.row]
+                cell.cellDelegate = self
+//                cell.btnDwnldPeringatan.tag = indexPath.row
+//                cell.btnDownloadQrCode.tag = indexPath.row
+//                cell.btnDownloadAplikasi.tag = indexPath.row
+                cell.updateViews(aplData: data)
+
+                return cell
+            } else {
+                return AplikasiCell()
+            }
+        } else {
+            return AplikasiCell()
+        }
+    }
+
+    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 AplikasiVC: UIDocumentInteractionControllerDelegate {
+    func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
+        guard let navVC = self.navigationController else {
+            return self
+        }
+        return navVC
+    }
+}
+
+// Search Delegate Protocols
+extension AplikasiVC: SearchAplikasiDelegate {
+    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()
+        aplikasiTV.reloadData()
+
+        loadData()
+    }
+
+    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
+        if segue.identifier == searchSegueId {
+            let destinationVC: SearchAplikasiVC = segue.destination as! SearchAplikasiVC
+            destinationVC.delegate = self
+        }
+    }
+    
+}
+
+

+ 20 - 0
Sirani/View/Aplikasi/AplikasiViewDelegate.swift

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

+ 163 - 0
Sirani/View/Aplikasi/SearchAplikasiVC.swift

@@ -0,0 +1,163 @@
+//
+//  SearchAplikasiVC.swift
+//  Sirani
+//
+//  Created by Hendi on 21/06/23.
+//  Copyright © 2023 PT. Mitra Sinerji Teknoindo. All rights reserved.
+//
+
+import UIKit
+
+protocol SearchAplikasiDelegate {
+    func onSearch(search: String, status: String, jenisLabel: String)
+}
+
+class SearchAplikasiVC: 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: SearchAplikasiDelegate?
+    
+    // constants vallue data
+    private let jenisSertifikatOptionItems = [
+        OptionItem(label: "Semua", value: "apl_all"),
+        OptionItem(label: "Draft Customer", value: "apl_draft_cust"),
+        OptionItem(label: "Pemeriksaan Dokumen", value: "apl_pemeriksaan_dok"),
+        OptionItem(label: "Tunggu Disposisi", value: ""),
+        OptionItem(label: "Evaluasi Dokumen", value: "apl_eval_dok"),
+        OptionItem(label: "Rekomendasi Aplikasi", value: ""),
+        OptionItem(label: "Persetujuan Aplikasi", value: ""),
+        OptionItem(label: "Persetujuan Penerbitan", value: ""),
+        OptionItem(label: "Proses Sertifikasi", value: "apl_proses_sertifikasi"),
+        OptionItem(label: "Pembayaran SP2", value: ""),
+        OptionItem(label: "Sertifikat Dicetak", value: "apl_sertifikat_dicetak"),
+        OptionItem(label: "Permohonan Tidak Lengkap", value: "apl_permohonan_tidak_lengkap"),
+        OptionItem(label: "Draft Ditolak", value: "apl_draft_ditolak"),
+        OptionItem(label: "SP2 Kadaluarsa", value: "apl_sp2_kadaluarsa"),
+        OptionItem(label: "Batal", value: "apl_batal")
+    ]
+    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 unwindToAplikasi(_ sender: Any) {
+        if optionsShown {
+            hideOptions()
+        } else {
+            performSegue(withIdentifier: "unwindSearchToAplikasiSegueId", 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
+        }
+        
+        self.delegate?.onSearch(search: search, status: status, jenisLabel: jenisLabel)
+        performSegue(withIdentifier: "unwindSearchToAplikasiSegueId", sender: self)
+    }
+    
+    private func showOptions() {
+        optionsLayout.show()
+        optionsShown = true
+    }
+    
+    @objc private func hideOptions() {
+        optionsLayout.hide()
+        optionsShown = false
+    }
+
+}
+
+extension SearchAplikasiVC: 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: "searchAplikasiCell", 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()
+    }
+}
+

+ 0 - 4
Sirani/View/Label/SearchLabelVC.swift

@@ -104,10 +104,6 @@ class SearchLabelVC: UIViewController {
         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)

+ 0 - 4
Sirani/View/Sertifikat/Search/SearchSertifikatNewVC.swift

@@ -105,10 +105,6 @@ class SearchSertifikatNewVC: UIViewController {
         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: "unwindSearchToSertifikatSegueId", sender: self)