Sindbad~EG File Manager

Current Path : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/membership/
Upload File :
Current File : /home/copmadinaarea/thecopmadinaarea.org/portal/modules/membership/bulk_csv_edit.php

<?php
require_once '../../config/config.php';
checkLogin();
checkAccess('assembly');

$db = Database::getInstance()->getConnection();
$pageTitle = 'Bulk Excel Edit - ' . APP_NAME;

// AJAX: Fetch members data
if (isset($_GET['action']) && $_GET['action'] === 'fetch_members' && isset($_GET['ids'])) {
    header('Content-Type: application/json');
    
    $memberIds = explode(',', $_GET['ids']);
    $memberIds = array_filter($memberIds, 'is_numeric');
    
    if (empty($memberIds)) {
        echo json_encode(['success' => false, 'message' => 'No valid member IDs']);
        exit;
    }
    
    $placeholders = str_repeat('?,', count($memberIds) - 1) . '?';
    $query = "SELECT m.*, a.area_name, d.district_name, asm.assembly_name
              FROM members m
              LEFT JOIN areas a ON m.area_id = a.id
              LEFT JOIN districts d ON m.district_id = d.id
              LEFT JOIN assemblies asm ON m.assembly_id = asm.id
              WHERE m.id IN ($placeholders)
              ORDER BY m.id";
    
    $stmt = $db->prepare($query);
    $stmt->execute($memberIds);
    $members = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
    // Prepare data for spreadsheet
    $data = [];
    foreach ($members as $member) {
        $data[] = [
            $member['id'],
            $member['title'] ?? '',
            $member['first_name'] ?? '',
            $member['middle_name'] ?? '',
            $member['last_name'] ?? '',
            $member['gender'] ?? '',
            $member['date_of_birth'] ?? '',
            $member['place_of_birth'] ?? '',
            $member['marital_status'] ?? '',
            $member['member_type'] ?? '',
            $member['phone'] ?? '',
            $member['email'] ?? '',
            $member['address_line'] ?? '',
            $member['gps_address'] ?? '',
            $member['street'] ?? '',
            $member['city'] ?? '',
            $member['hometown'] ?? '',
            $member['area_name'] ?? '',
            $member['district_name'] ?? '',
            $member['assembly_name'] ?? '',
            $member['family_id'] ?? '',
            $member['water_baptism'] ? 'Yes' : 'No',
            $member['water_baptism_date'] ?? '',
            $member['water_baptism_minister'] ?? '',
            $member['holyghost_baptism'] ? 'Yes' : 'No',
            $member['holyghost_baptism_date'] ?? '',
            $member['holyghost_baptism_minister'] ?? '',
            $member['communicant'] ? 'Yes' : 'No',
            $member['communicant_date'] ?? '',
            $member['communicant_minister'] ?? '',
            $member['dedicated'] ? 'Yes' : 'No',
            $member['dedicated_date'] ?? '',
            $member['dedicated_minister'] ?? '',
            $member['occupation'] ?? '',
            $member['education_level'] ?? '',
            $member['parent_name'] ?? '',
            $member['parent_relationship'] ?? ''
        ];
    }
    
    echo json_encode(['success' => true, 'data' => $data]);
    exit;
}

// AJAX: Save changes
if (isset($_POST['action']) && $_POST['action'] === 'save_changes') {
    header('Content-Type: application/json');
    
    $data = json_decode(file_get_contents('php://input'), true);
    
    if (!isset($data['changes']) || empty($data['changes'])) {
        echo json_encode(['success' => false, 'message' => 'No changes to save']);
        exit;
    }
    
    try {
        $db->beginTransaction();
        $updatedCount = 0;
        $errors = [];
        
        foreach ($data['changes'] as $row) {
            try {
                $memberId = $row[0]; // ID is always first column
                
                if (empty($memberId)) {
                    continue;
                }
                
                // Prepare update
                $sql = "UPDATE members SET 
                        title = ?,
                        first_name = ?,
                        middle_name = ?,
                        last_name = ?,
                        gender = ?,
                        date_of_birth = ?,
                        place_of_birth = ?,
                        marital_status = ?,
                        member_type = ?,
                        phone = ?,
                        email = ?,
                        address_line = ?,
                        gps_address = ?,
                        street = ?,
                        city = ?,
                        hometown = ?,
                        family_id = ?,
                        water_baptism = ?,
                        water_baptism_date = ?,
                        water_baptism_minister = ?,
                        holyghost_baptism = ?,
                        holyghost_baptism_date = ?,
                        holyghost_baptism_minister = ?,
                        communicant = ?,
                        communicant_date = ?,
                        communicant_minister = ?,
                        dedicated = ?,
                        dedicated_date = ?,
                        dedicated_minister = ?,
                        occupation = ?,
                        education_level = ?,
                        parent_name = ?,
                        parent_relationship = ?,
                        updated_at = NOW()
                        WHERE id = ?";
                
                $updateStmt = $db->prepare($sql);
                
                $params = [
                    $row[1],  // title
                    $row[2],  // first_name
                    $row[3],  // middle_name
                    $row[4],  // last_name
                    $row[5],  // gender
                    $row[6] ?: null,  // date_of_birth
                    $row[7],  // place_of_birth
                    $row[8],  // marital_status
                    $row[9],  // member_type
                    $row[10], // phone
                    $row[11], // email
                    $row[12], // address_line
                    $row[13], // gps_address
                    $row[14], // street
                    $row[15], // city
                    $row[16], // hometown
                    $row[20], // family_id
                    in_array(strtolower($row[21]), ['yes', '1']) ? 1 : 0, // water_baptism
                    $row[22] ?: null, // water_baptism_date
                    $row[23], // water_baptism_minister
                    in_array(strtolower($row[24]), ['yes', '1']) ? 1 : 0, // holyghost_baptism
                    $row[25] ?: null, // holyghost_baptism_date
                    $row[26], // holyghost_baptism_minister
                    in_array(strtolower($row[27]), ['yes', '1']) ? 1 : 0, // communicant
                    $row[28] ?: null, // communicant_date
                    $row[29], // communicant_minister
                    in_array(strtolower($row[30]), ['yes', '1']) ? 1 : 0, // dedicated
                    $row[31] ?: null, // dedicated_date
                    $row[32], // dedicated_minister
                    $row[33], // occupation
                    $row[34], // education_level
                    $row[35], // parent_name
                    $row[36], // parent_relationship
                    $memberId
                ];
                
                if ($updateStmt->execute($params)) {
                    $updatedCount++;
                } else {
                    $errors[] = "Failed to update member ID $memberId";
                }
                
            } catch (Exception $e) {
                $errors[] = "Member ID " . ($row[0] ?? 'unknown') . ": " . $e->getMessage();
            }
        }
        
        $db->commit();
        
        echo json_encode([
            'success' => true,
            'message' => "Successfully updated $updatedCount member(s)",
            'updated' => $updatedCount,
            'errors' => $errors
        ]);
        
    } catch (Exception $e) {
        $db->rollBack();
        echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
    }
    
    exit;
}

// If no POST data, redirect back
if (!isset($_POST['member_ids']) || empty($_POST['member_ids'])) {
    $_SESSION['error'] = 'No members selected.';
    header('Location: members_edit.php');
    exit;
}

$memberIds = array_filter($_POST['member_ids'], 'is_numeric');
$memberCount = count($memberIds);
$memberIdsString = implode(',', $memberIds);

include '../../includes/header.php';
include '../../includes/sidebar.php';
?>

<!-- jSpreadsheet CSS -->
<link rel="stylesheet" href="https://bossanova.uk/jspreadsheet/v4/jexcel.css" type="text/css" />
<link rel="stylesheet" href="https://jsuites.net/v4/jsuites.css" type="text/css" />

<style>
    #spreadsheet {
        border: 2px solid #e5e7eb;
        border-radius: 0.5rem;
        overflow: hidden;
    }
    
    .jexcel_content {
        max-height: calc(100vh - 400px);
        overflow: auto;
    }
    
    .jexcel thead td {
        background: linear-gradient(135deg, #1E40AF 0%, #3B82F6 100%) !important;
        color: white !important;
        font-weight: 600 !important;
        text-align: center !important;
        padding: 12px 8px !important;
        font-size: 0.875rem !important;
    }
    
    .jexcel tbody tr:nth-child(even) {
        background-color: #f9fafb;
    }
    
    .jexcel tbody tr:hover {
        background-color: #eff6ff;
    }
    
    .jexcel tbody td {
        padding: 8px !important;
        border: 1px solid #e5e7eb !important;
    }
    
    .jexcel tbody td:first-child {
        background-color: #f3f4f6 !important;
        font-weight: 600;
        color: #1f2937;
    }
    
    .readonly-column {
        background-color: #fef3c7 !important;
        cursor: not-allowed;
    }
</style>

<main class="flex-1 md:ml-64 mt-16">
<div class="container mx-auto px-4 py-8">
    
    <!-- Page Header -->
    <div class="mb-8">
        <h1 class="text-3xl font-bold text-gray-800">
            <i class="fas fa-table text-blue-500 mr-3"></i>Bulk Excel Edit
        </h1>
        <p class="text-gray-600 mt-2">Edit <?php echo $memberCount; ?> member(s) in real-time using Excel-like interface</p>
    </div>

    <!-- Instructions Card -->
    <div class="bg-blue-50 border-l-4 border-blue-500 p-6 mb-8 rounded-lg">
        <h3 class="text-lg font-semibold text-blue-800 mb-3">
            <i class="fas fa-info-circle mr-2"></i>How to Use
        </h3>
        <ul class="list-disc list-inside space-y-2 text-blue-900">
            <li><strong>Click any cell</strong> to start editing (except ID, Area, District, Assembly columns)</li>
            <li><strong>Use keyboard shortcuts:</strong> Tab to move right, Enter to move down, Arrow keys to navigate</li>
            <li><strong>Copy/Paste</strong> works just like Excel - select cells and use Ctrl+C / Ctrl+V</li>
            <li><strong>Click "Save All Changes"</strong> button when done to update the database</li>
        </ul>
        <div class="mt-4 p-3 bg-yellow-50 border-l-4 border-yellow-400 rounded">
            <p class="text-yellow-800 text-sm">
                <i class="fas fa-exclamation-triangle mr-2"></i>
                <strong>Read-Only Columns:</strong> ID, Area, District, and Assembly columns cannot be edited (shown in yellow).
            </p>
        </div>
    </div>

    <!-- Action Buttons -->
    <div class="flex justify-between items-center mb-6">
        <div class="flex gap-3">
            <button onclick="saveChanges()" class="bg-green-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-green-700 transition shadow-lg hover:shadow-xl">
                <i class="fas fa-save mr-2"></i>Save All Changes
            </button>
            
            <button onclick="resetData()" class="bg-yellow-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-yellow-700 transition shadow-lg">
                <i class="fas fa-undo mr-2"></i>Reset to Original
            </button>
        </div>
        
        <a href="members_edit.php" class="bg-gray-500 text-white px-6 py-3 rounded-lg font-semibold hover:bg-gray-600 transition shadow-lg">
            <i class="fas fa-times mr-2"></i>Cancel
        </a>
    </div>

    <!-- Loading Indicator -->
    <div id="loading" class="text-center py-12">
        <i class="fas fa-spinner fa-spin text-4xl text-blue-500 mb-4"></i>
        <p class="text-gray-600">Loading member data...</p>
    </div>

    <!-- Spreadsheet Container -->
    <div id="spreadsheet-container" style="display:none;" class="bg-white rounded-lg shadow-lg p-6">
        <div id="spreadsheet"></div>
        
        <div class="mt-6 flex justify-between items-center">
            <div class="text-sm text-gray-600">
                <i class="fas fa-lightbulb text-yellow-500 mr-2"></i>
                <strong>Tip:</strong> You can select multiple cells and drag to fill, just like Excel!
            </div>
            
            <div class="text-sm text-gray-600">
                Editing <strong><?php echo $memberCount; ?></strong> member(s)
            </div>
        </div>
    </div>

</div>
</main>

<!-- jSpreadsheet JS -->
<script src="https://bossanova.uk/jspreadsheet/v4/jexcel.js"></script>
<script src="https://jsuites.net/v4/jsuites.js"></script>

<script>
    let spreadsheet = null;
    let originalData = [];
    
    // Column headers
    const columns = [
        { title: 'ID', width: 80, type: 'text', readOnly: true },
        { title: 'Title', width: 100, type: 'dropdown', source: ['Mr.', 'Mrs.', 'Miss', 'Ms.', 'Dr.', 'Rev.', 'Prof.', 'Pastor', 'Elder', 'Deacon', 'Deaconess', 'Evangelist'] },
        { title: 'First Name', width: 150, type: 'text' },
        { title: 'Middle Name', width: 150, type: 'text' },
        { title: 'Last Name', width: 150, type: 'text' },
        { title: 'Gender', width: 100, type: 'dropdown', source: ['Male', 'Female'] },
        { title: 'Date of Birth', width: 120, type: 'calendar', options: { format: 'YYYY-MM-DD' } },
        { title: 'Place of Birth', width: 150, type: 'text' },
        { title: 'Marital Status', width: 120, type: 'dropdown', source: ['Single', 'Married', 'Divorced', 'Widowed'] },
        { title: 'Member Type', width: 120, type: 'dropdown', source: ['Regular', 'Youth', 'Children', 'Senior', 'Missionary'] },
        { title: 'Phone', width: 130, type: 'text' },
        { title: 'Email', width: 200, type: 'text' },
        { title: 'Address Line', width: 200, type: 'text' },
        { title: 'GPS Address', width: 150, type: 'text' },
        { title: 'Street', width: 150, type: 'text' },
        { title: 'City', width: 120, type: 'text' },
        { title: 'Hometown', width: 120, type: 'text' },
        { title: 'Area', width: 120, type: 'text', readOnly: true },
        { title: 'District', width: 120, type: 'text', readOnly: true },
        { title: 'Assembly', width: 150, type: 'text', readOnly: true },
        { title: 'Family ID', width: 100, type: 'text' },
        { title: 'Water Baptism', width: 120, type: 'dropdown', source: ['Yes', 'No'] },
        { title: 'Water Baptism Date', width: 140, type: 'calendar', options: { format: 'YYYY-MM-DD' } },
        { title: 'Water Baptism Minister', width: 180, type: 'text' },
        { title: 'Holy Ghost Baptism', width: 140, type: 'dropdown', source: ['Yes', 'No'] },
        { title: 'Holy Ghost Date', width: 140, type: 'calendar', options: { format: 'YYYY-MM-DD' } },
        { title: 'Holy Ghost Minister', width: 180, type: 'text' },
        { title: 'Communicant', width: 120, type: 'dropdown', source: ['Yes', 'No'] },
        { title: 'Communicant Date', width: 140, type: 'calendar', options: { format: 'YYYY-MM-DD' } },
        { title: 'Communicant Minister', width: 180, type: 'text' },
        { title: 'Dedicated', width: 120, type: 'dropdown', source: ['Yes', 'No'] },
        { title: 'Dedicated Date', width: 140, type: 'calendar', options: { format: 'YYYY-MM-DD' } },
        { title: 'Dedicated Minister', width: 180, type: 'text' },
        { title: 'Occupation', width: 150, type: 'text' },
        { title: 'Education Level', width: 130, type: 'dropdown', source: ['None', 'Primary', 'JHS', 'SHS', 'Tertiary', 'Postgraduate'] },
        { title: 'Parent Name', width: 150, type: 'text' },
        { title: 'Parent Relationship', width: 140, type: 'dropdown', source: ['Father', 'Mother', 'Guardian', 'Other'] }
    ];
    
    // Load member data
    async function loadMembers() {
        try {
            const response = await fetch('bulk_csv_edit.php?action=fetch_members&ids=<?php echo $memberIdsString; ?>');
            const result = await response.json();
            
            if (result.success) {
                originalData = JSON.parse(JSON.stringify(result.data)); // Deep copy
                initializeSpreadsheet(result.data);
                document.getElementById('loading').style.display = 'none';
                document.getElementById('spreadsheet-container').style.display = 'block';
            } else {
                alert('Error loading members: ' + result.message);
            }
        } catch (error) {
            alert('Error: ' + error.message);
        }
    }
    
    // Initialize spreadsheet
    function initializeSpreadsheet(data) {
        spreadsheet = jspreadsheet(document.getElementById('spreadsheet'), {
            data: data,
            columns: columns,
            minDimensions: [37, data.length],
            tableOverflow: true,
            tableWidth: '100%',
            freezeColumns: 1,
            contextMenu: function() {
                return false; // Disable context menu
            },
            updateTable: function(instance, cell, col, row, val, label, cellName) {
                // Highlight readonly columns
                if (col === 0 || col === 17 || col === 18 || col === 19) {
                    cell.classList.add('readonly-column');
                }
            }
        });
    }
    
    // Save changes
    async function saveChanges() {
        if (!spreadsheet) {
            alert('No data to save');
            return;
        }
        
        if (!confirm('Are you sure you want to save all changes? This will update <?php echo $memberCount; ?> member(s) in the database.')) {
            return;
        }
        
        const currentData = spreadsheet.getData();
        
        // Show loading
        const saveBtn = event.target;
        const originalText = saveBtn.innerHTML;
        saveBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Saving...';
        saveBtn.disabled = true;
        
        try {
            const response = await fetch('bulk_csv_edit.php?action=save_changes', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    action: 'save_changes',
                    changes: currentData
                })
            });
            
            const result = await response.json();
            
            if (result.success) {
                alert('✓ ' + result.message + (result.errors.length > 0 ? '\n\nWarnings:\n' + result.errors.join('\n') : ''));
                
                // Update original data
                originalData = JSON.parse(JSON.stringify(currentData));
                
                // Option to return
                if (confirm('Changes saved successfully! Do you want to return to the members list?')) {
                    window.location.href = 'members_edit.php';
                }
            } else {
                alert('Error: ' + result.message);
            }
        } catch (error) {
            alert('Error saving changes: ' + error.message);
        } finally {
            saveBtn.innerHTML = originalText;
            saveBtn.disabled = false;
        }
    }
    
    // Reset to original data
    function resetData() {
        if (!confirm('Are you sure you want to reset all changes? This will discard all edits.')) {
            return;
        }
        
        spreadsheet.setData(JSON.parse(JSON.stringify(originalData)));
        alert('Data reset to original values');
    }
    
    // Load on page ready
    document.addEventListener('DOMContentLoaded', function() {
        loadMembers();
    });
</script>

<?php include '../../includes/footer.php'; ?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists