LabelCell.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. //
  2. // LabelCell.swift
  3. // Sirani
  4. //
  5. // Created by Hendi on 20/06/23.
  6. // Copyright © 2026 PT. Mitra Sinerji Teknoindo. All rights reserved.
  7. //
  8. import UIKit
  9. import MaterialComponents.MaterialCards
  10. protocol LabelCellDelegate: NSObjectProtocol {
  11. func openLabelDetail(data: Label?)
  12. }
  13. class LabelCell: 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: LabelCellDelegate?
  26. var dataLabel: Label?
  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. for subview in card.subviews {
  71. subview.isHidden = true
  72. }
  73. modernContainer.translatesAutoresizingMaskIntoConstraints = false
  74. modernContainer.backgroundColor = .clear
  75. card.addSubview(modernContainer)
  76. NSLayoutConstraint.activate([
  77. modernContainer.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
  78. modernContainer.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -14),
  79. modernContainer.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
  80. modernContainer.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -14)
  81. ])
  82. // 1. Header Bar
  83. let headerStack = UIStackView()
  84. headerStack.translatesAutoresizingMaskIntoConstraints = false
  85. headerStack.axis = .horizontal
  86. headerStack.alignment = .center
  87. headerStack.spacing = 10
  88. modernContainer.addSubview(headerStack)
  89. modernBadgeView.translatesAutoresizingMaskIntoConstraints = false
  90. modernBadgeView.backgroundColor = UIColor(red: 235/255, green: 242/255, blue: 252/255, alpha: 1.0)
  91. modernBadgeView.layer.cornerRadius = 8
  92. modernBadgeView.layer.masksToBounds = true
  93. modernBadgeIcon.translatesAutoresizingMaskIntoConstraints = false
  94. if #available(iOS 13.0, *) {
  95. modernBadgeIcon.image = UIImage(systemName: "tag.fill")
  96. }
  97. modernBadgeIcon.tintColor = UIColor.siraniPrimaryBlue
  98. modernBadgeIcon.contentMode = .scaleAspectFit
  99. modernBadgeView.addSubview(modernBadgeIcon)
  100. NSLayoutConstraint.activate([
  101. modernBadgeView.widthAnchor.constraint(equalToConstant: 32),
  102. modernBadgeView.heightAnchor.constraint(equalToConstant: 32),
  103. modernBadgeIcon.centerXAnchor.constraint(equalTo: modernBadgeView.centerXAnchor),
  104. modernBadgeIcon.centerYAnchor.constraint(equalTo: modernBadgeView.centerYAnchor),
  105. modernBadgeIcon.widthAnchor.constraint(equalToConstant: 18),
  106. modernBadgeIcon.heightAnchor.constraint(equalToConstant: 18)
  107. ])
  108. headerStack.addArrangedSubview(modernBadgeView)
  109. let numberStack = UIStackView()
  110. numberStack.axis = .vertical
  111. numberStack.spacing = 1
  112. modernCategoryLabel.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  113. modernCategoryLabel.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  114. modernCategoryLabel.text = "NOMOR SERTIFIKAT LABEL"
  115. numberStack.addArrangedSubview(modernCategoryLabel)
  116. modernNomorLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
  117. modernNomorLabel.textColor = UIColor.siraniPrimaryBlue
  118. modernNomorLabel.numberOfLines = 1
  119. modernNomorLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
  120. modernNomorLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
  121. numberStack.addArrangedSubview(modernNomorLabel)
  122. headerStack.addArrangedSubview(numberStack)
  123. // Status Pill
  124. modernStatusPill.translatesAutoresizingMaskIntoConstraints = false
  125. modernStatusPill.layer.cornerRadius = 6
  126. modernStatusPill.layer.masksToBounds = true
  127. modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
  128. modernStatusLabel.translatesAutoresizingMaskIntoConstraints = false
  129. modernStatusLabel.font = UIFont.systemFont(ofSize: 10, weight: .bold)
  130. modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
  131. modernStatusLabel.textAlignment = .center
  132. modernStatusPill.addSubview(modernStatusLabel)
  133. NSLayoutConstraint.activate([
  134. modernStatusLabel.topAnchor.constraint(equalTo: modernStatusPill.topAnchor, constant: 4),
  135. modernStatusLabel.bottomAnchor.constraint(equalTo: modernStatusPill.bottomAnchor, constant: -4),
  136. modernStatusLabel.leadingAnchor.constraint(equalTo: modernStatusPill.leadingAnchor, constant: 8),
  137. modernStatusLabel.trailingAnchor.constraint(equalTo: modernStatusPill.trailingAnchor, constant: -8)
  138. ])
  139. headerStack.addArrangedSubview(modernStatusPill)
  140. // 2. Divider
  141. let divider = UIView()
  142. divider.translatesAutoresizingMaskIntoConstraints = false
  143. divider.backgroundColor = UIColor(red: 240/255, green: 244/255, blue: 250/255, alpha: 1.0)
  144. modernContainer.addSubview(divider)
  145. // 3. Equipment Name
  146. modernPerangkatLabel.translatesAutoresizingMaskIntoConstraints = false
  147. modernPerangkatLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold)
  148. modernPerangkatLabel.textColor = UIColor(red: 26/255, green: 29/255, blue: 38/255, alpha: 1.0)
  149. modernPerangkatLabel.numberOfLines = 2
  150. modernContainer.addSubview(modernPerangkatLabel)
  151. // 4. Company Row
  152. let companyStack = UIStackView()
  153. companyStack.translatesAutoresizingMaskIntoConstraints = false
  154. companyStack.axis = .horizontal
  155. companyStack.spacing = 6
  156. companyStack.alignment = .center
  157. if #available(iOS 13.0, *) {
  158. modernCompanyIcon.image = UIImage(systemName: "building.2.fill")
  159. }
  160. modernCompanyIcon.tintColor = UIColor(red: 120/255, green: 135/255, blue: 155/255, alpha: 1.0)
  161. modernCompanyIcon.contentMode = .scaleAspectFit
  162. modernCompanyIcon.translatesAutoresizingMaskIntoConstraints = false
  163. NSLayoutConstraint.activate([
  164. modernCompanyIcon.widthAnchor.constraint(equalToConstant: 14),
  165. modernCompanyIcon.heightAnchor.constraint(equalToConstant: 14)
  166. ])
  167. companyStack.addArrangedSubview(modernCompanyIcon)
  168. modernPemohonLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
  169. modernPemohonLabel.textColor = UIColor(red: 90/255, green: 105/255, blue: 125/255, alpha: 1.0)
  170. modernPemohonLabel.numberOfLines = 1
  171. companyStack.addArrangedSubview(modernPemohonLabel)
  172. modernContainer.addSubview(companyStack)
  173. // 5. Spec Mini Cards
  174. modernSpecStack.translatesAutoresizingMaskIntoConstraints = false
  175. modernSpecStack.axis = .horizontal
  176. modernSpecStack.distribution = .fillEqually
  177. modernSpecStack.spacing = 8
  178. modernContainer.addSubview(modernSpecStack)
  179. // Merk Card
  180. modernMerkCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  181. modernMerkCard.layer.cornerRadius = 8
  182. modernMerkCard.layer.borderWidth = 1
  183. modernMerkCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  184. let merkInnerStack = UIStackView()
  185. merkInnerStack.translatesAutoresizingMaskIntoConstraints = false
  186. merkInnerStack.axis = .vertical
  187. merkInnerStack.spacing = 2
  188. modernMerkCard.addSubview(merkInnerStack)
  189. modernMerkTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  190. modernMerkTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  191. modernMerkTitle.text = "MERK & MODEL"
  192. merkInnerStack.addArrangedSubview(modernMerkTitle)
  193. modernMerkVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  194. modernMerkVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  195. modernMerkVal.numberOfLines = 1
  196. merkInnerStack.addArrangedSubview(modernMerkVal)
  197. NSLayoutConstraint.activate([
  198. merkInnerStack.topAnchor.constraint(equalTo: modernMerkCard.topAnchor, constant: 6),
  199. merkInnerStack.bottomAnchor.constraint(equalTo: modernMerkCard.bottomAnchor, constant: -6),
  200. merkInnerStack.leadingAnchor.constraint(equalTo: modernMerkCard.leadingAnchor, constant: 8),
  201. merkInnerStack.trailingAnchor.constraint(equalTo: modernMerkCard.trailingAnchor, constant: -8)
  202. ])
  203. modernSpecStack.addArrangedSubview(modernMerkCard)
  204. // Negara Card
  205. modernNegaraCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  206. modernNegaraCard.layer.cornerRadius = 8
  207. modernNegaraCard.layer.borderWidth = 1
  208. modernNegaraCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  209. let negaraInnerStack = UIStackView()
  210. negaraInnerStack.translatesAutoresizingMaskIntoConstraints = false
  211. negaraInnerStack.axis = .vertical
  212. negaraInnerStack.spacing = 2
  213. modernNegaraCard.addSubview(negaraInnerStack)
  214. modernNegaraTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  215. modernNegaraTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  216. modernNegaraTitle.text = "NEGARA ASAL"
  217. negaraInnerStack.addArrangedSubview(modernNegaraTitle)
  218. modernNegaraVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  219. modernNegaraVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  220. modernNegaraVal.numberOfLines = 1
  221. negaraInnerStack.addArrangedSubview(modernNegaraVal)
  222. NSLayoutConstraint.activate([
  223. negaraInnerStack.topAnchor.constraint(equalTo: modernNegaraCard.topAnchor, constant: 6),
  224. negaraInnerStack.bottomAnchor.constraint(equalTo: modernNegaraCard.bottomAnchor, constant: -6),
  225. negaraInnerStack.leadingAnchor.constraint(equalTo: modernNegaraCard.leadingAnchor, constant: 8),
  226. negaraInnerStack.trailingAnchor.constraint(equalTo: modernNegaraCard.trailingAnchor, constant: -8)
  227. ])
  228. modernSpecStack.addArrangedSubview(modernNegaraCard)
  229. // 6. Validity Bar
  230. modernValidityCard.translatesAutoresizingMaskIntoConstraints = false
  231. modernValidityCard.backgroundColor = UIColor(red: 244/255, green: 248/255, blue: 254/255, alpha: 1.0)
  232. modernValidityCard.layer.cornerRadius = 8
  233. modernContainer.addSubview(modernValidityCard)
  234. let validityStack = UIStackView()
  235. validityStack.translatesAutoresizingMaskIntoConstraints = false
  236. validityStack.axis = .horizontal
  237. validityStack.alignment = .center
  238. validityStack.spacing = 6
  239. modernValidityCard.addSubview(validityStack)
  240. if #available(iOS 13.0, *) {
  241. modernCalendarIcon.image = UIImage(systemName: "calendar")
  242. }
  243. modernCalendarIcon.tintColor = UIColor.siraniPrimaryBlue
  244. modernCalendarIcon.contentMode = .scaleAspectFit
  245. modernCalendarIcon.translatesAutoresizingMaskIntoConstraints = false
  246. NSLayoutConstraint.activate([
  247. modernCalendarIcon.widthAnchor.constraint(equalToConstant: 13),
  248. modernCalendarIcon.heightAnchor.constraint(equalToConstant: 13)
  249. ])
  250. validityStack.addArrangedSubview(modernCalendarIcon)
  251. modernValidityLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
  252. modernValidityLabel.textColor = UIColor(red: 50/255, green: 70/255, blue: 95/255, alpha: 1.0)
  253. modernValidityLabel.numberOfLines = 1
  254. validityStack.addArrangedSubview(modernValidityLabel)
  255. let spacer = UIView()
  256. spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
  257. validityStack.addArrangedSubview(spacer)
  258. modernDetailLabel.font = UIFont.systemFont(ofSize: 11, weight: .bold)
  259. modernDetailLabel.textColor = UIColor.siraniPrimaryBlue
  260. modernDetailLabel.text = "Detail"
  261. validityStack.addArrangedSubview(modernDetailLabel)
  262. if #available(iOS 13.0, *) {
  263. modernChevronIcon.image = UIImage(systemName: "chevron.right")
  264. }
  265. modernChevronIcon.tintColor = UIColor.siraniPrimaryBlue
  266. modernChevronIcon.contentMode = .scaleAspectFit
  267. modernChevronIcon.translatesAutoresizingMaskIntoConstraints = false
  268. NSLayoutConstraint.activate([
  269. modernChevronIcon.widthAnchor.constraint(equalToConstant: 10),
  270. modernChevronIcon.heightAnchor.constraint(equalToConstant: 10)
  271. ])
  272. validityStack.addArrangedSubview(modernChevronIcon)
  273. NSLayoutConstraint.activate([
  274. validityStack.topAnchor.constraint(equalTo: modernValidityCard.topAnchor, constant: 6),
  275. validityStack.bottomAnchor.constraint(equalTo: modernValidityCard.bottomAnchor, constant: -6),
  276. validityStack.leadingAnchor.constraint(equalTo: modernValidityCard.leadingAnchor, constant: 10),
  277. validityStack.trailingAnchor.constraint(equalTo: modernValidityCard.trailingAnchor, constant: -10)
  278. ])
  279. NSLayoutConstraint.activate([
  280. headerStack.topAnchor.constraint(equalTo: modernContainer.topAnchor),
  281. headerStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  282. headerStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  283. divider.topAnchor.constraint(equalTo: headerStack.bottomAnchor, constant: 10),
  284. divider.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  285. divider.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  286. divider.heightAnchor.constraint(equalToConstant: 1),
  287. modernPerangkatLabel.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 10),
  288. modernPerangkatLabel.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  289. modernPerangkatLabel.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  290. companyStack.topAnchor.constraint(equalTo: modernPerangkatLabel.bottomAnchor, constant: 6),
  291. companyStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  292. companyStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  293. modernSpecStack.topAnchor.constraint(equalTo: companyStack.bottomAnchor, constant: 10),
  294. modernSpecStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  295. modernSpecStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  296. modernValidityCard.topAnchor.constraint(equalTo: modernSpecStack.bottomAnchor, constant: 10),
  297. modernValidityCard.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  298. modernValidityCard.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  299. modernValidityCard.bottomAnchor.constraint(equalTo: modernContainer.bottomAnchor)
  300. ])
  301. }
  302. func updateViews(labelData: Label) {
  303. setupCardVisuals()
  304. setupModernLayout()
  305. card.isInteractable = true
  306. let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onItemTap))
  307. card.addGestureRecognizer(gesture)
  308. modernNomorLabel.text = labelData.nomor.isEmpty ? "-" : labelData.nomor
  309. let statusStr = labelData.statusBerlaku.trimmingCharacters(in: .whitespacesAndNewlines)
  310. let displayStatus = statusStr.isEmpty ? "TERBIT" : statusStr.uppercased()
  311. modernStatusLabel.text = displayStatus
  312. if displayStatus.contains("CABUT") || displayStatus.contains("TIDAK") {
  313. modernStatusPill.backgroundColor = UIColor(red: 254/255, green: 242/255, blue: 242/255, alpha: 1.0)
  314. modernStatusLabel.textColor = UIColor(red: 220/255, green: 38/255, blue: 38/255, alpha: 1.0)
  315. } else if displayStatus.contains("REVISI") || displayStatus.contains("PROSES") {
  316. modernStatusPill.backgroundColor = UIColor(red: 254/255, green: 249/255, blue: 235/255, alpha: 1.0)
  317. modernStatusLabel.textColor = UIColor(red: 180/255, green: 83/255, blue: 9/255, alpha: 1.0)
  318. } else {
  319. modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
  320. modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
  321. }
  322. modernPerangkatLabel.text = labelData.eqp_name.isEmpty ? "-" : labelData.eqp_name
  323. modernPemohonLabel.text = labelData.cust_name.isEmpty ? "-" : labelData.cust_name
  324. var mm = labelData.merk
  325. if !labelData.model.isEmpty && labelData.model != "null" {
  326. mm = mm.isEmpty ? labelData.model : "\(mm) • \(labelData.model)"
  327. }
  328. modernMerkVal.text = mm.isEmpty ? "-" : mm
  329. let negara = labelData.negara.trimmingCharacters(in: .whitespacesAndNewlines)
  330. modernNegaraVal.text = (negara.isEmpty || negara == "null") ? "-" : negara
  331. let tglBerlaku = CommonHelper.formatDate(from: labelData.lic_date)
  332. let tglBerakhir = CommonHelper.formatDate(from: labelData.exp_date)
  333. if tglBerlaku != "-" && tglBerakhir != "-" {
  334. modernValidityLabel.text = "Berlaku: \(tglBerlaku) s/d \(tglBerakhir)"
  335. } else if tglBerlaku != "-" {
  336. modernValidityLabel.text = "Berlaku: \(tglBerlaku)"
  337. } else {
  338. modernValidityLabel.text = "Masa Berlaku: -"
  339. }
  340. self.dataLabel = labelData
  341. }
  342. @objc func onItemTap(sender: UITapGestureRecognizer) {
  343. cellDelegate?.openLabelDetail(data: dataLabel)
  344. }
  345. }