BalaiUjiCell.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. //
  2. // BalaiUjiCell.swift
  3. // Sirani
  4. //
  5. // Created by Anhar Solehudin on 03/04/19.
  6. // Copyright © 2019 Anhar Solehudin. All rights reserved.
  7. //
  8. import UIKit
  9. import MaterialComponents.MaterialCards
  10. protocol BalaiUjiCellDelegate: NSObjectProtocol {
  11. func openWeb(urlWeb: String?)
  12. }
  13. class BalaiUjiCell: UITableViewCell {
  14. @IBOutlet weak var card: MDCCard!
  15. @IBOutlet weak var lblIdBalai: UILabel!
  16. @IBOutlet weak var lblNoPenetapanTitle: UILabel!
  17. @IBOutlet weak var lblNoPenetapan: UILabel!
  18. @IBOutlet weak var lblTglPenetapanTitle: UILabel!
  19. @IBOutlet weak var lblTglPenetapan: UILabel!
  20. @IBOutlet weak var lblNamaBalaiUji: UILabel!
  21. @IBOutlet weak var lblRuangLingkup: UILabel!
  22. @IBOutlet weak var lblAlamat: UILabel!
  23. @IBOutlet weak var lblKontak: UILabel!
  24. @IBOutlet weak var btnWebsite: UIButton!
  25. var cellDelegate: BalaiUjiCellDelegate?
  26. var urlWeb: String?
  27. // Modern UI Elements
  28. private let modernContainer = UIView()
  29. private let modernBadgeView = UIView()
  30. private let modernBadgeIcon = UIImageView()
  31. private let modernNamaLabel = UILabel()
  32. private let modernCategoryLabel = UILabel()
  33. private let modernStatusPill = UIView()
  34. private let modernStatusLabel = UILabel()
  35. private let modernSpecStack = UIStackView()
  36. private let modernPenetapanCard = UIView()
  37. private let modernPenetapanTitle = UILabel()
  38. private let modernPenetapanVal = UILabel()
  39. private let modernTglCard = UIView()
  40. private let modernTglTitle = UILabel()
  41. private let modernTglVal = UILabel()
  42. private let modernLingkupCard = UIView()
  43. private let modernLingkupTitle = UILabel()
  44. private let modernLingkupVal = UILabel()
  45. private let modernAlamatStack = UIStackView()
  46. private let modernAlamatIcon = UIImageView()
  47. private let modernAlamatLabel = UILabel()
  48. private let modernKontakStack = UIStackView()
  49. private let modernKontakIcon = UIImageView()
  50. private let modernKontakLabel = UILabel()
  51. private let modernBtnWebsite = UIButton(type: .system)
  52. private var isModernLayoutBuilt = false
  53. override func awakeFromNib() {
  54. super.awakeFromNib()
  55. self.selectionStyle = .none
  56. self.backgroundColor = .clear
  57. setupCardVisuals()
  58. setupModernLayout()
  59. }
  60. private func setupCardVisuals() {
  61. guard let card = card else { return }
  62. card.cornerRadius = 16
  63. card.setShadowElevation(ShadowElevation(rawValue: 2), for: .normal)
  64. card.setShadowColor(UIColor.black.withAlphaComponent(0.06), for: .normal)
  65. card.layer.borderColor = UIColor(red: 228/255, green: 235/255, blue: 245/255, alpha: 1.0).cgColor
  66. card.layer.borderWidth = 1.0
  67. card.backgroundColor = .white
  68. }
  69. private func setupModernLayout() {
  70. guard let card = card, !isModernLayoutBuilt else { return }
  71. isModernLayoutBuilt = true
  72. for subview in card.subviews {
  73. subview.isHidden = true
  74. }
  75. modernContainer.translatesAutoresizingMaskIntoConstraints = false
  76. modernContainer.backgroundColor = .clear
  77. card.addSubview(modernContainer)
  78. NSLayoutConstraint.activate([
  79. modernContainer.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
  80. modernContainer.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -14),
  81. modernContainer.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
  82. modernContainer.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -14)
  83. ])
  84. // 1. Header Bar
  85. let headerStack = UIStackView()
  86. headerStack.translatesAutoresizingMaskIntoConstraints = false
  87. headerStack.axis = .horizontal
  88. headerStack.alignment = .center
  89. headerStack.spacing = 10
  90. modernContainer.addSubview(headerStack)
  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: "flask.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. let titleStack = UIStackView()
  112. titleStack.axis = .vertical
  113. titleStack.spacing = 1
  114. modernCategoryLabel.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  115. modernCategoryLabel.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  116. modernCategoryLabel.text = "BALAI UJI TERAKREDITASI"
  117. titleStack.addArrangedSubview(modernCategoryLabel)
  118. modernNamaLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
  119. modernNamaLabel.textColor = UIColor.siraniPrimaryBlue
  120. modernNamaLabel.numberOfLines = 1
  121. modernNamaLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
  122. titleStack.addArrangedSubview(modernNamaLabel)
  123. headerStack.addArrangedSubview(titleStack)
  124. // Status Pill
  125. modernStatusPill.translatesAutoresizingMaskIntoConstraints = false
  126. modernStatusPill.layer.cornerRadius = 6
  127. modernStatusPill.layer.masksToBounds = true
  128. modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
  129. modernStatusLabel.translatesAutoresizingMaskIntoConstraints = false
  130. modernStatusLabel.font = UIFont.systemFont(ofSize: 10, weight: .bold)
  131. modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
  132. modernStatusLabel.text = "RESMI"
  133. modernStatusPill.addSubview(modernStatusLabel)
  134. NSLayoutConstraint.activate([
  135. modernStatusLabel.topAnchor.constraint(equalTo: modernStatusPill.topAnchor, constant: 4),
  136. modernStatusLabel.bottomAnchor.constraint(equalTo: modernStatusPill.bottomAnchor, constant: -4),
  137. modernStatusLabel.leadingAnchor.constraint(equalTo: modernStatusPill.leadingAnchor, constant: 8),
  138. modernStatusLabel.trailingAnchor.constraint(equalTo: modernStatusPill.trailingAnchor, constant: -8)
  139. ])
  140. headerStack.addArrangedSubview(modernStatusPill)
  141. // 2. Divider
  142. let divider = UIView()
  143. divider.translatesAutoresizingMaskIntoConstraints = false
  144. divider.backgroundColor = UIColor(red: 240/255, green: 244/255, blue: 250/255, alpha: 1.0)
  145. modernContainer.addSubview(divider)
  146. // 3. Mini Spec Cards (No Penetapan, Tgl Penetapan)
  147. modernSpecStack.translatesAutoresizingMaskIntoConstraints = false
  148. modernSpecStack.axis = .horizontal
  149. modernSpecStack.distribution = .fillEqually
  150. modernSpecStack.spacing = 8
  151. modernContainer.addSubview(modernSpecStack)
  152. modernPenetapanCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  153. modernPenetapanCard.layer.cornerRadius = 8
  154. modernPenetapanCard.layer.borderWidth = 1
  155. modernPenetapanCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  156. let pStack = UIStackView()
  157. pStack.translatesAutoresizingMaskIntoConstraints = false
  158. pStack.axis = .vertical
  159. pStack.spacing = 2
  160. modernPenetapanCard.addSubview(pStack)
  161. modernPenetapanTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  162. modernPenetapanTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  163. modernPenetapanTitle.text = "NO. PENETAPAN"
  164. pStack.addArrangedSubview(modernPenetapanTitle)
  165. modernPenetapanVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  166. modernPenetapanVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  167. pStack.addArrangedSubview(modernPenetapanVal)
  168. NSLayoutConstraint.activate([
  169. pStack.topAnchor.constraint(equalTo: modernPenetapanCard.topAnchor, constant: 6),
  170. pStack.bottomAnchor.constraint(equalTo: modernPenetapanCard.bottomAnchor, constant: -6),
  171. pStack.leadingAnchor.constraint(equalTo: modernPenetapanCard.leadingAnchor, constant: 8),
  172. pStack.trailingAnchor.constraint(equalTo: modernPenetapanCard.trailingAnchor, constant: -8)
  173. ])
  174. modernSpecStack.addArrangedSubview(modernPenetapanCard)
  175. modernTglCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
  176. modernTglCard.layer.cornerRadius = 8
  177. modernTglCard.layer.borderWidth = 1
  178. modernTglCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
  179. let tStack = UIStackView()
  180. tStack.translatesAutoresizingMaskIntoConstraints = false
  181. tStack.axis = .vertical
  182. tStack.spacing = 2
  183. modernTglCard.addSubview(tStack)
  184. modernTglTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  185. modernTglTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
  186. modernTglTitle.text = "TGL PENETAPAN"
  187. tStack.addArrangedSubview(modernTglTitle)
  188. modernTglVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
  189. modernTglVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
  190. tStack.addArrangedSubview(modernTglVal)
  191. NSLayoutConstraint.activate([
  192. tStack.topAnchor.constraint(equalTo: modernTglCard.topAnchor, constant: 6),
  193. tStack.bottomAnchor.constraint(equalTo: modernTglCard.bottomAnchor, constant: -6),
  194. tStack.leadingAnchor.constraint(equalTo: modernTglCard.leadingAnchor, constant: 8),
  195. tStack.trailingAnchor.constraint(equalTo: modernTglCard.trailingAnchor, constant: -8)
  196. ])
  197. modernSpecStack.addArrangedSubview(modernTglCard)
  198. // 4. Ruang Lingkup Card
  199. modernLingkupCard.translatesAutoresizingMaskIntoConstraints = false
  200. modernLingkupCard.backgroundColor = UIColor(red: 245/255, green: 249/255, blue: 255/255, alpha: 1.0)
  201. modernLingkupCard.layer.cornerRadius = 8
  202. modernLingkupCard.layer.borderWidth = 1
  203. modernLingkupCard.layer.borderColor = UIColor(red: 228/255, green: 236/255, blue: 248/255, alpha: 1.0).cgColor
  204. modernContainer.addSubview(modernLingkupCard)
  205. let lStack = UIStackView()
  206. lStack.translatesAutoresizingMaskIntoConstraints = false
  207. lStack.axis = .vertical
  208. lStack.spacing = 2
  209. modernLingkupCard.addSubview(lStack)
  210. modernLingkupTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
  211. modernLingkupTitle.textColor = UIColor.siraniPrimaryBlue
  212. modernLingkupTitle.text = "RUANG LINGKUP PENGUJIAN"
  213. lStack.addArrangedSubview(modernLingkupTitle)
  214. modernLingkupVal.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
  215. modernLingkupVal.textColor = UIColor(red: 30/255, green: 45/255, blue: 65/255, alpha: 1.0)
  216. modernLingkupVal.numberOfLines = 2
  217. lStack.addArrangedSubview(modernLingkupVal)
  218. NSLayoutConstraint.activate([
  219. lStack.topAnchor.constraint(equalTo: modernLingkupCard.topAnchor, constant: 6),
  220. lStack.bottomAnchor.constraint(equalTo: modernLingkupCard.bottomAnchor, constant: -6),
  221. lStack.leadingAnchor.constraint(equalTo: modernLingkupCard.leadingAnchor, constant: 10),
  222. lStack.trailingAnchor.constraint(equalTo: modernLingkupCard.trailingAnchor, constant: -10)
  223. ])
  224. // 5. Alamat Stack
  225. modernAlamatStack.translatesAutoresizingMaskIntoConstraints = false
  226. modernAlamatStack.axis = .horizontal
  227. modernAlamatStack.spacing = 6
  228. modernAlamatStack.alignment = .top
  229. if #available(iOS 13.0, *) {
  230. modernAlamatIcon.image = UIImage(systemName: "mappin.and.ellipse")
  231. }
  232. modernAlamatIcon.tintColor = UIColor(red: 120/255, green: 135/255, blue: 155/255, alpha: 1.0)
  233. modernAlamatIcon.contentMode = .scaleAspectFit
  234. modernAlamatIcon.translatesAutoresizingMaskIntoConstraints = false
  235. NSLayoutConstraint.activate([
  236. modernAlamatIcon.widthAnchor.constraint(equalToConstant: 14),
  237. modernAlamatIcon.heightAnchor.constraint(equalToConstant: 14)
  238. ])
  239. modernAlamatStack.addArrangedSubview(modernAlamatIcon)
  240. modernAlamatLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
  241. modernAlamatLabel.textColor = UIColor(red: 90/255, green: 105/255, blue: 125/255, alpha: 1.0)
  242. modernAlamatLabel.numberOfLines = 2
  243. modernAlamatStack.addArrangedSubview(modernAlamatLabel)
  244. modernContainer.addSubview(modernAlamatStack)
  245. // 6. Kontak Stack & Button Website
  246. let footerStack = UIStackView()
  247. footerStack.translatesAutoresizingMaskIntoConstraints = false
  248. footerStack.axis = .horizontal
  249. footerStack.spacing = 8
  250. footerStack.alignment = .center
  251. modernContainer.addSubview(footerStack)
  252. if #available(iOS 13.0, *) {
  253. modernKontakIcon.image = UIImage(systemName: "phone.fill")
  254. }
  255. modernKontakIcon.tintColor = UIColor(red: 120/255, green: 135/255, blue: 155/255, alpha: 1.0)
  256. modernKontakIcon.contentMode = .scaleAspectFit
  257. modernKontakIcon.translatesAutoresizingMaskIntoConstraints = false
  258. NSLayoutConstraint.activate([
  259. modernKontakIcon.widthAnchor.constraint(equalToConstant: 12),
  260. modernKontakIcon.heightAnchor.constraint(equalToConstant: 12)
  261. ])
  262. footerStack.addArrangedSubview(modernKontakIcon)
  263. modernKontakLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
  264. modernKontakLabel.textColor = UIColor(red: 90/255, green: 105/255, blue: 125/255, alpha: 1.0)
  265. modernKontakLabel.numberOfLines = 1
  266. footerStack.addArrangedSubview(modernKontakLabel)
  267. let spacer = UIView()
  268. spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
  269. footerStack.addArrangedSubview(spacer)
  270. modernBtnWebsite.translatesAutoresizingMaskIntoConstraints = false
  271. modernBtnWebsite.setTitle("🌐 Website", for: .normal)
  272. modernBtnWebsite.titleLabel?.font = UIFont.systemFont(ofSize: 11, weight: .bold)
  273. modernBtnWebsite.setTitleColor(UIColor.siraniPrimaryBlue, for: .normal)
  274. modernBtnWebsite.backgroundColor = UIColor(red: 235/255, green: 242/255, blue: 252/255, alpha: 1.0)
  275. modernBtnWebsite.layer.cornerRadius = 8
  276. modernBtnWebsite.contentEdgeInsets = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
  277. modernBtnWebsite.addTarget(self, action: #selector(onBtnWebsiteTap(_:)), for: .touchUpInside)
  278. footerStack.addArrangedSubview(modernBtnWebsite)
  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. modernSpecStack.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 10),
  288. modernSpecStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  289. modernSpecStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  290. modernLingkupCard.topAnchor.constraint(equalTo: modernSpecStack.bottomAnchor, constant: 8),
  291. modernLingkupCard.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  292. modernLingkupCard.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  293. modernAlamatStack.topAnchor.constraint(equalTo: modernLingkupCard.bottomAnchor, constant: 8),
  294. modernAlamatStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  295. modernAlamatStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  296. footerStack.topAnchor.constraint(equalTo: modernAlamatStack.bottomAnchor, constant: 8),
  297. footerStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
  298. footerStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
  299. footerStack.bottomAnchor.constraint(equalTo: modernContainer.bottomAnchor)
  300. ])
  301. }
  302. func updateViews(balaiUji: BalaiUji) {
  303. setupCardVisuals()
  304. setupModernLayout()
  305. card.isInteractable = false
  306. modernNamaLabel.text = balaiUji.nama.isEmpty ? "-" : balaiUji.nama
  307. modernPenetapanVal.text = balaiUji.balai_id == 0 ? "-" : String(balaiUji.balai_id)
  308. let tglFormatted = CommonHelper.formatDate(from: balaiUji.tanggal_terbit)
  309. modernTglVal.text = tglFormatted.isEmpty ? "-" : tglFormatted
  310. modernLingkupVal.text = balaiUji.ruang_lingkup.isEmpty ? "-" : balaiUji.ruang_lingkup
  311. modernAlamatLabel.text = balaiUji.tempat.isEmpty ? "-" : balaiUji.tempat
  312. modernKontakLabel.text = balaiUji.kontak.isEmpty ? "-" : balaiUji.kontak
  313. urlWeb = balaiUji.website
  314. modernBtnWebsite.isHidden = (urlWeb == nil || urlWeb!.isEmpty || urlWeb == "null")
  315. }
  316. @IBAction func onBtnWebsiteTap(_ sender: Any) {
  317. cellDelegate?.openWeb(urlWeb: urlWeb)
  318. }
  319. }