| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- //
- // AplikasiCell.swift
- // Sirani
- //
- // Created by Hendi on 21/06/23.
- // Copyright © 2026 PT. Mitra Sinerji Teknoindo. All rights reserved.
- //
- import UIKit
- import MaterialComponents.MaterialCards
- import SkeletonView
- 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!
- @IBOutlet weak var statusTop: NSLayoutConstraint!
- @IBOutlet weak var disposisiTitle: UILabel!
- var cellDelegate: AplikasiCellDelegate?
- var dataAplikasi: Aplikasi?
-
- // Modern Programmatic Subviews
- private let modernContainer = UIView()
- private let modernBadgeView = UIView()
- private let modernBadgeIcon = UIImageView()
- private let modernAplIdLabel = UILabel()
- private let modernIdIzinLabel = UILabel()
- private let modernCategoryLabel = UILabel()
- private let modernStatusPill = UIView()
- private let modernStatusLabel = UILabel()
-
- private let modernEqpLabel = UILabel()
- private let modernCompanyIcon = UIImageView()
- private let modernCustLabel = UILabel()
-
- private let modernSpecStack = UIStackView()
- private let modernMerkCard = UIView()
- private let modernMerkTitle = UILabel()
- private let modernMerkVal = UILabel()
-
- private let modernResiCard = UIView()
- private let modernResiTitle = UILabel()
- private let modernResiVal = UILabel()
-
- private let modernDisposisiCard = UIView()
- private let modernDisposisiIcon = UIImageView()
- private let modernDisposisiLabel = UILabel()
- private let modernDetailLabel = UILabel()
- private let modernChevronIcon = UIImageView()
-
- private var isModernLayoutBuilt = false
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- self.selectionStyle = .none
- self.backgroundColor = .clear
-
- setupCardVisuals()
- setupModernLayout()
- }
-
- private func setupCardVisuals() {
- guard let card = card else { return }
- card.cornerRadius = 16
- card.setShadowElevation(ShadowElevation(rawValue: 2), for: .normal)
- card.setShadowColor(UIColor.black.withAlphaComponent(0.06), for: .normal)
- card.layer.borderColor = UIColor(red: 228/255, green: 235/255, blue: 245/255, alpha: 1.0).cgColor
- card.layer.borderWidth = 1.0
- card.backgroundColor = .white
- }
-
- private func setupModernLayout() {
- guard let card = card, !isModernLayoutBuilt else { return }
- isModernLayoutBuilt = true
-
- for subview in card.subviews {
- subview.isHidden = true
- }
-
- modernContainer.translatesAutoresizingMaskIntoConstraints = false
- modernContainer.backgroundColor = .clear
- card.addSubview(modernContainer)
-
- NSLayoutConstraint.activate([
- modernContainer.topAnchor.constraint(equalTo: card.topAnchor, constant: 14),
- modernContainer.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -14),
- modernContainer.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: 14),
- modernContainer.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -14)
- ])
-
- // 1. Header Bar
- let headerStack = UIStackView()
- headerStack.translatesAutoresizingMaskIntoConstraints = false
- headerStack.axis = .horizontal
- headerStack.alignment = .center
- headerStack.spacing = 10
- modernContainer.addSubview(headerStack)
-
- modernBadgeView.translatesAutoresizingMaskIntoConstraints = false
- modernBadgeView.backgroundColor = UIColor(red: 235/255, green: 242/255, blue: 252/255, alpha: 1.0)
- modernBadgeView.layer.cornerRadius = 8
- modernBadgeView.layer.masksToBounds = true
-
- modernBadgeIcon.translatesAutoresizingMaskIntoConstraints = false
- if #available(iOS 13.0, *) {
- modernBadgeIcon.image = UIImage(systemName: "app.badge.fill")
- }
- modernBadgeIcon.tintColor = UIColor.siraniPrimaryBlue
- modernBadgeIcon.contentMode = .scaleAspectFit
- modernBadgeView.addSubview(modernBadgeIcon)
-
- NSLayoutConstraint.activate([
- modernBadgeView.widthAnchor.constraint(equalToConstant: 32),
- modernBadgeView.heightAnchor.constraint(equalToConstant: 32),
- modernBadgeIcon.centerXAnchor.constraint(equalTo: modernBadgeView.centerXAnchor),
- modernBadgeIcon.centerYAnchor.constraint(equalTo: modernBadgeView.centerYAnchor),
- modernBadgeIcon.widthAnchor.constraint(equalToConstant: 18),
- modernBadgeIcon.heightAnchor.constraint(equalToConstant: 18)
- ])
- headerStack.addArrangedSubview(modernBadgeView)
-
- let idStack = UIStackView()
- idStack.axis = .vertical
- idStack.spacing = 2
-
- modernAplIdLabel.font = UIFont.systemFont(ofSize: 14, weight: .bold)
- modernAplIdLabel.textColor = UIColor.siraniPrimaryBlue
- modernAplIdLabel.numberOfLines = 1
- modernAplIdLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
- modernAplIdLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
- idStack.addArrangedSubview(modernAplIdLabel)
-
- modernIdIzinLabel.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
- modernIdIzinLabel.textColor = UIColor(red: 113/255, green: 128/255, blue: 150/255, alpha: 1.0)
- modernIdIzinLabel.numberOfLines = 1
- modernIdIzinLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
- modernIdIzinLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
- idStack.addArrangedSubview(modernIdIzinLabel)
-
- headerStack.addArrangedSubview(idStack)
-
- // Status Pill
- modernStatusPill.translatesAutoresizingMaskIntoConstraints = false
- modernStatusPill.layer.cornerRadius = 6
- modernStatusPill.layer.masksToBounds = true
- modernStatusPill.backgroundColor = UIColor(red: 230/255, green: 244/255, blue: 234/255, alpha: 1.0)
-
- modernStatusLabel.translatesAutoresizingMaskIntoConstraints = false
- modernStatusLabel.font = UIFont.systemFont(ofSize: 10, weight: .bold)
- modernStatusLabel.textColor = UIColor(red: 19/255, green: 115/255, blue: 51/255, alpha: 1.0)
- modernStatusLabel.textAlignment = .center
- modernStatusPill.addSubview(modernStatusLabel)
-
- NSLayoutConstraint.activate([
- modernStatusLabel.topAnchor.constraint(equalTo: modernStatusPill.topAnchor, constant: 4),
- modernStatusLabel.bottomAnchor.constraint(equalTo: modernStatusPill.bottomAnchor, constant: -4),
- modernStatusLabel.leadingAnchor.constraint(equalTo: modernStatusPill.leadingAnchor, constant: 8),
- modernStatusLabel.trailingAnchor.constraint(equalTo: modernStatusPill.trailingAnchor, constant: -8)
- ])
- headerStack.addArrangedSubview(modernStatusPill)
-
- // 2. Divider
- let divider = UIView()
- divider.translatesAutoresizingMaskIntoConstraints = false
- divider.backgroundColor = UIColor(red: 240/255, green: 244/255, blue: 250/255, alpha: 1.0)
- modernContainer.addSubview(divider)
-
- // 3. Equipment Name
- modernEqpLabel.translatesAutoresizingMaskIntoConstraints = false
- modernEqpLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold)
- modernEqpLabel.textColor = UIColor(red: 26/255, green: 29/255, blue: 38/255, alpha: 1.0)
- modernEqpLabel.numberOfLines = 2
- modernContainer.addSubview(modernEqpLabel)
-
- // 4. Company Row
- let companyStack = UIStackView()
- companyStack.translatesAutoresizingMaskIntoConstraints = false
- companyStack.axis = .horizontal
- companyStack.spacing = 6
- companyStack.alignment = .center
-
- if #available(iOS 13.0, *) {
- modernCompanyIcon.image = UIImage(systemName: "building.2.fill")
- }
- modernCompanyIcon.tintColor = UIColor(red: 120/255, green: 135/255, blue: 155/255, alpha: 1.0)
- modernCompanyIcon.contentMode = .scaleAspectFit
- modernCompanyIcon.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- modernCompanyIcon.widthAnchor.constraint(equalToConstant: 14),
- modernCompanyIcon.heightAnchor.constraint(equalToConstant: 14)
- ])
- companyStack.addArrangedSubview(modernCompanyIcon)
-
- modernCustLabel.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
- modernCustLabel.textColor = UIColor(red: 90/255, green: 105/255, blue: 125/255, alpha: 1.0)
- modernCustLabel.numberOfLines = 1
- companyStack.addArrangedSubview(modernCustLabel)
- modernContainer.addSubview(companyStack)
-
- // 5. Spec Mini Cards
- modernSpecStack.translatesAutoresizingMaskIntoConstraints = false
- modernSpecStack.axis = .horizontal
- modernSpecStack.distribution = .fillEqually
- modernSpecStack.spacing = 8
- modernContainer.addSubview(modernSpecStack)
-
- // Merk Card
- modernMerkCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
- modernMerkCard.layer.cornerRadius = 8
- modernMerkCard.layer.borderWidth = 1
- modernMerkCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
-
- let merkInnerStack = UIStackView()
- merkInnerStack.translatesAutoresizingMaskIntoConstraints = false
- merkInnerStack.axis = .vertical
- merkInnerStack.spacing = 2
- modernMerkCard.addSubview(merkInnerStack)
-
- modernMerkTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
- modernMerkTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
- modernMerkTitle.text = "MERK & MODEL"
- merkInnerStack.addArrangedSubview(modernMerkTitle)
-
- modernMerkVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
- modernMerkVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
- modernMerkVal.numberOfLines = 1
- merkInnerStack.addArrangedSubview(modernMerkVal)
-
- NSLayoutConstraint.activate([
- merkInnerStack.topAnchor.constraint(equalTo: modernMerkCard.topAnchor, constant: 6),
- merkInnerStack.bottomAnchor.constraint(equalTo: modernMerkCard.bottomAnchor, constant: -6),
- merkInnerStack.leadingAnchor.constraint(equalTo: modernMerkCard.leadingAnchor, constant: 8),
- merkInnerStack.trailingAnchor.constraint(equalTo: modernMerkCard.trailingAnchor, constant: -8)
- ])
- modernSpecStack.addArrangedSubview(modernMerkCard)
-
- // Resi Card
- modernResiCard.backgroundColor = UIColor(red: 248/255, green: 250/255, blue: 253/255, alpha: 1.0)
- modernResiCard.layer.cornerRadius = 8
- modernResiCard.layer.borderWidth = 1
- modernResiCard.layer.borderColor = UIColor(red: 235/255, green: 240/255, blue: 248/255, alpha: 1.0).cgColor
-
- let resiInnerStack = UIStackView()
- resiInnerStack.translatesAutoresizingMaskIntoConstraints = false
- resiInnerStack.axis = .vertical
- resiInnerStack.spacing = 2
- modernResiCard.addSubview(resiInnerStack)
-
- modernResiTitle.font = UIFont.systemFont(ofSize: 9, weight: .bold)
- modernResiTitle.textColor = UIColor(red: 140/255, green: 155/255, blue: 175/255, alpha: 1.0)
- modernResiTitle.text = "NO. RESI"
- resiInnerStack.addArrangedSubview(modernResiTitle)
-
- modernResiVal.font = UIFont.systemFont(ofSize: 11, weight: .semibold)
- modernResiVal.textColor = UIColor(red: 40/255, green: 50/255, blue: 65/255, alpha: 1.0)
- modernResiVal.numberOfLines = 1
- resiInnerStack.addArrangedSubview(modernResiVal)
-
- NSLayoutConstraint.activate([
- resiInnerStack.topAnchor.constraint(equalTo: modernResiCard.topAnchor, constant: 6),
- resiInnerStack.bottomAnchor.constraint(equalTo: modernResiCard.bottomAnchor, constant: -6),
- resiInnerStack.leadingAnchor.constraint(equalTo: modernResiCard.leadingAnchor, constant: 8),
- resiInnerStack.trailingAnchor.constraint(equalTo: modernResiCard.trailingAnchor, constant: -8)
- ])
- modernSpecStack.addArrangedSubview(modernResiCard)
-
- // 6. Disposisi / Bottom Footer
- modernDisposisiCard.translatesAutoresizingMaskIntoConstraints = false
- modernDisposisiCard.backgroundColor = UIColor(red: 244/255, green: 248/255, blue: 254/255, alpha: 1.0)
- modernDisposisiCard.layer.cornerRadius = 8
- modernContainer.addSubview(modernDisposisiCard)
-
- let disposisiStack = UIStackView()
- disposisiStack.translatesAutoresizingMaskIntoConstraints = false
- disposisiStack.axis = .horizontal
- disposisiStack.alignment = .center
- disposisiStack.spacing = 6
- modernDisposisiCard.addSubview(disposisiStack)
-
- if #available(iOS 13.0, *) {
- modernDisposisiIcon.image = UIImage(systemName: "arrow.triangle.branch")
- }
- modernDisposisiIcon.tintColor = UIColor.siraniPrimaryBlue
- modernDisposisiIcon.contentMode = .scaleAspectFit
- modernDisposisiIcon.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- modernDisposisiIcon.widthAnchor.constraint(equalToConstant: 13),
- modernDisposisiIcon.heightAnchor.constraint(equalToConstant: 13)
- ])
- disposisiStack.addArrangedSubview(modernDisposisiIcon)
-
- modernDisposisiLabel.font = UIFont.systemFont(ofSize: 11, weight: .medium)
- modernDisposisiLabel.textColor = UIColor(red: 50/255, green: 70/255, blue: 95/255, alpha: 1.0)
- modernDisposisiLabel.numberOfLines = 1
- disposisiStack.addArrangedSubview(modernDisposisiLabel)
-
- let spacer = UIView()
- spacer.setContentHuggingPriority(.defaultLow, for: .horizontal)
- disposisiStack.addArrangedSubview(spacer)
-
- modernDetailLabel.font = UIFont.systemFont(ofSize: 11, weight: .bold)
- modernDetailLabel.textColor = UIColor.siraniPrimaryBlue
- modernDetailLabel.text = "Detail"
- disposisiStack.addArrangedSubview(modernDetailLabel)
-
- if #available(iOS 13.0, *) {
- modernChevronIcon.image = UIImage(systemName: "chevron.right")
- }
- modernChevronIcon.tintColor = UIColor.siraniPrimaryBlue
- modernChevronIcon.contentMode = .scaleAspectFit
- modernChevronIcon.translatesAutoresizingMaskIntoConstraints = false
- NSLayoutConstraint.activate([
- modernChevronIcon.widthAnchor.constraint(equalToConstant: 10),
- modernChevronIcon.heightAnchor.constraint(equalToConstant: 10)
- ])
- disposisiStack.addArrangedSubview(modernChevronIcon)
-
- NSLayoutConstraint.activate([
- disposisiStack.topAnchor.constraint(equalTo: modernDisposisiCard.topAnchor, constant: 6),
- disposisiStack.bottomAnchor.constraint(equalTo: modernDisposisiCard.bottomAnchor, constant: -6),
- disposisiStack.leadingAnchor.constraint(equalTo: modernDisposisiCard.leadingAnchor, constant: 10),
- disposisiStack.trailingAnchor.constraint(equalTo: modernDisposisiCard.trailingAnchor, constant: -10)
- ])
-
- NSLayoutConstraint.activate([
- headerStack.topAnchor.constraint(equalTo: modernContainer.topAnchor),
- headerStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- headerStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
-
- divider.topAnchor.constraint(equalTo: headerStack.bottomAnchor, constant: 10),
- divider.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- divider.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
- divider.heightAnchor.constraint(equalToConstant: 1),
-
- modernEqpLabel.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 10),
- modernEqpLabel.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- modernEqpLabel.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
-
- companyStack.topAnchor.constraint(equalTo: modernEqpLabel.bottomAnchor, constant: 6),
- companyStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- companyStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
-
- modernSpecStack.topAnchor.constraint(equalTo: companyStack.bottomAnchor, constant: 10),
- modernSpecStack.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- modernSpecStack.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
-
- modernDisposisiCard.topAnchor.constraint(equalTo: modernSpecStack.bottomAnchor, constant: 10),
- modernDisposisiCard.leadingAnchor.constraint(equalTo: modernContainer.leadingAnchor),
- modernDisposisiCard.trailingAnchor.constraint(equalTo: modernContainer.trailingAnchor),
- modernDisposisiCard.bottomAnchor.constraint(equalTo: modernContainer.bottomAnchor)
- ])
- }
-
- func updateViews(aplData: Aplikasi) {
- setupCardVisuals()
- setupModernLayout()
-
- card.isInteractable = true
- card.isUserInteractionEnabled = true
- modernContainer.isUserInteractionEnabled = true
- modernDisposisiCard.isUserInteractionEnabled = true
-
- if let gestures = card.gestureRecognizers {
- for g in gestures { card.removeGestureRecognizer(g) }
- }
- let gesture = UITapGestureRecognizer(target: self, action: #selector(self.onItemTap))
- card.addGestureRecognizer(gesture)
-
- if let disposisiGestures = modernDisposisiCard.gestureRecognizers {
- for g in disposisiGestures { modernDisposisiCard.removeGestureRecognizer(g) }
- }
- let disposisiGesture = UITapGestureRecognizer(target: self, action: #selector(self.onItemTap))
- modernDisposisiCard.addGestureRecognizer(disposisiGesture)
-
- modernAplIdLabel.text = "APL ID: \(aplData.apl_id)"
- lblAplId?.text = "\(aplData.apl_id)"
-
- let idIzinStr = aplData.id_izin.isEmpty ? "-" : aplData.id_izin
- modernIdIzinLabel.text = "ID Izin: \(idIzinStr)"
- lblIdIzin?.text = idIzinStr
-
- let statusStr = aplData.StatusStr.trimmingCharacters(in: .whitespacesAndNewlines)
- modernStatusLabel.text = statusStr.isEmpty ? "PROSES" : statusStr.uppercased()
-
- if let statusColor = UIColor(hexString: aplData.StatusBgColor) {
- modernStatusPill.backgroundColor = statusColor.withAlphaComponent(0.15)
- modernStatusLabel.textColor = statusColor
- } else {
- modernStatusPill.backgroundColor = UIColor(red: 235/255, green: 242/255, blue: 252/255, alpha: 1.0)
- modernStatusLabel.textColor = UIColor.siraniPrimaryBlue
- }
-
- modernEqpLabel.text = aplData.eqp_name.isEmpty ? "-" : aplData.eqp_name
- modernCustLabel.text = aplData.cust_name.isEmpty ? "-" : aplData.cust_name
-
- var mm = aplData.merk
- if !aplData.model.isEmpty && aplData.model != "null" {
- mm = mm.isEmpty ? aplData.model : "\(mm) • \(aplData.model)"
- }
- modernMerkVal.text = mm.isEmpty ? "-" : mm
-
- modernResiVal.text = aplData.resi_no.isEmpty ? "-" : aplData.resi_no
-
- let roleId = UserState.instance.getRoleId()
- if roleId == "17" || aplData.disposisi.isEmpty {
- let resiDate = CommonHelper.formatDate(from: aplData.resi_date)
- modernDisposisiLabel.text = resiDate == "-" ? "Permohonan Layanan DJID" : "Tanggal Resi: \(resiDate)"
- } else {
- modernDisposisiLabel.text = "Disposisi: \(aplData.disposisi)"
- }
-
- self.dataAplikasi = aplData
- }
-
- @objc func onItemTap(sender: UITapGestureRecognizer) {
- cellDelegate?.openAplikasiDetail(data: dataAplikasi)
- }
- }
|