SearchSertifikatNewVC.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. //
  2. // SearchSertifikatNewVC.swift
  3. // Sirani
  4. //
  5. // Created by Hendi on 19/06/23.
  6. // Copyright © 2026 PT. Mitra Sinerji Teknoindo. All rights reserved.
  7. //
  8. import UIKit
  9. protocol SearchSertifikatNewDelegate {
  10. func onSearch(search: String, status: String, jenisLabel: String)
  11. }
  12. class SearchSertifikatNewVC: UIViewController {
  13. // Outlets / Views
  14. @IBOutlet weak var inputSearch: RoundTextField!
  15. @IBOutlet weak var optionsLayoutTV: UITableView!
  16. @IBOutlet weak var optionsLayout: UIView!
  17. @IBOutlet weak var optionsBg: UIView!
  18. @IBOutlet weak var btnStatusOptions: UIButton!
  19. // Initialize
  20. var delegate: SearchSertifikatNewDelegate?
  21. var backtoSertifikatPrivate = "unwindSearchToSertifikatSegueId"
  22. var backtoSertifikatPublic = "unwindSearchToSertifikatPublicSegueId"
  23. var isSertifikat = 1
  24. var currentSearch: String = ""
  25. var currentStatus: String = ""
  26. // Constants value data
  27. private let statusSertifikatPrivateOptionItems = [
  28. OptionItem(label: "Semua Sertifikat", value: ""),
  29. OptionItem(label: "Sertifikat Berlaku", value: "sertifkat_berlaku"),
  30. OptionItem(label: "Sertifikat Tidak Berlaku", value: "sertifikat_tidak_berlaku"),
  31. OptionItem(label: "Sertifikat Revisi", value: "sertifikat_revisi")
  32. ]
  33. private let statusSertifikatPublicOptionItems = [
  34. OptionItem(label: "Sertifikat Terbit", value: "sertifikat_terbit"),
  35. OptionItem(label: "Sertifikat Dicabut", value: "sertifikat_dicabut")
  36. ]
  37. private let OPTION_JENIS_SERTIFIKAT_ID = 1
  38. // Others
  39. private var selectedOptions = 0
  40. private var selectedOptionItems = [OptionItem]()
  41. private var optionsShown = false
  42. // Result Values
  43. private var search = ""
  44. private var status = ""
  45. private var jenisLabel = "Semua"
  46. // Modern UI Elements
  47. private let chipContainerStack = UIStackView()
  48. private var chipButtons = [UIButton]()
  49. private let modernCard = UIView()
  50. private let modernSearchIcon = UIImageView()
  51. private let btnApplyModern = UIButton(type: .system)
  52. private let btnResetModern = UIButton(type: .system)
  53. private var isModernFilterBuilt = false
  54. // View Lifecycles
  55. override var preferredStatusBarStyle: UIStatusBarStyle {
  56. return .lightContent
  57. }
  58. override func viewWillAppear(_ animated: Bool) {
  59. super.viewWillAppear(animated)
  60. setupInitialState()
  61. updateToolbarTitle()
  62. buildModernFilterUI()
  63. updateChipSelection()
  64. }
  65. override func viewDidLoad() {
  66. super.viewDidLoad()
  67. optionsLayoutTV.delegate = self
  68. optionsLayoutTV.dataSource = self
  69. optionsLayoutTV.tableFooterView = UIView()
  70. let tapDismiss = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
  71. tapDismiss.cancelsTouchesInView = false
  72. view.addGestureRecognizer(tapDismiss)
  73. }
  74. @objc private func dismissKeyboard() {
  75. view.endEditing(true)
  76. }
  77. private func setupInitialState() {
  78. if !currentSearch.isEmpty {
  79. search = currentSearch
  80. inputSearch.text = currentSearch
  81. } else {
  82. search = inputSearch.text ?? ""
  83. }
  84. if isSertifikat == 2 {
  85. // Public certificates
  86. if currentStatus == "sertifikat_dicabut" {
  87. status = "sertifikat_dicabut"
  88. jenisLabel = "Sertifikat Dicabut"
  89. } else {
  90. status = "sertifikat_terbit"
  91. jenisLabel = "Sertifikat Terbit"
  92. }
  93. } else {
  94. // Private certificates
  95. if !currentStatus.isEmpty {
  96. status = currentStatus
  97. if let matched = statusSertifikatPrivateOptionItems.first(where: { ($0.value as? String) == currentStatus }) {
  98. jenisLabel = matched.label
  99. } else {
  100. jenisLabel = "Sertifikat Berlaku"
  101. }
  102. } else {
  103. status = ""
  104. jenisLabel = "Semua Sertifikat"
  105. }
  106. }
  107. btnStatusOptions.setTitle(jenisLabel, for: .normal)
  108. hideOptions()
  109. }
  110. private func updateToolbarTitle() {
  111. // Find Toolbar Title in subviews and set modern title
  112. for subview in view.subviews {
  113. for inner in subview.subviews {
  114. if let lbl = inner as? UILabel, lbl.text?.lowercased().contains("search") == true {
  115. lbl.text = "Filter & Pencarian"
  116. lbl.font = UIFont.systemFont(ofSize: 18, weight: .bold)
  117. }
  118. }
  119. }
  120. }
  121. private func buildModernFilterUI() {
  122. guard !isModernFilterBuilt else { return }
  123. isModernFilterBuilt = true
  124. // Style search input
  125. inputSearch.layer.cornerRadius = 12
  126. inputSearch.layer.borderWidth = 1
  127. inputSearch.layer.borderColor = UIColor(red: 226/255, green: 232/255, blue: 240/255, alpha: 1.0).cgColor
  128. inputSearch.backgroundColor = UIColor(red: 250/255, green: 251/255, blue: 253/255, alpha: 1.0)
  129. inputSearch.placeholder = "Cari nomor sertifikat, pemohon, model..."
  130. inputSearch.clearButtonMode = .whileEditing
  131. // Hide legacy dropdown button & arrow and build modern interactive chips
  132. btnStatusOptions.isHidden = true
  133. if let arrowImg = btnStatusOptions.superview?.subviews.first(where: { ($0 as? UIImageView)?.image == UIImage(named: "dropdown") }) {
  134. arrowImg.isHidden = true
  135. }
  136. // Find existing CARI and RESET buttons and hide them so we can use our modern action buttons
  137. for subview in view.subviews {
  138. findAndHideLegacyActionButtons(in: subview)
  139. }
  140. guard let container = btnStatusOptions.superview else { return }
  141. // Build Chip Stack
  142. chipContainerStack.translatesAutoresizingMaskIntoConstraints = false
  143. chipContainerStack.axis = .horizontal
  144. chipContainerStack.distribution = .fillEqually
  145. chipContainerStack.spacing = 8
  146. chipContainerStack.alignment = .fill
  147. container.addSubview(chipContainerStack)
  148. let activeOptions = (isSertifikat == 2) ? statusSertifikatPublicOptionItems : statusSertifikatPrivateOptionItems
  149. chipButtons.removeAll()
  150. for item in activeOptions {
  151. let btn = UIButton(type: .system)
  152. btn.translatesAutoresizingMaskIntoConstraints = false
  153. btn.setTitle(item.label, for: .normal)
  154. btn.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
  155. btn.layer.cornerRadius = 10
  156. btn.layer.borderWidth = 1
  157. btn.tag = activeOptions.firstIndex(where: { $0.label == item.label }) ?? 0
  158. btn.addTarget(self, action: #selector(onChipTapped(_:)), for: .touchUpInside)
  159. chipButtons.append(btn)
  160. chipContainerStack.addArrangedSubview(btn)
  161. }
  162. NSLayoutConstraint.activate([
  163. chipContainerStack.topAnchor.constraint(equalTo: btnStatusOptions.topAnchor),
  164. chipContainerStack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
  165. chipContainerStack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16),
  166. chipContainerStack.heightAnchor.constraint(equalToConstant: 44)
  167. ])
  168. // Modern Action Buttons: Terapkan Filter & Reset
  169. let buttonStack = UIStackView()
  170. buttonStack.translatesAutoresizingMaskIntoConstraints = false
  171. buttonStack.axis = .vertical
  172. buttonStack.spacing = 10
  173. container.addSubview(buttonStack)
  174. // 1. Primary Button
  175. btnApplyModern.translatesAutoresizingMaskIntoConstraints = false
  176. btnApplyModern.setTitle("🔍 TERAPKAN FILTER", for: .normal)
  177. btnApplyModern.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .bold)
  178. btnApplyModern.setTitleColor(.white, for: .normal)
  179. btnApplyModern.backgroundColor = UIColor.siraniPrimaryBlue
  180. btnApplyModern.layer.cornerRadius = 12
  181. btnApplyModern.layer.masksToBounds = false
  182. btnApplyModern.layer.shadowColor = UIColor.siraniPrimaryBlue.cgColor
  183. btnApplyModern.layer.shadowOffset = CGSize(width: 0, height: 4)
  184. btnApplyModern.layer.shadowRadius = 8
  185. btnApplyModern.layer.shadowOpacity = 0.25
  186. btnApplyModern.addTarget(self, action: #selector(onSearchTapped(_:)), for: .touchUpInside)
  187. NSLayoutConstraint.activate([
  188. btnApplyModern.heightAnchor.constraint(equalToConstant: 48)
  189. ])
  190. buttonStack.addArrangedSubview(btnApplyModern)
  191. // 2. Secondary Reset Button
  192. btnResetModern.translatesAutoresizingMaskIntoConstraints = false
  193. btnResetModern.setTitle("↺ Reset Filter", for: .normal)
  194. btnResetModern.titleLabel?.font = UIFont.systemFont(ofSize: 13, weight: .semibold)
  195. btnResetModern.setTitleColor(UIColor(red: 100/255, green: 116/255, blue: 139/255, alpha: 1.0), for: .normal)
  196. btnResetModern.backgroundColor = UIColor(red: 241/255, green: 245/255, blue: 249/255, alpha: 1.0)
  197. btnResetModern.layer.cornerRadius = 12
  198. btnResetModern.layer.borderWidth = 1
  199. btnResetModern.layer.borderColor = UIColor(red: 226/255, green: 232/255, blue: 240/255, alpha: 1.0).cgColor
  200. btnResetModern.addTarget(self, action: #selector(onResetTapped(_:)), for: .touchUpInside)
  201. NSLayoutConstraint.activate([
  202. btnResetModern.heightAnchor.constraint(equalToConstant: 44)
  203. ])
  204. buttonStack.addArrangedSubview(btnResetModern)
  205. NSLayoutConstraint.activate([
  206. buttonStack.topAnchor.constraint(equalTo: chipContainerStack.bottomAnchor, constant: 28),
  207. buttonStack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
  208. buttonStack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -16)
  209. ])
  210. }
  211. private func findAndHideLegacyActionButtons(in view: UIView) {
  212. for sub in view.subviews {
  213. if let btn = sub as? UIButton {
  214. let title = btn.title(for: .normal)?.uppercased() ?? ""
  215. if (title == "CARI" || title == "RESET") && btn != btnApplyModern && btn != btnResetModern {
  216. btn.isHidden = true
  217. }
  218. }
  219. findAndHideLegacyActionButtons(in: sub)
  220. }
  221. }
  222. @objc private func onChipTapped(_ sender: UIButton) {
  223. let activeOptions = (isSertifikat == 2) ? statusSertifikatPublicOptionItems : statusSertifikatPrivateOptionItems
  224. guard sender.tag < activeOptions.count else { return }
  225. let item = activeOptions[sender.tag]
  226. status = item.value as! String
  227. jenisLabel = item.label
  228. btnStatusOptions.setTitle(jenisLabel, for: .normal)
  229. updateChipSelection()
  230. }
  231. private func updateChipSelection() {
  232. let activeOptions = (isSertifikat == 2) ? statusSertifikatPublicOptionItems : statusSertifikatPrivateOptionItems
  233. for btn in chipButtons {
  234. guard btn.tag < activeOptions.count else { continue }
  235. let item = activeOptions[btn.tag]
  236. let isSelected = (item.value as! String) == status
  237. if isSelected {
  238. btn.backgroundColor = UIColor.siraniPrimaryBlue
  239. btn.setTitleColor(.white, for: .normal)
  240. btn.layer.borderColor = UIColor.siraniPrimaryBlue.cgColor
  241. } else {
  242. btn.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  243. btn.setTitleColor(UIColor(red: 71/255, green: 85/255, blue: 105/255, alpha: 1.0), for: .normal)
  244. btn.layer.borderColor = UIColor(red: 226/255, green: 232/255, blue: 240/255, alpha: 1.0).cgColor
  245. }
  246. }
  247. }
  248. @IBAction func onInputSearchKeyTap(_ sender: Any) {
  249. view.endEditing(true)
  250. onSearchTapped(sender)
  251. }
  252. @IBAction func unwindToSertifikatNew(_ sender: Any) {
  253. if optionsShown {
  254. hideOptions()
  255. } else {
  256. if isSertifikat == 2 {
  257. performSegue(withIdentifier: backtoSertifikatPublic, sender: self)
  258. } else {
  259. performSegue(withIdentifier: backtoSertifikatPrivate, sender: self)
  260. }
  261. }
  262. }
  263. @IBAction func onJenisSertifikatOptionsTapped(_ sender: Any) {
  264. selectedOptions = OPTION_JENIS_SERTIFIKAT_ID
  265. selectedOptionItems.removeAll()
  266. if isSertifikat == 2 {
  267. selectedOptionItems += statusSertifikatPublicOptionItems
  268. } else {
  269. selectedOptionItems += statusSertifikatPrivateOptionItems
  270. }
  271. optionsLayoutTV.reloadData()
  272. showOptions()
  273. }
  274. @IBAction func onResetTapped(_ sender: Any) {
  275. search = ""
  276. inputSearch.text = ""
  277. if isSertifikat == 2 {
  278. status = "sertifikat_terbit"
  279. jenisLabel = "Sertifikat Terbit"
  280. } else {
  281. status = ""
  282. jenisLabel = "Semua Sertifikat"
  283. }
  284. btnStatusOptions.setTitle(jenisLabel, for: .normal)
  285. updateChipSelection()
  286. }
  287. @IBAction func onSearchTapped(_ sender: Any) {
  288. view.endEditing(true)
  289. let searchText = inputSearch.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
  290. search = searchText
  291. self.delegate?.onSearch(search: search, status: status, jenisLabel: jenisLabel)
  292. if isSertifikat == 2 {
  293. performSegue(withIdentifier: backtoSertifikatPublic, sender: self)
  294. } else {
  295. performSegue(withIdentifier: backtoSertifikatPrivate, sender: self)
  296. }
  297. }
  298. private func showOptions() {
  299. optionsLayout.show()
  300. optionsShown = true
  301. }
  302. @objc private func hideOptions() {
  303. optionsLayout.hide()
  304. optionsShown = false
  305. }
  306. }
  307. extension SearchSertifikatNewVC: UITableViewDelegate, UITableViewDataSource {
  308. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  309. return selectedOptionItems.count
  310. }
  311. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  312. if let cell = tableView.dequeueReusableCell(withIdentifier: "searchSertifikatNewCell", for: indexPath) as? OptionsCell {
  313. let optionItem = selectedOptionItems[indexPath.row]
  314. cell.updateViews(item: optionItem)
  315. return cell
  316. } else {
  317. return OptionsCell()
  318. }
  319. }
  320. func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  321. if selectedOptions == OPTION_JENIS_SERTIFIKAT_ID {
  322. status = selectedOptionItems[indexPath.row].value as! String
  323. jenisLabel = selectedOptionItems[indexPath.row].label
  324. btnStatusOptions.setTitle(jenisLabel, for: .normal)
  325. updateChipSelection()
  326. }
  327. hideOptions()
  328. }
  329. }