SertifikatNewCell.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. //
  2. // SertifikatNewCell.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. import MaterialComponents.MaterialCards
  10. protocol SertifikatNewCellDelegate: NSObjectProtocol {
  11. func openSertifikatDetail(data: CertificateData?)
  12. }
  13. class SertifikatNewCell: UITableViewCell {
  14. @IBOutlet weak var card: MDCCard!
  15. @IBOutlet weak var lblNomor: UILabel!
  16. @IBOutlet weak var lblPemohon: UILabel!
  17. @IBOutlet weak var lblPerangkat: UILabel!
  18. @IBOutlet weak var lblMerk: UILabel!
  19. @IBOutlet weak var lblModel: UILabel!
  20. @IBOutlet weak var lblBrand: UILabel!
  21. @IBOutlet weak var lblBerlakuDate: UILabel!
  22. @IBOutlet weak var lblBerakhirDate: UILabel!
  23. @IBOutlet weak var lblStatus: UILabel!
  24. @IBOutlet weak var lblNegara: UILabel!
  25. var cellDelegate: SertifikatNewCellDelegate?
  26. var dataSertifikat: CertificateData?
  27. // Modern UI Elements
  28. private let modernContainer = UIView()
  29. private let modernBadgeView = UIView()
  30. private let modernBadgeIcon = UIImageView()
  31. private let modernNomorLabel = UILabel()
  32. private let modernCategoryLabel = UILabel()
  33. private let modernStatusPill = UIView()
  34. private let modernStatusLabel = UILabel()
  35. private let modernPerangkatLabel = UILabel()
  36. private let modernCompanyIcon = UIImageView()
  37. private let modernPemohonLabel = UILabel()
  38. private let modernSpecStack = UIStackView()
  39. private let modernMerkCard = UIView()
  40. private let modernMerkTitle = UILabel()
  41. private let modernMerkVal = UILabel()
  42. private let modernNegaraCard = UIView()
  43. private let modernNegaraTitle = UILabel()
  44. private let modernNegaraVal = UILabel()
  45. private let modernValidityCard = UIView()
  46. private let modernCalendarIcon = UIImageView()
  47. private let modernValidityLabel = UILabel()
  48. private let modernDetailLabel = UILabel()
  49. private let modernChevronIcon = UIImageView()
  50. private var isModernLayoutBuilt = false
  51. override func awakeFromNib() {
  52. super.awakeFromNib()
  53. self.selectionStyle = .none
  54. self.backgroundColor = .clear
  55. setupCardVisuals()
  56. setupModernLayout()
  57. }
  58. private func setupCardVisuals() {
  59. guard let card = card else { return }
  60. card.cornerRadius = 16
  61. card.setShadowElevation(ShadowElevation(rawValue: 2), for: .normal)
  62. card.setShadowColor(UIColor.black.withAlphaComponent(0.06), for: .normal)
  63. card.layer.borderColor = UIColor(red: 228/255, green: 235/255, blue: 245/255, alpha: 1.0).cgColor
  64. card.layer.borderWidth = 1.0
  65. card.backgroundColor = .white
  66. }
  67. private func setupModernLayout() {
  68. guard let card = card, !isModernLayoutBuilt else { return }
  69. isModernLayoutBuilt = true
  70. // Hide legacy storyboard labels
  71. for subview in card.subviews {
  72. subview.isHidden = true
  73. }
  74. modernContainer.translatesAutoresizingMaskIntoConstraints = false
  75. modernContainer.backgroundColor = .clear
  76. card.addSubview(modernContainer)
  77. NSLayoutConstraint.activate([
  78. modernContainer.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
  79. modernContainer.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -14),
  80. modernContainer.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
  81. modernContainer.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -14)
  82. ])
  83. // 1. Header Bar
  84. let headerStack = UIStackView()
  85. headerStack.translatesAutoresizingMaskIntoConstraints = false
  86. headerStack.axis = .horizontal
  87. headerStack.alignment = .center
  88. headerStack.spacing = 10
  89. modernContainer.addSubview(headerStack)
  90. // Badge Icon Box
  91. modernBadgeView.translatesAutoresizingMaskIntoConstraints = false
  92. modernBadgeView.backgroundColor = UIColor(red: 235/255, green: 242/255, blue: 252/255, alpha: 1.0)
  93. modernBadgeView.layer.cornerRadius = 8
  94. modernBadgeView.layer.masksToBounds = true
  95. modernBadgeIcon.translatesAutoresizingMaskIntoConstraints = false
  96. if #available(iOS 13.0, *) {
  97. modernBadgeIcon.image = UIImage(systemName: "checkmark.seal.fill")
  98. }
  99. modernBadgeIcon.tintColor = UIColor.siraniPrimaryBlue
  100. modernBadgeIcon.contentMode = .scaleAspectFit
  101. modernBadgeView.addSubview(modernBadgeIcon)
  102. NSLayoutConstraint.activate([
  103. modernBadgeView.widthAnchor.constraint(equalToConstant: 32),
  104. modernBadgeView.heightAnchor.constraint(equalToConstant: 32),
  105. modernBadgeIcon.centerXAnchor.constraint(equalTo: modernBadgeView.centerXAnchor),
  106. modernBadgeIcon.centerYAnchor.constraint(equalTo: modernBadgeView.centerYAnchor),
  107. modernBadgeIcon.widthAnchor.constraint(equalToConstant: 18),
  108. modernBadgeIcon.heightAnchor.constraint(equalToConstant: 18)
  109. ])
  110. headerStack.addArrangedSubview(modernBadgeView)
  111. // Title & Number Stack
  112. let numberStack = UIStackView()
  113. numberStack.axis = .vertical
  114. numberStack.spacing = 1
  115. modernCategoryLabel.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  116. modernCategoryLabel.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  117. modernCategoryLabel.text = "NOMOR SERTIFIKAT"
  118. numberStack.addArrangedSubview(modernCategoryLabel)
  119. modernNomorLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
  120. modernNomorLabel.textColor = UIColor.siraniPrimaryBlue
  121. modernNomorLabel.numberOfLines = 1
  122. modernNomorLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
  123. modernNomorLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
  124. numberStack.addArrangedSubview(modernNomorLabel)
  125. headerStack.addArrangedSubview(numberStack)
  126. // Status Pill Badge
  127. modernStatusPill.translatesAutoresizingMaskIntoConstraints = false
  128. modernStatusPill.layer.cornerRadius = 6
  129. modernStatusPill.layer.masksToBounds = true
  130. modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
  131. modernStatusLabel.translatesAutoresizingMaskIntoConstraints = false
  132. modernStatusLabel.font = UIFont.systemFont(ofSize: 10, weight: .bold)
  133. modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
  134. modernStatusLabel.textAlignment = .center
  135. modernStatusPill.addSubview(modernStatusLabel)
  136. NSLayoutConstraint.activate([
  137. modernStatusLabel.topAnchor.constraint(equalTo: modernStatusPill.topAnchor, constant: 4),
  138. modernStatusLabel.bottomAnchor.constraint(equalTo: modernStatusPill.bottomAnchor, constant: -4),
  139. modernStatusLabel.leadingAnchor.constraint(equalTo: modernStatusPill.leadingAnchor, constant: 8),
  140. modernStatusLabel.trailingAnchor.constraint(equalTo: modernStatusPill.trailingAnchor, constant: -8)
  141. ])
  142. headerStack.addArrangedSubview(modernStatusPill)
  143. // 2. Divider Line
  144. let divider = UIView()
  145. divider.translatesAutoresizingMaskIntoConstraints = false
  146. divider.backgroundColor = UIColor(red: 240/255, green: 244/255, blue: 250/255, alpha: 1.0)
  147. modernContainer.addSubview(divider)
  148. // 3. Equipment Name (Headline)
  149. modernPerangkatLabel.translatesAutoresizingMaskIntoConstraints = false
  150. modernPerangkatLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold)
  151. modernPerangkatLabel.textColor = UIColor(red: 26/255, green: 29/255, blue: 38/255, alpha: 1.0)
  152. modernPerangkatLabel.numberOfLines = 2
  153. modernContainer.addSubview(modernPerangkatLabel)
  154. // 4. Company Name Row
  155. let companyStack = UIStackView()
  156. companyStack.translatesAutoresizingMaskIntoConstraints = false
  157. companyStack.axis = .horizontal
  158. companyStack.spacing = 6
  159. companyStack.alignment = .center
  160. if #available(iOS 13.0, *) {
  161. modernCompanyIcon.image = UIImage(systemName: "building.2.fill")
  162. }
  163. modernCompanyIcon.tintColor = UIColor(red: 120/255, green: 135/255, blue: 155/255, alpha: 1.0)
  164. modernCompanyIcon.contentMode = .scaleAspectFit
  165. modernCompanyIcon.translatesAutoresizingMaskIntoConstraints = false
  166. NSLayoutConstraint.activate([
  167. modernCompanyIcon.widthAnchor.constraint(equalToConstant: 14),
  168. modernCompanyIcon.heightAnchor.constraint(equalToConstant: 14)
  169. ])
  170. companyStack.addArrangedSubview(modernCompanyIcon)
  171. modernPemohonLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
  172. modernPemohonLabel.textColor = UIColor(red: 90/255, green: 105/255, blue: 125/255, alpha: 1.0)
  173. modernPemohonLabel.numberOfLines = 1
  174. companyStack.addArrangedSubview(modernPemohonLabel)
  175. modernContainer.addSubview(companyStack)
  176. // 5. Spec Mini Cards (Merk & Model, Negara)
  177. modernSpecStack.translatesAutoresizingMaskIntoConstraints = false
  178. modernSpecStack.axis = .horizontal
  179. modernSpecStack.distribution = .fillEqually
  180. modernSpecStack.spacing = 8
  181. modernContainer.addSubview(modernSpecStack)
  182. // Merk Card
  183. modernMerkCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  184. modernMerkCard.layer.cornerRadius = 8
  185. modernMerkCard.layer.borderWidth = 1
  186. modernMerkCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  187. let merkInnerStack = UIStackView()
  188. merkInnerStack.translatesAutoresizingMaskIntoConstraints = false
  189. merkInnerStack.axis = .vertical
  190. merkInnerStack.spacing = 2
  191. modernMerkCard.addSubview(merkInnerStack)
  192. modernMerkTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  193. modernMerkTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  194. modernMerkTitle.text = "MERK & MODEL"
  195. merkInnerStack.addArrangedSubview(modernMerkTitle)
  196. modernMerkVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  197. modernMerkVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  198. modernMerkVal.numberOfLines = 1
  199. merkInnerStack.addArrangedSubview(modernMerkVal)
  200. NSLayoutConstraint.activate([
  201. merkInnerStack.topAnchor.constraint(equalTo: modernMerkCard.topAnchor, constant: 6),
  202. merkInnerStack.bottomAnchor.constraint(equalTo: modernMerkCard.bottomAnchor, constant: -6),
  203. merkInnerStack.leadingAnchor.constraint(equalTo: modernMerkCard.leadingAnchor, constant: 8),
  204. merkInnerStack.trailingAnchor.constraint(equalTo: modernMerkCard.trailingAnchor, constant: -8)
  205. ])
  206. modernSpecStack.addArrangedSubview(modernMerkCard)
  207. // Negara Card
  208. modernNegaraCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  209. modernNegaraCard.layer.cornerRadius = 8
  210. modernNegaraCard.layer.borderWidth = 1
  211. modernNegaraCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  212. let negaraInnerStack = UIStackView()
  213. negaraInnerStack.translatesAutoresizingMaskIntoConstraints = false
  214. negaraInnerStack.axis = .vertical
  215. negaraInnerStack.spacing = 2
  216. modernNegaraCard.addSubview(negaraInnerStack)
  217. modernNegaraTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  218. modernNegaraTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  219. modernNegaraTitle.text = "NEGARA ASAL"
  220. negaraInnerStack.addArrangedSubview(modernNegaraTitle)
  221. modernNegaraVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  222. modernNegaraVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  223. modernNegaraVal.numberOfLines = 1
  224. negaraInnerStack.addArrangedSubview(modernNegaraVal)
  225. NSLayoutConstraint.activate([
  226. negaraInnerStack.topAnchor.constraint(equalTo: modernNegaraCard.topAnchor, constant: 6),
  227. negaraInnerStack.bottomAnchor.constraint(equalTo: modernNegaraCard.bottomAnchor, constant: -6),
  228. negaraInnerStack.leadingAnchor.constraint(equalTo: modernNegaraCard.leadingAnchor, constant: 8),
  229. negaraInnerStack.trailingAnchor.constraint(equalTo: modernNegaraCard.trailingAnchor, constant: -8)
  230. ])
  231. modernSpecStack.addArrangedSubview(modernNegaraCard)
  232. // 6. Validity Bar (Bottom Footer)
  233. modernValidityCard.translatesAutoresizingMaskIntoConstraints = false
  234. modernValidityCard.backgroundColor = UIColor(red: 244/255, green: 248/255, blue: 254/255, alpha: 1.0)
  235. modernValidityCard.layer.cornerRadius = 8
  236. modernContainer.addSubview(modernValidityCard)
  237. let validityStack = UIStackView()
  238. validityStack.translatesAutoresizingMaskIntoConstraints = false
  239. validityStack.axis = .horizontal
  240. validityStack.alignment = .center
  241. validityStack.spacing = 6
  242. modernValidityCard.addSubview(validityStack)
  243. if #available(iOS 13.0, *) {
  244. modernCalendarIcon.image = UIImage(systemName: "calendar")
  245. }
  246. modernCalendarIcon.tintColor = UIColor.siraniPrimaryBlue
  247. modernCalendarIcon.contentMode = .scaleAspectFit
  248. modernCalendarIcon.translatesAutoresizingMaskIntoConstraints = false
  249. NSLayoutConstraint.activate([
  250. modernCalendarIcon.widthAnchor.constraint(equalToConstant: 13),
  251. modernCalendarIcon.heightAnchor.constraint(equalToConstant: 13)
  252. ])
  253. validityStack.addArrangedSubview(modernCalendarIcon)
  254. modernValidityLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
  255. modernValidityLabel.textColor = UIColor(red: 50/255, green: 70/255, blue: 95/255, alpha: 1.0)
  256. modernValidityLabel.numberOfLines = 1
  257. validityStack.addArrangedSubview(modernValidityLabel)
  258. let spacer = UIView()
  259. spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
  260. validityStack.addArrangedSubview(spacer)
  261. modernDetailLabel.font = UIFont.systemFont(ofSize: 11, weight: .bold)
  262. modernDetailLabel.textColor = UIColor.siraniPrimaryBlue
  263. modernDetailLabel.text = "Detail"
  264. validityStack.addArrangedSubview(modernDetailLabel)
  265. if #available(iOS 13.0, *) {
  266. modernChevronIcon.image = UIImage(systemName: "chevron.right")
  267. }
  268. modernChevronIcon.tintColor = UIColor.siraniPrimaryBlue
  269. modernChevronIcon.contentMode = .scaleAspectFit
  270. modernChevronIcon.translatesAutoresizingMaskIntoConstraints = false
  271. NSLayoutConstraint.activate([
  272. modernChevronIcon.widthAnchor.constraint(equalToConstant: 10),
  273. modernChevronIcon.heightAnchor.constraint(equalToConstant: 10)
  274. ])
  275. validityStack.addArrangedSubview(modernChevronIcon)
  276. NSLayoutConstraint.activate([
  277. validityStack.topAnchor.constraint(equalTo: modernValidityCard.topAnchor, constant: 6),
  278. validityStack.bottomAnchor.constraint(equalTo: modernValidityCard.bottomAnchor, constant: -6),
  279. validityStack.leadingAnchor.constraint(equalTo: modernValidityCard.leadingAnchor, constant: 10),
  280. validityStack.trailingAnchor.constraint(equalTo: modernValidityCard.trailingAnchor, constant: -10)
  281. ])
  282. // Main Container Constraints
  283. NSLayoutConstraint.activate([
  284. headerStack.topAnchor.constraint(equalTo: modernContainer.topAnchor),
  285. headerStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  286. headerStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  287. divider.topAnchor.constraint(equalTo: headerStack.bottomAnchor, constant: 10),
  288. divider.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  289. divider.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  290. divider.heightAnchor.constraint(equalToConstant: 1),
  291. modernPerangkatLabel.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 10),
  292. modernPerangkatLabel.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  293. modernPerangkatLabel.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  294. companyStack.topAnchor.constraint(equalTo: modernPerangkatLabel.bottomAnchor, constant: 6),
  295. companyStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  296. companyStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  297. modernSpecStack.topAnchor.constraint(equalTo: companyStack.bottomAnchor, constant: 10),
  298. modernSpecStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  299. modernSpecStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  300. modernValidityCard.topAnchor.constraint(equalTo: modernSpecStack.bottomAnchor, constant: 10),
  301. modernValidityCard.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  302. modernValidityCard.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  303. modernValidityCard.bottomAnchor.constraint(equalTo: modernContainer.bottomAnchor)
  304. ])
  305. }
  306. func updateViews(certificateData: CertificateData) {
  307. setupCardVisuals()
  308. setupModernLayout()
  309. card.isInteractable = true
  310. let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onItemTap))
  311. card.addGestureRecognizer(gesture)
  312. // 1. Nomor
  313. modernNomorLabel.text = certificateData.nomor.isEmpty ? "-" : certificateData.nomor
  314. // 2. Status Pill
  315. let statusStr = certificateData.statusBerlaku.trimmingCharacters(in: .whitespacesAndNewlines)
  316. let displayStatus = statusStr.isEmpty ? "TERBIT" : statusStr.uppercased()
  317. modernStatusLabel.text = displayStatus
  318. if displayStatus.contains("CABUT") || displayStatus.contains("TIDAK") {
  319. modernStatusPill.backgroundColor = UIColor(red: 254/255, green: 242/255, blue: 242/255, alpha: 1.0)
  320. modernStatusLabel.textColor = UIColor(red: 220/255, green: 38/255, blue: 38/255, alpha: 1.0)
  321. } else if displayStatus.contains("REVISI") || displayStatus.contains("PROSES") {
  322. modernStatusPill.backgroundColor = UIColor(red: 254/255, green: 249/255, blue: 235/255, alpha: 1.0)
  323. modernStatusLabel.textColor = UIColor(red: 180/255, green: 83/255, blue: 9/255, alpha: 1.0)
  324. } else {
  325. modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
  326. modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
  327. }
  328. // 3. Perangkat
  329. modernPerangkatLabel.text = certificateData.eqp_name.isEmpty ? "-" : certificateData.eqp_name
  330. // 4. Pemohon
  331. modernPemohonLabel.text = certificateData.cust_name.isEmpty ? "-" : certificateData.cust_name
  332. // 5. Merk & Model
  333. var mm = certificateData.merk
  334. if !certificateData.model.isEmpty && certificateData.model != "null" {
  335. mm = mm.isEmpty ? certificateData.model : "\(mm) • \(certificateData.model)"
  336. }
  337. modernMerkVal.text = mm.isEmpty ? "-" : mm
  338. // 6. Negara
  339. let negara = certificateData.negara.trimmingCharacters(in: .whitespacesAndNewlines)
  340. modernNegaraVal.text = (negara.isEmpty || negara == "null") ? "-" : negara
  341. // 7. Validity
  342. let tglBerlaku = CommonHelper.formatDate(from: certificateData.lic_date)
  343. let tglBerakhir = CommonHelper.formatDate(from: certificateData.exp_date)
  344. if tglBerlaku != "-" && tglBerakhir != "-" {
  345. modernValidityLabel.text = "Berlaku: \(tglBerlaku) s/d \(tglBerakhir)"
  346. } else if tglBerlaku != "-" {
  347. modernValidityLabel.text = "Berlaku: \(tglBerlaku)"
  348. } else {
  349. modernValidityLabel.text = "Masa Berlaku: -"
  350. }
  351. self.dataSertifikat = certificateData
  352. }
  353. @objc func onItemTap(sender: UITapGestureRecognizer) {
  354. cellDelegate?.openSertifikatDetail(data: dataSertifikat)
  355. }
  356. }