<?php
/**
 * Copyright (C) 2007,2008  Arie Nugraha (dicarve@yahoo.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

use SLiMS\Plugins;

/* Membership Management section */
use SLiMS\Filesystems\Storage;
use SLiMS\Form\FormAjaxWithCustomField;

// key to authenticate
define('INDEX_AUTH', '1');
// key to get full database access
define('DB_ACCESS', 'fa');

if (!defined('SB')) {
    // main system configuration
    require '../../../sysconfig.inc.php';
    // start the session
    require SB.'admin/default/session.inc.php';
}
// IP based access limitation
require LIB.'ip_based_access.inc.php';
do_checkIP('smc');
do_checkIP('smc-membership');

require SB.'admin/default/session_check.inc.php';
require SIMBIO.'simbio_GUI/table/simbio_table.inc.php';
require SIMBIO.'simbio_GUI/form_maker/simbio_form_table_AJAX.inc.php';
require SIMBIO.'simbio_GUI/paging/simbio_paging.inc.php';
require SIMBIO.'simbio_DB/datagrid/simbio_dbgrid.inc.php';
require SIMBIO.'simbio_DB/simbio_dbop.inc.php';
require SIMBIO.'simbio_UTILS/simbio_date.inc.php';
require SIMBIO.'simbio_FILE/simbio_file_upload.inc.php';

// privileges checking
$can_read = utility::havePrivilege('membership', 'r');
$can_write = utility::havePrivilege('membership', 'w');

if (!$can_read) {
    die('<div class="errorBox">You dont have enough privileges to view this section</div>');
}
# CHECK ACCESS
if ($_SESSION['uid'] != 1) {
    if (!utility::haveAccess('membership.view-member-list')) {
        die('<div class="errorBox">' . __('You are not authorized to view this section') . '</div>');
    }
}

// ========== TAMBAHAN MULTI-LIBRARY ==========
$current_library_id = isset($_SESSION['library_id']) ? (int)$_SESSION['library_id'] : 1;
$is_superadmin = isset($_SESSION['uid']) && $_SESSION['uid'] == 1;
// ========== AKHIR TAMBAHAN ==========

// execute registered hook
Plugins::getInstance()->execute(Plugins::MEMBERSHIP_INIT);

/* Just In Case for PHP < 5.4 */
/* Taken From imageman (http://www.php.net/manual/en/function.getimagesizefromstring.php#113976) */
/* Make sure to set allow_url_fopen = on inside your php.ini */
if (version_compare(phpversion(), '5.4', '<')) {
    function getimagesizefromstring($string_data)
    {
        $uri = 'data://application/octet-stream;base64,'  . base64_encode($string_data);
        return getimagesize($uri);
    }
}

/* ============================================================
   PROSES ACTIVATE / DEACTIVATE MEMBER TYPE
   ============================================================ */
if (isset($_GET['set_active']) && isset($_GET['itemID'])) {
    $rel_id = (int)$_GET['set_active'];
    $member_id = $dbs->escape_string(trim($_GET['itemID']));
    
    // Get current status
    $rel_q = $dbs->query("SELECT is_active, library_id FROM member_member_type WHERE id = $rel_id AND member_id = '$member_id'");
    if ($rel_q->num_rows > 0) {
        $rel_d = $rel_q->fetch_assoc();
        $new_status = $rel_d['is_active'] == 1 ? 0 : 1;
        
        if ($new_status == 1) {
            // Nonaktifkan yang lain di library yang sama
            $dbs->query("UPDATE member_member_type SET is_active = 0 
                WHERE member_id = '$member_id' AND library_id = " . (int)$rel_d['library_id']);
        }
        
        $dbs->query("UPDATE member_member_type SET is_active = $new_status WHERE id = $rel_id");
        
        if ($new_status == 1) {
            utility::jsToastr('Member Type', __('Member type activated'), 'success');
        } else {
            utility::jsToastr('Member Type', __('Member type deactivated'), 'warning');
        }
        
        echo '<script type="text/javascript">';
        echo 'parent.$("#mainContent").simbioAJAX("' . MWB . 'membership/index.php?action=detail&itemID=' . $member_id . '");';
        echo '</script>';
        exit;
    }
}

/* REMOVE IMAGE */
if (isset($_POST['removeImage']) && isset($_POST['mimg']) && isset($_POST['img'])) {
    // validate post image
    $member_id = utility::filterData('mimg', 'post', true, true, true);
    $image_name = utility::filterData('img', 'post', true, true, true);
    $query_image = $dbs->query("SELECT member_id FROM member WHERE member_id='{$member_id}' AND member_image='{$image_name}'");
    if (!empty($query_image->num_rows)) {
        $_delete = $dbs->query(sprintf("UPDATE member SET member_image=NULL WHERE member_id='%s'", $member_id));
        if ($_delete) {
            $image = Storage::images();
            $postImage = stripslashes($_POST['img']);
            $postImage = str_replace('/', '', $postImage);
            $imagePath = sprintf('persons/%s', $postImage);
            if (!empty($postImage) && $image->isExists($imagePath)) {
                @Storage::images()->delete($imagePath);
            }
            $safePostImage = htmlspecialchars($postImage, ENT_QUOTES, 'UTF-8');
            exit('<script type="text/javascript">alert(\''.str_replace('{imageFilename}', $safePostImage, __('{imageFilename} successfully removed!')).'\'); $(\'#memberImage, #imageFilename\').remove();</script>');
        }
    }
    exit();
}

/* member update process */
if (isset($_POST['saveData']) && $can_read && $can_write) {
    // check form validity
    $memberID = trim($_POST['memberID']);
    $memberName = trim($_POST['memberName']);
    $birthDate = trim($_POST['birthDate']);
    $mpasswd1 = trim($_POST['memberPasswd']);
    $mpasswd2 = trim($_POST['memberPasswd2']);
    if (empty($memberID) OR empty($memberName) OR empty($birthDate)) {
        toastr(__('Member ID, Name and Birthday cannot be empty'))->error(); //mfc
        exit();
    } else if (($mpasswd1 OR $mpasswd2) && ($mpasswd1 !== $mpasswd2)) {
        toastr(__('Password confirmation does not match. See if your Caps Lock key is on!'))->error();
        exit();
    } else if ($sysconf['password_policy_strong'] && ($mpasswd1 AND $mpasswd2) && ($mpasswd1 === $mpasswd2) && !simbio_security::validatePassword($mpasswd2, $sysconf['password_policy_min_length'])) {
        toastr(__( sprintf('Password should at least %d characters long, contains one capital letter, one number, and one non-alphanumeric character !', $sysconf['password_policy_min_length']) ))->error();
        exit();
    } else {
        // include custom fields file
        if (file_exists(MDLBS.'membership/member_custom_fields.inc.php')) {
            include MDLBS.'membership/member_custom_fields.inc.php';
        }
        /**
         * Custom fields
         */
        if (isset($member_custom_fields)) {
            if (is_array($member_custom_fields) && $member_custom_fields) {
                foreach ($member_custom_fields as $fid => $cfield) {
                // custom field data
                $cf_dbfield = $cfield['dbfield'];
                if (isset($_POST[$cf_dbfield])) {
                    if(is_array($_POST[$cf_dbfield])){ 
                    foreach ($_POST[$cf_dbfield] as $value) {
                        $arr[$value] = $value;
                    }
                    $custom_data[$cf_dbfield] = serialize($arr);
                    }
                    else{
                    $cf_val = $dbs->escape_string(strip_tags(trim($_POST[$cf_dbfield]), $sysconf['content']['allowable_tags']));
                    if($cfield['type'] == 'numeric' && (!is_numeric($cf_val) && $cf_val!='')){
                        toastr(sprintf(__('Field %s only number for allowed'),$cfield['label']))->error(__('Membership'));      
                        exit();        
                    }
                    elseif ($cfield['type'] == 'date' && $cf_val == '') {
                        toastr(sprintf(__('Field %s is date format, empty not allowed'),$cfield['label']))->error(__('Membership'));      
                        exit();
                    }
                    $custom_data[$cf_dbfield] = $cf_val;
                    }
                }else{
                    $custom_data[$cf_dbfield] = serialize(array());
                }
                }
            }
        }

        // Register advance custom field data
        Plugins::getInstance()->execute(Plugins::MEMBERSHIP_CUSTOM_FIELD_DATA, ['custom_data' => &$custom_data]);

        $data['member_id'] = $dbs->escape_string($memberID);
        $data['member_name'] = $dbs->escape_string($memberName);
        $data['member_type_id'] = (integer)$_POST['memberTypeID'];
        $data['inst_name'] = trim($dbs->escape_string(strip_tags($_POST['instName'])));
        $data['gender'] = trim($dbs->escape_string(strip_tags($_POST['gender'])));
        $data['birth_date'] = trim($dbs->escape_string(strip_tags($_POST['birthDate'])));
        $data['birth_date'] = $data['birth_date'] == '' ? null : $data['birth_date'];
        $data['register_date'] = trim($dbs->escape_string(strip_tags($_POST['regDate'])));
        // member since date
        $member_since = trim($dbs->escape_string(strip_tags($_POST['sinceDate'])));
        if (isset($_POST['updateRecordID'])) {
            $data['member_since_date'] = $member_since;
        } else {
            if ($member_since) {
                $data['member_since_date'] = $member_since;
            } else {
                $data['member_since_date'] = $data['register_date'];
            }
        }

        $data['expire_date'] = trim($dbs->escape_string(strip_tags($_POST['expDate'])));
        // extending membership
        if (isset($_POST['extend']) AND !empty($_POST['extend'])) {
            // get membership periode from database
            $mtype_query = $dbs->query("SELECT member_periode FROM mst_member_type WHERE member_type_id=".$data['member_type_id']);
            $mtype_data = $mtype_query->fetch_row();
            $data['register_date'] = date('Y-m-d');
            $data['expire_date'] = simbio_date::getNextDate($mtype_data[0], $data['register_date']);
        }
        $data['pin'] = trim($dbs->escape_string(strip_tags($_POST['memberPIN'])));
        $data['member_address'] = trim($dbs->escape_string(strip_tags($_POST['memberAddress'])));
        $data['member_mail_address'] = trim($dbs->escape_string(strip_tags($_POST['memberMailAddress'])));
        $data['member_phone'] = trim($dbs->escape_string(strip_tags($_POST['memberPhone'])));
        $data['member_fax'] = trim($dbs->escape_string(strip_tags($_POST['memberFax'])));
        $data['postal_code'] = trim($dbs->escape_string(strip_tags($_POST['memberPostal'])));
        $data['member_notes'] = trim($dbs->escape_string(strip_tags($_POST['memberNotes'])));
        $data['member_email'] = trim($dbs->escape_string(strip_tags($_POST['memberEmail'])));
        $data['is_pending'] = isset($_POST['isPending'])? intval($_POST['isPending']) : '0';
        $data['input_date'] = date('Y-m-d');
        $data['last_update'] = date('Y-m-d');
        
        // ========== TAMBAHAN: library_id ==========
        if ($is_superadmin && isset($_POST['library_id']) && $_POST['library_id'] > 0) {
            $data['library_id'] = (int)$_POST['library_id'];
        } else {
            $data['library_id'] = $current_library_id;
        }
        // ========== AKHIR TAMBAHAN ==========
        
        $imageDisk = Storage::images();
        $base64_data = null;
        $file_extension = 'jpg';
        if (!empty($_POST['base64picstring'])) {
            $base64_full_string = $_POST['base64picstring'];
            if (strpos($base64_full_string, 'base64,') !== false) {
                list($mime, $data_string) = explode(';', $base64_full_string);
                list(, $base64_data) = explode(',', $data_string);
            }
            elseif (strpos($base64_full_string, '#image/type#') !== false) {
                list($base64_data_raw, $file_extension) = explode('#image/type#', $base64_full_string);
                $base64_data = $base64_data_raw;
                $file_extension = strtolower(trim($file_extension));
            }

        }
        if (!empty($base64_data)) {
            $base64_data = trim($base64_data);
            $filedata = base64_decode($base64_data, true);
            $fileinfo = $filedata !== false ? @getimagesizefromstring($filedata) : false;
            $fileMime = $fileinfo ? ($fileinfo['mime'] ?? '') : '';
            $file_extension = $fileinfo ? ltrim(strtolower(image_type_to_extension($fileinfo[2], false)), '.') : strtolower($file_extension);
            $file_extension = $fileMime && !$file_extension && strpos($fileMime, 'image/') === 0 ? substr($fileMime, 6) : $file_extension;
            $fileMimeLower = strtolower($fileMime);

            $fileSizeOkay = $filedata !== false && (strlen($filedata) <= ($sysconf['max_image_upload'] * 1024));
            $allowedMimes = array_map('strtolower', (array)$sysconf['allowed_images_mimetype']);
            $allowedExts = array_map('strtolower', (array)$sysconf['allowed_images']);
            $mimeAllowed = $fileMime && in_array($fileMimeLower, $allowedMimes, true);
            $extAllowed = $file_extension && in_array($file_extension, $allowedExts, true);
            $valid = $fileinfo && $fileSizeOkay && $mimeAllowed && $mimeAllowed;
            $new_filename = 'member_'.$data['member_id'].'.'.$file_extension;
            if ($valid) {
                $imageDisk->put('persons/'.$new_filename, $filedata);
                if ($imageDisk->isExists('persons/'.$new_filename)) {
                    if (isset($_POST['old_member_image']) && !empty($_POST['old_member_image']) && $_POST['old_member_image'] != $new_filename) {
                        @$imageDisk->delete('persons/'.$_POST['old_member_image']);
                    }
                    
                    $data['member_image'] = $dbs->escape_string($new_filename);
                    if (!defined('UPLOAD_SUCCESS')) define('UPLOAD_SUCCESS', 1);
                    $upload_status = UPLOAD_SUCCESS;
                }
            } else {
                utility::jsToastr('Membership', __('Image Uploaded Failed').'<br/>'.__('Cropped image data is invalid or exceeds max size.'), 'error');
            }

        }
        elseif (!empty($_FILES['image']) AND $_FILES['image']['size']) {
            // create upload object
            $upload = $imageDisk->upload('image', function($image) use($sysconf) {
                // Extension check
                $image->isExtensionAllowed($sysconf['allowed_images']);
                // File size check
                $image->isLimitExceeded($sysconf['max_image_upload']*1024);
                // Is it really image file
                $image->isImageFile();
                // destroy it if failed
                if (!empty($image->getError())) $image->destroyIfFailed();
                // remove exif from image
                if (empty($image->getError())) $image->cleanExifInfo();
            })->as('persons/' . 'member_'.$data['member_id']);
            if ($upload->getUploadStatus()) {
                $data['member_image'] = $dbs->escape_string($upload->getUploadedFileName());
                if (!defined('UPLOAD_SUCCESS')) define('UPLOAD_SUCCESS', 1);
                $upload_status = UPLOAD_SUCCESS;
            } else {
                // write log
                writeLog('staff', $_SESSION['uid'], 'membership', 'ERROR : ' . $_SESSION['realname'] . ' FAILED TO upload image file ' . $upload->getUploadedFileName() . ', with error (' . $upload->getError() . ')', 'Member Image', 'Upload failed');
                utility::jsToastr('Membership', __('Image Uploaded Failed').'<br/>'.$upload->getError(), 'error');
            }
        }
        // password confirmation
        if (($mpasswd1 AND $mpasswd2) AND ($mpasswd1 === $mpasswd2)) {
            // $data['mpasswd'] = 'literal{MD5(\''.$mpasswd2.'\')}';
            $data['mpasswd'] = password_hash($mpasswd2, PASSWORD_BCRYPT);
        }

        // create sql op object
        $sql_op = new simbio_dbop($dbs);
        if (isset($_POST['updateRecordID'])) {
            /* UPDATE RECORD MODE */
            // remove input date
            unset($data['input_date']);
            // filter update record ID
            $updateRecordID = $dbs->escape_string(trim($_POST['updateRecordID']));
            $old_member_ID = $updateRecordID;
            // execute registered hook
            Plugins::getInstance()->execute(Plugins::MEMBERSHIP_BEFORE_UPDATE, ['data' => $data]);
            // update the data
            $update = $sql_op->update('member', $data, "member_id='$updateRecordID'");
            if ($update) {
                
                // ========== TAMBAHAN: Update relasi member_member_type ==========
                $member_id = $updateRecordID;
                $member_type_id = (int)$_POST['memberTypeID'];
                $library_id = $data['library_id'];
                
                // Nonaktifkan semua relasi di library ini
                $dbs->query("UPDATE member_member_type SET is_active=0 
                    WHERE member_id='$member_id' AND library_id=$library_id");
                
                // Aktifkan member_type yang dipilih
                $check_rel = $dbs->query("SELECT id FROM member_member_type 
                    WHERE member_id='$member_id' AND member_type_id=$member_type_id AND library_id=$library_id");
                if ($check_rel->num_rows > 0) {
                    $dbs->query("UPDATE member_member_type SET is_active=1 
                        WHERE member_id='$member_id' AND member_type_id=$member_type_id AND library_id=$library_id");
                } else {
                    $dbs->query("INSERT INTO member_member_type (member_id, member_type_id, library_id, is_active) 
                        VALUES ('$member_id', $member_type_id, $library_id, 1)");
                }
                // ========== AKHIR TAMBAHAN ==========

                // execute registered hook
                Plugins::getInstance()->execute(Plugins::MEMBERSHIP_AFTER_UPDATE, ['data' => api::member_load($dbs, $updateRecordID)]);
                // update custom data
                if (isset($custom_data)) {
                    // check if custom data for this record exists
                    $_sql_check_custom_q = sprintf("SELECT member_id FROM member_custom WHERE member_id='%s'", $updateRecordID);
                    $check_custom_q = $dbs->query($_sql_check_custom_q);
                    if ($check_custom_q->num_rows) {
                        @$sql_op->update('member_custom', $custom_data, 'member_id=\''.$updateRecordID.'\'');
                    } else {
                        $custom_data['member_id'] = $updateRecordID;
                        @$sql_op->insert('member_custom', $custom_data);
                    }
                }
                // update other tables contain this member ID
                @$dbs->query('UPDATE loan SET member_id=\''.$data['member_id'].'\' WHERE member_id=\''.$old_member_ID.'\'');
                // update loan_history to match loan table changes (replaces update_loan_history trigger)
                @$dbs->query('UPDATE loan_history SET member_id=\''.$data['member_id'].'\' WHERE member_id=\''.$old_member_ID.'\'');
                @$dbs->query('UPDATE fines SET member_id=\''.$data['member_id'].'\' WHERE member_id=\''.$old_member_ID.'\'');
                toastr(__('Member Data Successfully Updated'))->success();
                // upload status alert
                if (isset($upload_status)) {
                    if ($upload_status == UPLOAD_SUCCESS) {
                        // write log
                        writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' upload image file '.$data['member_image'], 'Member Image', 'Upload success');
                        toastr(__('Image Uploaded Successfully'))->success();
                    } else {
                        // write log
                        writeLog('staff', $_SESSION['uid'], 'membership', 'ERROR : '.$_SESSION['realname'].' FAILED TO upload image file '.$data['member_image'].', with error ('.$upload->error.')', 'Member Image', 'Upload failed');
                        toastr(__('Image FAILED to upload'))->error();
                    }
                }
                // write log
                writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' update member data ('.$memberName.') with ID ('.$memberID.')', 'Member Profile', 'Profile updated');
                if ($sysconf['webcam'] == 'html5') {
                    echo '<script type="text/javascript">parent.$(\'#mainContent\').simbioAJAX(\''.MWB.'membership/index.php\');</script>';
                } else {
                    echo '<script type="text/javascript">parent.$(\'#mainContent\').simbioAJAX(\''.MWB.'membership/index.php\');</script>';
                }
            } else { toastr(__('Member Data FAILED to Save/Update. Please Contact System Administrator')."\nDEBUG : ".$sql_op->error)->error(); }
            exit();
        } else {
            /* INSERT RECORD MODE */
            if (!$mpasswd1 AND !$mpasswd2) { $data['mpasswd'] = 'literal{NULL}'; }
            // insert the data
            $insert = $sql_op->insert('member', $data);
            if ($insert) {
                
                // ========== TAMBAHAN: Simpan relasi member_member_type ==========
                $member_id = $data['member_id'];
                $member_type_id = (int)$_POST['memberTypeID'];
                $library_id = $data['library_id'];
                
                // Cek apakah sudah ada relasi untuk library ini
                $check_rel = $dbs->query("SELECT id FROM member_member_type 
                    WHERE member_id='$member_id' AND library_id=$library_id");
                if ($check_rel->num_rows > 0) {
                    // Update existing
                    $dbs->query("UPDATE member_member_type SET member_type_id=$member_type_id, is_active=1 
                        WHERE member_id='$member_id' AND library_id=$library_id");
                } else {
                    // Insert new
                    $dbs->query("INSERT INTO member_member_type (member_id, member_type_id, library_id, is_active) 
                        VALUES ('$member_id', $member_type_id, $library_id, 1)");
                }
                // ========== AKHIR TAMBAHAN ==========

                // insert custom data
                if (isset($custom_data)) {
                  $custom_data['member_id'] = $data['member_id'];
                  @$sql_op->insert('member_custom', $custom_data);
                }

                Plugins::getInstance()->execute(Plugins::MEMBERSHIP_AFTER_SAVE, ['data' => api::member_load($dbs, $data['member_id'])]);

                toastr(__('New Member Data Successfully Saved'))->success();
                // upload status alert
                if (isset($upload_status)) {
                    if ($upload_status == UPLOAD_SUCCESS) {
                        // write log
                        if (isset($upload)) {
                            writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' upload image file '.$upload->new_filename, 'Member Image', 'Upload success');
                        } else {
                            writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' taken image photo ('.$memberName.') with ID ('.$memberID.')', 'Member Image', 'Take Photo');
                        }
                        toastr(__('Image Uploaded Successfully'))->success();
                    } else {
                        // write log
                        writeLog('staff', $_SESSION['uid'], 'membership', 'ERROR : '.$_SESSION['realname'].' FAILED TO upload image file '.$upload->new_filename.', with error ('.$upload->error.')', 'member Image', 'Upload failed');
                        toastr(__('Image FAILED to upload'))->error();
                    }
                }
                // write log
                writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' add new member ('.$memberName.') with ID ('.$memberID.')', 'Member Profile', 'Profile added');
                echo '<script type="text/javascript">parent.$(\'#mainContent\').simbioAJAX(\''.$_SERVER['PHP_SELF'].'\');</script>';
            } else { toastr(__('Member Data FAILED to Save/Update. Please Contact System Administrator')."\nDEBUG : ".$sql_op->error)->danger(); }
            exit();
        }
    }
    exit();
} else if (isset($_POST['batchExtend']) && $can_read && $can_write) {
    /* BATCH extend membership proccessing */
    $curr_date = date('Y-m-d');
    $num_extended = 0;
    foreach ($_POST['itemID'] as $itemID) {
        $memberID = $dbs->escape_string(trim($itemID));
        // get membership periode from database
        $mtype_q = $dbs->query('SELECT member_periode, m.member_name FROM member AS m
            LEFT JOIN mst_member_type AS mt ON m.member_type_id=mt.member_type_id
            WHERE m.member_id=\''.$memberID.'\'');
        $mtype_d = $mtype_q->fetch_row();
        $expire_date = simbio_date::getNextDate($mtype_d[0], $curr_date);
        @$dbs->query('UPDATE member SET register_date=\''.date("Y-m-d").'\',  expire_date=\''.$expire_date.'\', last_update=\''.date("y-m-d").'\' WHERE member_id=\''.$memberID.'\'');
        // write log
        writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' extends membership for member ('.$mtype_d[1].') with ID ('.$memberID.')', 'Member Profile', 'Extend Membership');
        $num_extended++;
    }
    header('Location: '.MWB.'membership/index.php?expire=true&numExtended='.$num_extended);
    exit();
} else if (isset($_POST['itemID']) AND !empty($_POST['itemID']) AND isset($_POST['itemAction'])) {
    if (!($can_read AND $can_write)) {
        die();
    }
    /* DATA DELETION PROCESS */
    $sql_op = new simbio_dbop($dbs);
    $failed_array = array();
    $error_num = 0;
    $still_have_loan = array();
    if (!is_array($_POST['itemID'])) {
        // make an array
        $_POST['itemID'] = array($dbs->escape_string(trim($_POST['itemID'])));
    }
    // loop array
    foreach ($_POST['itemID'] as $itemID) {
        $itemID = $dbs->escape_string(trim($itemID));
        // check if the member still have loan
        $loan_q = $dbs->query('SELECT DISTINCT m.member_id, m.member_name, COUNT(l.loan_id) FROM member AS m
            LEFT JOIN loan AS l ON (m.member_id=l.member_id AND l.is_lent=1 AND l.is_return=0)
            WHERE m.member_id=\''.$itemID.'\' GROUP BY m.member_id');
        $loan_d = $loan_q->fetch_row();
        if ($loan_d[2] < 1) {
            if (!$sql_op->delete('member', "member_id='$itemID'")) {
                $error_num++;
            } else {
                // delete custom data
                $sql_op->delete('member_custom', "member_id='$itemID'");
                // write log
                writeLog('staff', $_SESSION['uid'], 'membership', $_SESSION['realname'].' DELETE member data ('.$loan_d[1].') with ID ('.$loan_d[0].')', 'Member Profile', 'Profile deleted');
            }
        } else {
            $safe_member_name = htmlspecialchars($loan_d[1]);
            $still_have_loan[] = $loan_d[0].' - '.$safe_member_name;
            $error_num++;
        }
    }

    if ($still_have_loan) {
        $members = '';
        foreach ($still_have_loan as $mbr) {
            $members .= $mbr."\n";
        }
        toastr(__('Below member data can\'t be deleted because still have unreturned item(s)').' : '."\n".$members)->error();
        exit();
    }
    // error alerting
    if ($error_num == 0) {
        toastr(__('All Data Successfully Deleted'))->success();
        echo '<script type="text/javascript">parent.$(\'#mainContent\').simbioAJAX(\''.$_SERVER['PHP_SELF'].'?'.($_POST['lastQueryStr']??'').'\');</script>';
    } else {
        toastr(__('Some or All Data NOT deleted successfully!\nPlease contact system administrator'))->error();
        echo '<script type="text/javascript">parent.$(\'#mainContent\').simbioAJAX(\''.$_SERVER['PHP_SELF'].'?'.$_POST['lastQueryStr'].'\');</script>';
    }
    exit();
}
/* RECORD OPERATION END */

/* search form */
$page_title = __('Membership');
if(isset($_GET['expire'])) {
    $page_title = __('Expired Member List');
}
?>
<div class="menuBox">
<div class="menuBoxInner memberIcon">
    <div class="per_title">
        <h2><?php echo $page_title; ?></h2>
    </div>
    <div class="sub_section">
    <div class="btn-group">
    <a href="<?php echo MWB; ?>membership/index.php" class="btn btn-default"><?php echo __('Member List'); ?></a>
    <a href="<?php echo MWB; ?>membership/index.php?action=detail" class="btn btn-default"><?php echo __('Add New Member'); ?></a>
    <a href="<?php echo MWB; ?>membership/index.php?expire=true" class="btn btn-danger"><?php echo __('View Expired Member'); ?></a>
    </div>
    <form name="search" action="<?php echo MWB; ?>membership/index.php" id="search" method="get" class="form-inline"><?php echo __('Search'); ?>
        <input type="text" name="keywords" class="form-control col-md-3" /><?php if (isset($_GET['expire'])) { echo '<input type="hidden" name="expire" value="true" />'; } ?>
        <input type="submit" id="doSearch" value="<?php echo __('Search'); ?>" class="s-btn btn btn-default" />
    </form>
    </div>
</div>
</div>
<?php
/* search form end */
/* main content */
if (isset($_POST['detail']) OR (isset($_GET['action']) AND $_GET['action'] == 'detail')) {
    if (!($can_read AND $can_write)) {
        die('<div class="errorBox">'.__('You don\'t have enough privileges to view this section').'</div>');
    }
    # CHECK ACCESS
    if ($_SESSION['uid'] != 1) {
        if (!utility::haveAccess('membership.add-new-member')) {
            die('<div class="errorBox">' . __('You are not authorized to view this section') . '</div>');
        }
    }
    /* RECORD FORM */
    $itemID = $dbs->escape_string(trim(isset($_POST['itemID'])?$_POST['itemID']:'0'));
    $rec_d = [];
    if (!empty($itemID))
    {
      $rec_q = $dbs->query("SELECT * FROM member WHERE member_id='$itemID'");
      $rec_d = $rec_q->fetch_assoc();
    }
    // ========== TAMBAHAN: Ambil data relasi member_member_type ==========
    $active_member_types = array();
    if (!empty($itemID)) {
        $rel_q = $dbs->query("SELECT member_type_id, library_id FROM member_member_type 
            WHERE member_id='$itemID' AND is_active=1");
        while ($rel_d = $rel_q->fetch_assoc()) {
            $active_member_types[$rel_d['library_id']] = $rel_d['member_type_id'];
        }
    }
    // ========== AKHIR TAMBAHAN ==========
    // create new instance
    $form = new FormAjaxWithCustomField('mainForm', $_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING'], 'post');
    $form->submit_button_attr = 'name="saveData" value="'.__('Save').'" class="s-btn btn btn-default"';

    // form table attributes
    $form->table_attr = 'id="dataList" class="s-table table"';
    $form->table_header_attr = 'class="alterCell font-weight-bold"';
    $form->table_content_attr = 'class="alterCell2"';

    // edit mode flag set
    if (!empty($itemID) && $rec_q->num_rows > 0) {
        $form->edit_mode = true;
        // record ID for delete process
        $form->record_id = $itemID;
        // form record title
        $form->record_title = $rec_d['member_name'];
        // submit button attribute
        $form->submit_button_attr = 'name="saveData" value="'.__('Update').'" class="s-btn btn btn-primary"';

        // custom field data query
        $_sql_rec_cust_q = sprintf("SELECT * FROM member_custom WHERE member_id='%s'", $itemID);
        $rec_cust_q = $dbs->query($_sql_rec_cust_q);
        $rec_cust_d = $rec_cust_q->fetch_assoc();

    }

    /* Form Element(s) */
    if ($form->edit_mode) {
        // check if member expired
        $curr_date = date('Y-m-d');
        $compared_date = simbio_date::compareDates($rec_d['expire_date'], $curr_date);
        $is_expired = ($compared_date == $curr_date);
        $expired_message = '';
        if ($is_expired) {
            // extend membership
            $chbox_array[] = array('1', __('Extend'));
            $form->addCheckBox('extend', __('Extend Membership'), $chbox_array);
            $expired_message = '<strong class="text-danger">('.__('Membership Already Expired').')</strong>';
        }
        $form->addHidden('old_member_image', $rec_d['member_image'] ?? ''); 
    }

    // include custom fields file
    if (file_exists(MDLBS.'membership/member_custom_fields.inc.php')) {
        include MDLBS.'membership/member_custom_fields.inc.php';
    }

    // member code
    $str_input  = '<div class="container-fluid">';
    $str_input .= '<div class="row">';
    $str_input .= simbio_form_element::textField('text', 'memberID', $rec_d['member_id']??'', 'id="memberID" onblur="ajaxCheckID(\''.SWB.'admin/AJAX_check_id.php\', \'member\', \'member_id\', \'msgBox\', \'memberID\')" class="form-control col-4"');
    $str_input .= '<div id="msgBox" class="col mt-2"></div>';
    $str_input .= '</div>';
    $str_input .= '</div>';
    $form->addAnything(__('Member ID').'*', $str_input);
    // member name
    $form->addTextField('text', 'memberName', __('Member Name').'*', $rec_d['member_name']??'', 'class="form-control" style="width: 50%;"');
    // member birth date
    $form->addDateField('birthDate', __('Birth Date').'*', $rec_d['birth_date']??'','class="form-control"');
    // member since date
    $form->addDateField('sinceDate', __('Member Since').'*', $rec_d['member_since_date']??date('Y-m-d'),'class="form-control"');
    // member register date
    $form->addDateField('regDate', __('Register Date').'*', $rec_d['register_date']??date('Y-m-d'),'class="form-control"');
    // member expire date
    if ($form->edit_mode) {
        $form->addDateField('expDate', __('Expiry Date').'*', $rec_d['expire_date']??'','class="form-control"');
    } else {
        $chbox_array[] = array('1', __('Auto Set'));
        $str_input = '<div>'.simbio_form_element::checkBox('extend', $chbox_array, '1').'</div>';
        $str_input .= '<div>'.simbio_form_element::dateField('expDate', $rec_d['expire_date']??'', 'class="form-control"').'</div>';
        $form->addAnything(__('Expiry Date').'*', $str_input);
    }
    // member institution
    $form->addTextField('text', 'instName', __('Institution'), $rec_d['inst_name']??'', 'class="form-control" style="width: 100%;"');

    // ============================================================
    // ========== MEMBER TYPE (DITEMPATKAN BERDEKATAN) ==========
    // ============================================================

    // Tentukan library yang dipilih (dari session atau dari data member)
    $selected_library_id = $is_superadmin ? 
        (isset($_GET['library_id']) ? (int)$_GET['library_id'] : $current_library_id) : 
        $current_library_id;

    // Jika edit mode, ambil library dari data member
    if (!empty($itemID) && isset($rec_d['library_id']) && $rec_d['library_id'] > 0) {
        $selected_library_id = $rec_d['library_id'];
    }

    // ============================================================
    // BAGIAN 1: LIBRARY SELECTION & MAIN MEMBERSHIP TYPE (BERDEKATAN)
    // ============================================================
    if ($is_superadmin) {
        // Superadmin: bisa pilih library
        $lib_options = array();
        $lib_query = "SELECT library_id, library_code, library_name FROM mst_library WHERE is_active = 1 ORDER BY library_code ASC";
        $lib_result = $dbs->query($lib_query);
        while ($lib_row = $lib_result->fetch_assoc()) {
            $lib_options[] = array($lib_row['library_id'], $lib_row['library_code'] . ' - ' . $lib_row['library_name']);
        }

        $str_html = '<div class="card card-primary" style="border-color: #007bff; margin-bottom:15px;">';
        $str_html .= '<div class="card-header" style="background-color: #007bff; color: white; font-weight: bold;">';
        $str_html .= '<i class="fa fa-cog"></i> ' . __('Pengaturan Membership Type');
        $str_html .= '</div>';
        $str_html .= '<div class="card-body">';

        $str_html .= '<div class="form-group row">';
        $str_html .= '<label class="col-sm-3 col-form-label font-weight-bold">' . __('Library') . '</label>';
        $str_html .= '<div class="col-sm-9">';
        $str_html .= simbio_form_element::selectList('library_id', $lib_options, $selected_library_id, 
                        'class="form-control" id="library_id" onchange="filterMemberType(this.value)"');
        $str_html .= '<small class="form-text text-muted">' . __('Pilih perpustakaan untuk menentukan member type yang tersedia.') . '</small>';
        $str_html .= '</div></div>';

        // JS untuk filter
        $str_html .= '
        <script>
        function filterMemberType(libraryId) {
            var currentUrl = window.location.href.split("?")[0];
            var params = new URLSearchParams(window.location.search);
            params.set("library_id", libraryId);
            window.location.href = currentUrl + "?" + params.toString();
        }
        </script>';

    } else {
        // Non-superadmin: tampilkan info library
        $str_html = '<div class="card card-primary" style="border-color: #007bff; margin-bottom:15px;">';
        $str_html .= '<div class="card-header" style="background-color: #007bff; color: white; font-weight: bold;">';
        $str_html .= '<i class="fa fa-cog"></i> ' . __('Pengaturan Membership Type');
        $str_html .= '</div>';
        $str_html .= '<div class="card-body">';

        $lib_query = "SELECT library_code, library_name FROM mst_library WHERE library_id = " . $selected_library_id;
        $lib_result = $dbs->query($lib_query);
        if ($lib_result && $lib_data = $lib_result->fetch_assoc()) {
            $str_html .= '<div class="alert alert-info">';
            $str_html .= '<i class="fa fa-info-circle"></i> ' . 
                        __('Perpustakaan') . ': <strong>' . $lib_data['library_code'] . ' - ' . $lib_data['library_name'] . '</strong>';
            $str_html .= '</div>';
        } else {
            $str_html .= '<div class="alert alert-warning">';
            $str_html .= '<i class="fa fa-warning"></i> ' . __('Perpustakaan tidak terdeteksi.');
            $str_html .= '</div>';
        }
    }

    // ============================================================
    // MAIN MEMBERSHIP TYPE (DROPDOWN) - LANGSUNG DI BAWAH LIBRARY
    // ============================================================
    $str_html .= '<div class="form-group row">';
    $str_html .= '<label class="col-sm-3 col-form-label font-weight-bold">' . __('Main Membership Type') . ' <span class="text-danger">*</span></label>';
    $str_html .= '<div class="col-sm-9">';

    // Query member type berdasarkan library yang dipilih
    $mtype_query = $dbs->query("SELECT member_type_id, member_type_name 
        FROM mst_member_type 
        WHERE library_id = " . (int)$selected_library_id . " 
        ORDER BY member_type_name ASC");

    $mtype_options = array();
    while ($mtype_data = $mtype_query->fetch_row()) {
        $mtype_options[] = array($mtype_data[0], $mtype_data[1]);
    }

    if (empty($mtype_options)) {
        $mtype_options[] = array('', __('Tidak ada member type untuk perpustakaan ini'));
    }

    $current_selected = $rec_d['member_type_id'] ?? '';
    $str_html .= simbio_form_element::selectList('memberTypeID', $mtype_options, $current_selected, 
                    'class="form-control" id="memberTypeID"');
    $str_html .= '<small class="form-text text-muted">' . __('Pilih membership type utama untuk anggota ini di perpustakaan yang dipilih.') . '</small>';
    $str_html .= '</div></div>';

    $str_html .= '</div>'; // card-body
    $str_html .= '</div>'; // card

    $form->addAnything('', $str_html);

    // ============================================================
    // BAGIAN 2: DAFTAR MEMBER TYPES (TABEL) - LANGSUNG DI BAWAHNYA
    // ============================================================
    if ($form->edit_mode && !empty($itemID)) {
        $member_type_list = '';
        $rel_q = $dbs->query("SELECT mmt.*, mt.member_type_name, 
                              l.library_code, l.library_name,
                              CASE WHEN mmt.is_active = 1 THEN 'Aktif' ELSE 'Nonaktif' END AS status_label
                              FROM member_member_type mmt
                              JOIN mst_member_type mt ON mmt.member_type_id = mt.member_type_id
                              LEFT JOIN mst_library l ON mmt.library_id = l.library_id
                              WHERE mmt.member_id = '$itemID'
                              ORDER BY mmt.library_id ASC");

        $str_html2 = '<div class="card card-info" style="border-color: #17a2b8; margin-bottom:15px;">';
        $str_html2 .= '<div class="card-header" style="background-color: #17a2b8; color: white; font-weight: bold;">';
        $str_html2 .= '<i class="fa fa-list"></i> ' . __('Daftar Member Types per Perpustakaan');
        $str_html2 .= ' <span class="badge badge-light" style="background: white; color: #17a2b8;">' . ($rel_q->num_rows ?? 0) . '</span>';
        $str_html2 .= '</div>';
        $str_html2 .= '<div class="card-body">';

        if ($rel_q->num_rows > 0) {
            $str_html2 .= '<div class="alert alert-info">';
            $str_html2 .= '<i class="fa fa-info-circle"></i> ';
            $str_html2 .= __('Berikut adalah daftar membership type yang dimiliki oleh anggota ini di setiap perpustakaan. ');
            $str_html2 .= __('Status <strong>Aktif</strong> berarti membership type ini sedang digunakan.');
            $str_html2 .= '</div>';

            $str_html2 .= '<table class="table table-striped table-bordered">';
            $str_html2 .= '<thead><tr>
                            <th>' . __('Library') . '</th>
                            <th>' . __('Member Type') . '</th>
                            <th>' . __('Status') . '</th>
                            <th>' . __('Action') . '</th>
                          </tr></thead><tbody>';

            while ($rel_d = $rel_q->fetch_assoc()) {
                $status_color = $rel_d['is_active'] == 1 ? 'success' : 'danger';
                $status_text = $rel_d['is_active'] == 1 ? __('Active') : __('Inactive');
                $action_link = '<a href="' . MWB . 'membership/index.php?action=detail&itemID=' . $itemID . 
                               '&set_active=' . $rel_d['id'] . '" class="btn btn-xs btn-' . 
                               ($rel_d['is_active'] == 1 ? 'warning" onclick="return confirm(\'' . __('Nonaktifkan member type ini?') . '\')">' . __('Deactivate') : 
                               'success" onclick="return confirm(\'' . __('Aktifkan member type ini?') . '\')">' . __('Activate')) . '</a>';

                $str_html2 .= '<tr>';
                $str_html2 .= '<td><strong>' . $rel_d['library_code'] . '</strong> - ' . $rel_d['library_name'] . '</td>';
                $str_html2 .= '<td>' . $rel_d['member_type_name'] . '</td>';
                $str_html2 .= '<td><span class="label label-' . $status_color . '">' . $status_text . '</span></td>';
                $str_html2 .= '<td>' . $action_link . '</td>';
                $str_html2 .= '</tr>';
            }
            $str_html2 .= '</tbody></table>';
        } else {
            $str_html2 .= '<div class="alert alert-info">' . __('Belum ada member type yang terdaftar untuk anggota ini.') . '</div>';
        }

        $str_html2 .= '<div style="margin-top:10px;">';
        $str_html2 .= '<a href="' . MWB . 'membership/pop_member_type.php?member_id=' . $itemID . '" 
                       class="s-btn btn btn-primary notAJAX openPopUp" 
                       width="500" height="400" 
                       title="' . __('Tambah Member Type') . '">
                       <i class="fa fa-plus"></i> ' . __('Tambah Member Type') . '</a>';
        $str_html2 .= ' <span class="text-muted small">' . __('Tambahkan membership type untuk perpustakaan lain') . '</span>';
        $str_html2 .= '</div>';

        $str_html2 .= '</div>'; // card-body
        $str_html2 .= '</div>'; // card

        $form->addAnything('', $str_html2);
    }
    // ============================================================
    // ========== AKHIR MEMBER TYPE ===============================
    // ============================================================

    // member gender
    $gender_chbox[0] = array('1', __('Male'));
    $gender_chbox[1] = array('0', __('Female'));
    $form->addRadio('gender', __('Sex'), $gender_chbox, !empty($rec_d['gender'])?$rec_d['gender']:'0');
    // member address
    $form->addTextField('textarea', 'memberAddress', __('Address'), $rec_d['member_address']??'', 'rows="2" class="form-control" style="width: 100%;"');
    // member postal
    $form->addTextField('text', 'memberPostal', __('Postal Code'), $rec_d['postal_code']??'', 'class="form-control" style="width: 50%;"');
    // member mail address
    $form->addTextField('textarea', 'memberMailAddress', __('Mail Address'), $rec_d['member_mail_address']??'', 'rows="2" class="form-control" style="width: 100%;"');
    // member phone
    $form->addTextField('text', 'memberPhone', __('Phone Number'), $rec_d['member_phone']??'', 'class="form-control" style="width: 50%;"');
    // member fax
    $form->addTextField('text', 'memberFax', __('Fax Number'), $rec_d['member_fax']??'', 'class="form-control" style="width: 50%;"');
    // member pin
    $form->addTextField('text', 'memberPIN', __('Personal ID Number'), $rec_d['pin']??'', 'class="form-control" style="width: 50%;"');
    // member notes
    $form->addTextField('textarea', 'memberNotes', __('Notes'), $rec_d['member_notes']??'', 'rows="2" class="form-control" style="width: 100%;"');

    /**
     * Custom fields
     */
    $form->loadCustomField('member', 'member_id', $itemID);

    // get advance custom field based on plugin
    $js = '';
    Plugins::getInstance()->execute(Plugins::MEMBERSHIP_CUSTOM_FIELD_FORM, ['form' => $form, 'js' => &$js, 'data' => $rec_cust_d ?? []]);

    // member is_pending
    $form->addCheckBox('isPending', __('Pending Membership'), array( array('1', __('Yes')) ), $rec_d['is_pending']??'');
    // member photo
    $upper_dir  = '';
    $str_input  = '<div class="row">';
    $str_input .= '<div class="col-2">';
    $str_input .= '<div id="imageFilename" class="s-margin__bottom-1">';
    
    if (isset($rec_d['member_image']) && Storage::images()->isExists('persons/'.$rec_d['member_image'])) {
        $str_input  .= '<a href="'.SWB . 'lib/minigalnano/createthumb.php?filename=images/persons/' . urlencode($rec_d['member_image']) . '&width=600" class="openPopUp notAJAX" title="'.__('Click to enlarge preview').'" width="300" height="400" >';
        $str_input .= '<img src="'.SWB.'lib/minigalnano/createthumb.php?filename=images/persons/'.urlencode(($rec_d['member_image']??'photo.png')).'&width=600&v='.date('this').'" class="img-fluid rounded" id="current_image_preview" alt="Image cover">';
        $str_input .= '</a>';
        // Tombol Remove
        $str_input .= '<a href="'.MWB.'membership/index.php" postdata="removeImage=true&mimg='.$itemID.'&img='.($rec_d['member_image']??'photo.png').'" loadcontainer="imageFilename" class="s-margin__bottom-1 s-btn btn btn-danger btn-block rounded-0 makeHidden removeImage">'.__('Remove Image').'</a>';
    } else {
        $str_input .= '<img src="'.SWB.'images/persons/person.png'.'?'.date('this').'" class="img-fluid rounded" id="current_image_preview" alt="Image cover">';
    }
    $str_input .= '</div>';
    $str_input .= '</div>';
    
    $str_input .= '<div class="custom-file col-4">';
    $str_input .= simbio_form_element::textField('file', 'image_file_input', '', 'id="image_file_input" class="custom-file-input" accept="'.implode(',', $sysconf['allowed_images']).'"');
    $str_input .= '<label class="custom-file-label" for="image_file_input">Choose file</label>';
    $str_input .= '</div>';
    $str_input .= ' <div class="mt-2 ml-2">Maximum '.$sysconf['max_image_upload'].' KB</div>';
    $str_input .= '</div>';
    
    if ($sysconf['webcam'] !== false) {
        $str_input .= '<textarea id="base64picstring" name="base64picstring" style="display: none;"></textarea>';
        if ($sysconf['webcam'] == 'flex') {
            $str_input .= '<object id="flash_video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" height="280px" width="100%">';
            $str_input .= '<param name="src" value="'.SWB.'lib/flex/ShotSLiMSMemberPicture.swf"/>';
            $str_input .= '<embed name="src" src="'.SWB.'lib/flex/ShotSLiMSMemberPicture.swf" height="280px" width="100%"/>';
            $str_input .= '</object>';
        } elseif ($sysconf['webcam'] == 'html5') {
            $str_input .= '<div class="makeHidden_">';
            $str_input .= '<p>'.__('or take a photo').'</p>';
            $str_input .= '<div class="form-inline">';
            $str_input .= '<div class="form-group pr-2">';
            $str_input .= '<button id="btn_load" type="button" class="btn btn-primary" onclick="loadcam(this)">'.__('Load Camera').'</button>';
            $str_input .= '</div>';
            $str_input .= '<div class="form-group pr-2">';
            $str_input .= '<select class="form-control" onchange="aspect(this)"><option value="1">1x1</option><option value="2" selected>2x3</option><option value="3">3x4</option></select>';
            $str_input .= '</div>';
            $str_input .= '<div class="form-group pr-2">';
            $str_input .= '<select class="form-control" id="cmb_format" onchange="if(pause){set();}"><option value="png">PNG</option><option value="jpg">JPEG</option></select>';
            $str_input .= '</div>';
            $str_input .= '<div class="form-group pr-2">';
            $str_input .= '<button id="btn_pause" type="button" class="btn btn-primary" onclick="snapshot(this)" disabled>'.__('Capture').'</button>';
            $str_input .= '</div>';
            $str_input .= '<div class="form-group pr-2">';
            $str_input .= '<button type="button" id="btn_reset" class="btn btn-danger" onclick="resetvalue()">'.__('Reset').'</button>';
            $str_input .= '</div>';
            $str_input .= '</div>';
            $str_input .= '<div id="my_container" class="makeHidden_ mt-2" style="width: 400px; height: 300px; border: 1px solid #f4f4f4; position: relative;">';
            $str_input .= '<video id="my_vid" autoplay width="400" height="300" style="float: left; position: absolute; left: 10;"></video>';
            $str_input .= '<canvas id="my_canvas" width="400" height="300" style="float: left; position: absolute; left: 10; visibility: hidden;"></canvas>';
            $str_input .= '<div id="my_frame" style="border: 1px solid #CCC; width: 160px; height: 240px; z-index: 2; margin: auto; position: absolute; top: 0; bottom: 0; left: 0; right: 0;"></div></div>';
            $str_input .= '<canvas id="my_preview" width="160" height="240" style="width: 160px; height: 240px; border: 1px solid #f4f4f4; display: none;"></canvas>';
        }
    }

    $form->addAnything(__('Photo'), $str_input);

    // hidden username and password fields so that the password manager of the browser will not fill in the username in the memberEmail and the password in the memberPasswd field
    $form->addTextField('text', 'dummyUserField', null, null, '');
    $form->addTextField('password', 'dummyPasswdField', null, null, '');
    echo '<style type="text/css">#simbioFormRowdummyPasswdField, #simbioFormRowdummyUserField {display: none}</style>';

    // member email
    $form->addTextField('text', 'memberEmail', __('E-mail'), $rec_d['member_email']??'', 'class="form-control" style="width: 40%;" class="form-control"');
    // member password
    $form->addTextField('password', 'memberPasswd', __('New Password'), null, 'class="form-control" style="width: 40%;" class="form-control" autocomplete="new-password"');
    // member password confirmation
    $form->addTextField('password', 'memberPasswd2', __('Confirm New Password'), null, 'class="form-control" style="width: 40%;" class="form-control" autocomplete="new-password"');

    // edit mode messagge
    if ($form->edit_mode) {
        if (isset($rec_d['member_image'])) {
            if (Storage::images()->isExists('persons/'.$rec_d['member_image'])) {
                echo '<div id="memberImage"><img src="'.SWB.'lib/minigalnano/createthumb.php?filename=images/persons/'.urlencode($rec_d['member_image']).'&width=120&v='.date('his').'" alt="'.$rec_d['member_name'].'" /></div>';
            }
        }
        echo '<div class="infoBox">
                <div>'.__('You are going to edit member data').' : <strong>'.$rec_d['member_name'].'</strong></div>
                <div>'.__('Last Updated').' '.date('d F Y h:i:s',strtotime($rec_d['last_update'])).' '.$expired_message.'</div>
                <div>'.__('Leave Password field blank if you don\'t want to change the password').'</div>';
        echo '</div>'."\n";
    }
    
    // ========== TAMBAHAN: Tombol Edit/Update sesuai alur SLiMS ==========
    if ($form->edit_mode) {
        echo '<div class="form-group" style="margin-top:15px;">';
        echo '<button type="button" id="btnEditMember" class="s-btn btn btn-default" onclick="enableMemberForm()">' . __('Edit') . '</button>';
        echo '<button type="submit" name="saveData" id="btnUpdateMember" class="s-btn btn btn-primary" disabled="disabled">' . __('Update') . '</button>';
        echo '</div>';
        echo '<script type="text/javascript">
                function enableMemberForm() {
                    // Enable semua input di form (kecuali hidden)
                    $("#mainForm input:not([type=hidden]), #mainForm select, #mainForm textarea").prop("disabled", false);
                    // Enable update button
                    $("#btnUpdateMember").prop("disabled", false);
                    $("#btnUpdateMember").addClass("btn-primary");
                    // Sembunyikan tombol edit
                    $("#btnEditMember").hide();
                }
                // Form dimulai disabled (kecuali hidden fields)
                $(document).ready(function() {
                    $("#mainForm input:not([type=hidden]), #mainForm select, #mainForm textarea").prop("disabled", true);
                    $("#btnUpdateMember").prop("disabled", true);
                });
              </script>';
    }
    // ========== AKHIR TAMBAHAN ==========
    
    // print out the form object
    echo $form->printOut();
    if ($sysconf['password_policy_strong']) {
        echo simbio_security::validatePasswordFunctionJS();
        echo '<script type="text/javascript">comparePassword("#mainForm", "#memberPasswd", "#memberPasswd2", '.$sysconf['password_policy_min_length'].');</script>';
    }
?>

<div id="croppie-processor" style="visibility: hidden; position: absolute; width: 300px; height: 300px; top: -9999px;"></div>
<script type="text/javascript">
$(document).ready(function() {
    const outputWidth = 300; 
    const outputHeight = 400;   
    const processorEl = $('#croppie-processor');
    $('#image_file_input').on('change', function(){
        const input = this;
        $('#base64picstring').val('');
        if (input.files && input.files[0]) {
            let fileName = $(this).val().replace(/\\/g, '/').replace(/.*\//, '');
            $(this).parent('.custom-file').find('.custom-file-label').text(fileName);
            const reader = new FileReader();
            reader.onload = function (e) {
                if (processorEl.data('croppie')) {
                    processorEl.croppie('destroy');
                }
                let $image_crop = processorEl.croppie({
                    enableExif: true,
                    enableZoom: true,
                    enableResize: false,
                    enableOrientation: true,
                    viewport: {
                        width: outputWidth,
                        height: outputHeight,
                        type: 'square'
                    },
                    boundary: {
                        width: outputWidth + 100, 
                        height: outputHeight + 100
                    }
                });
                $image_crop.croppie('bind', {
                    url: e.target.result,
                    zoom: 0
                }).then(function() {
                    setTimeout(function() {
                        $image_crop.croppie('result', {
                            type: 'base64',
                            size: { width: outputWidth, height: outputHeight }, 
                            format: 'jpeg',
                            quality: 0.75
                        }).then(function(base64_result) {
                            $('#base64picstring').val(base64_result);
                            $('#current_image_preview').attr('src', base64_result);
                            $image_crop.croppie('destroy');
                        });
                    }, 50);
                });
            }
            reader.readAsDataURL(input.files[0]);
        }
    });

    $('.removeImage').click(function (e) {
        if (confirm('Are you sure you want to permanently remove this image?')) {
            $('#base64picstring').val('');
            return true;
        } else {
            return false;
        }
    });

    $(document).on('change', '.custom-file-input', function () {
        let fileName = $(this).val().replace(/\\/g, '/').replace(/.*\//, '');
        $(this).parent('.custom-file').find('.custom-file-label').text(fileName);
    });

    <?php
    if (isset($js) && !empty($js)) {
        echo $js;
    }
    ?>
});
</script>
<?php
} else {
    /* MEMBERSHIP LIST */
    function showMemberImage($obj_db, $array_data) {
        global $sysconf;
        $imageDisk = Storage::images();
        $image = 'images/persons/photo.png';
        $_q = $obj_db->query('SELECT member_image,member_name,member_address,member_phone FROM member WHERE member_id = "'.$obj_db->escape_string($array_data[0]).'"');
        if(isset($_q->num_rows)){
            $_d = $_q->fetch_row();
            if($_d[0] != NULL){     
                $image = $imageDisk->isExists('persons/'.$_d[0])?'images/persons/'.$_d[0]:'images/persons/photo.png';
            }
            $addr  = $_d[2]!=''?'<i class="fa fa-map-marker" aria-hidden="true"></i></i>&nbsp;'.$_d[2]:'';
            $phone = $_d[3]!=''?'<i class="fa fa-phone" aria-hidden="true"></i>&nbsp;'.$_d[3]:'';
        }
        $imageUrl = SWB . 'lib/minigalnano/createthumb.php?filename=' . $image . '&width=600';
        $_output = '<div class="media"> 
                    <a href="'.$imageUrl.'" class="openPopUp notAJAX" title="'.$_d[1].'" width="300" height="400" >
                    <img class="mr-3 rounded" src="'.$imageUrl.'" alt="cover image" width="60" height: auto;></a>
                    <div class="media-body">
                      <div class="title">'.$array_data[2].'</div>
                      <div class="sub">'.$phone.'</div>
                      <div class="sub">'.$addr.'</div>
                    </div>
                  </div>';
        return $_output;
    }

   // ========== MODIFIKASI: Penggabungan Member Type dengan GROUP_CONCAT ==========
    $table_spec = 'member AS m
        LEFT JOIN member_member_type AS mmt ON m.member_id = mmt.member_id
        LEFT JOIN mst_member_type AS mt ON (
            (mmt.member_type_id = mt.member_type_id AND mmt.library_id = ' . (int)$current_library_id . ') 
            OR (m.member_type_id = mt.member_type_id)
        )';

    // create datagrid
    $datagrid = new simbio_datagrid();
    if ($can_read AND $can_write) {
        // Menggabungkan member_type_name dengan GROUP_CONCAT agar jadi satu baris
        $datagrid->setSQLColumn('m.member_id',
            'm.member_id AS \''.__('Member ID').'\'',
            'm.member_name AS \''.__('Member Name').'\'',
            'GROUP_CONCAT(DISTINCT mt.member_type_name SEPARATOR ", ") AS \''.__('Membership Type').'\'',
            'm.member_email AS \''.__('E-mail').'\'',
            'm.last_update AS \''.__('Last Updated').'\'');
        $datagrid->modifyColumnContent(2, 'callback{showMemberImage}');
    } else {
        $datagrid->setSQLColumn('m.member_id AS \''.__('Member ID').'\'',
            'm.member_name AS \''.__('Member Name').'\'',
            'GROUP_CONCAT(DISTINCT mt.member_type_name SEPARATOR ", ") AS \''.__('Membership Type').'\'',
            'm.member_email AS \''.__('E-mail').'\'',
            'm.last_update AS \''.__('Last Updated').'\'');
            $datagrid->modifyColumnContent(1, 'callback{showMemberImage}');
    }
    $datagrid->setSQLorder('m.last_update DESC');

    // is there any search
    $criteria = 'm.member_id IS NOT NULL ';
    
    // Filter 2 Kondisi (Library Asal OR Member Type Aktif)
    if (!$is_superadmin) {
        $criteria .= " AND (m.library_id = $current_library_id OR (mmt.library_id = $current_library_id AND mmt.is_active = 1))";
    }
    
    if (isset($_GET['keywords']) AND $_GET['keywords']) {
        $keywords = $dbs->escape_string($_GET['keywords']);
        $criteria .= " AND (m.member_name LIKE '%$keywords%' OR m.member_id LIKE '%$keywords%') ";
    }
    if (isset($_GET['expire'])) {
        $criteria .= " AND TO_DAYS('".date('Y-m-d')."')>TO_DAYS(m.expire_date)";
    }

    // ========== WAJIB: Tambahkan GROUP BY berdasarkan member_id ==========
    $criteria .= " GROUP BY m.member_id";
    // ======================================================================

    $datagrid->setSQLCriteria($criteria);
    // set table and table header attributes
    $datagrid->icon_edit = SWB.'admin/'.$sysconf['admin_template']['dir'].'/'.$sysconf['admin_template']['theme'].'/edit.gif';
    $datagrid->table_name = 'memberList';
    $datagrid->table_attr = 'id="dataList" class="s-table table"';
    $datagrid->table_header_attr = 'class="dataListHeader" style="font-weight: bold;"';
    // set delete proccess URL
    $datagrid->chbox_form_URL = $_SERVER['PHP_SELF'];
    // put the result into variables
    $datagrid_result = $datagrid->createDataGrid($dbs, $table_spec, 20, ($can_read AND $can_write));
    if ((isset($_GET['keywords']) AND $_GET['keywords']) OR isset($_GET['expire'])) {
        if (isset($_GET['expire'])) {
            echo '<div class="infoBox">';
            echo '<input type="button" value="'.__('Extend Selected Member(s)').'" onclick="javascript: if (confirm(\''.__('Are you sure to EXTEND membership for selected members?').'\')) { $(\'#mainContent\').simbioAJAX(\''.MWB.'membership/index.php?expire=1\', { method: \'post\', addData: $(\'#memberList\').serialize() + \'&batchExtend=true\' } ); }" class="s-btn btn btn-primary" />';
            echo '</div>';
            if (isset($_GET['numExtended']) AND $_GET['numExtended'] > 0) {
                echo '<div class="infoBox mt-1">';
                echo '<strong>'.$_GET['numExtended'].'</strong> '.__('members extended!'); //mfc
                echo '</div>';
            }
        }
        if (isset($_GET['keywords']) AND $_GET['keywords']) {
            echo __('Found').' '.$datagrid->num_rows.' '.__('from your search with keyword').' : "'.htmlentities($_GET['keywords']).'"'; //mfc
        }
        echo '</div>';
    }

    echo $datagrid_result;
}