mayankjoin3

Dynamic Tables Query

Aug 18th, 2025
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 30.33 KB | None | 0 0
  1. <?php
  2. // PHP Backend Logic
  3. // This script handles all database interactions.
  4. // It is designed to be self-contained within the HTML file.
  5.  
  6. // --- Configuration and Error Reporting ---
  7. // Enable MySQLi exceptions for better error handling.
  8. // This allows us to use try-catch blocks for database operations.
  9. mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
  10.  
  11. // Define a constant for the error log file path
  12. define('LOG_FILE_PATH', 'error_log.txt');
  13.  
  14. // --- Centralized Error Logging Function ---
  15. // This function writes detailed error messages to the log file.
  16. function log_error($message, $file, $line) {
  17.     // OLD: error_log($message);
  18.     // NEW: Log the error with a timestamp and file/line number
  19.     $timestamp = date('d-M-Y H:i:s');
  20.     $log_message = sprintf("[%s] [%s:%s] %s\n", $timestamp, $file, $line, $message);
  21.     error_log($log_message, 3, LOG_FILE_PATH);
  22. }
  23.  
  24. // Database connection details
  25. // IMPORTANT: Replace with your actual credentials
  26. $db_host = "localhost";
  27. $db_user = "root";
  28. $db_pass = "";
  29. $db_name = "test"; // The database name as specified in the request
  30.  
  31. // Establish a connection to the database
  32. try {
  33.     $mysqli = new mysqli($db_host, $db_user, $db_pass, $db_name);
  34.     // Set character set to prevent encoding issues
  35.     $mysqli->set_charset("utf8");
  36. } catch (mysqli_sql_exception $e) {
  37.     // Log the connection error and provide a user-friendly message
  38.     log_error("Database connection failed: " . $e->getMessage(), __FILE__, __LINE__);
  39.     die("An error occurred while connecting to the database. Please check the logs for more details.");
  40. }
  41.  
  42. // --- Function to safely escape user input ---
  43. // This function trims whitespace and escapes special characters to prevent SQL injection.
  44. function clean_input($data) {
  45.     global $mysqli;
  46.     // OLD: $data = trim($data);
  47.     // NEW: Ensure data is a string before trimming and escaping
  48.     $data = is_string($data) ? trim($data) : $data;
  49.     $data = mysqli_real_escape_string($mysqli, $data);
  50.     return $data;
  51. }
  52.  
  53. // --- AJAX action handler ---
  54. // This part of the code responds to requests from the frontend.
  55. try {
  56.     if (isset($_POST['action'])) {
  57.         $action = clean_input($_POST['action']);
  58.        
  59.         // Action 1: Fetch columns and key info for a selected table
  60.         if ($action === 'get_columns') {
  61.             $table_name = isset($_POST['table']) ? clean_input($_POST['table']) : '';
  62.             if (empty($table_name)) {
  63.                 die(json_encode(['error' => 'No table specified.']));
  64.             }
  65.  
  66.             $response = [
  67.                 'columns' => [],
  68.                 'primary_key' => '',
  69.                 'auto_increment' => ''
  70.             ];
  71.  
  72.             try {
  73.                 // Get column details and primary key
  74.                 $result = $mysqli->query("DESCRIBE `$table_name`");
  75.                 if ($result) {
  76.                     while ($row = $result->fetch_assoc()) {
  77.                         $column_name = $row['Field'];
  78.                         $response['columns'][] = $column_name;
  79.                         if ($row['Key'] === 'PRI') {
  80.                             $response['primary_key'] = $column_name;
  81.                         }
  82.                     }
  83.                     $result->free();
  84.                 }
  85.  
  86.                 // Get auto_increment property from SHOW CREATE TABLE
  87.                 $result = $mysqli->query("SHOW CREATE TABLE `$table_name`");
  88.                 if ($result) {
  89.                     $row = $result->fetch_assoc();
  90.                     $create_table_sql = $row['Create Table'];
  91.                     if (preg_match('/`[^`]+`\s+INT\([^`]+\)\s+NOT NULL\s+AUTO_INCREMENT/', $create_table_sql, $matches)) {
  92.                         $auto_increment_column = str_replace('`', '', substr($matches[0], 0, strpos($matches[0], ' ')));
  93.                         $response['auto_increment'] = $auto_increment_column;
  94.                     }
  95.                     $result->free();
  96.                 }
  97.  
  98.                 header('Content-Type: application/json');
  99.                 echo json_encode($response);
  100.             } catch (mysqli_sql_exception $e) {
  101.                 // Log the query error
  102.                 log_error("Failed to get columns for table '$table_name': " . $e->getMessage(), __FILE__, __LINE__);
  103.                 die(json_encode(['error' => 'An error occurred while fetching table information.']));
  104.             }
  105.             exit;
  106.         }
  107.  
  108.         // Action 2: Execute a user-defined query and display results
  109.         if ($action === 'execute_query') {
  110.             // OLD: $query = isset($_POST['query']) ? trim($_POST['query']) : '';
  111.             // NEW: Apply trim to the raw query
  112.             $query = isset($_POST['query']) ? trim($_POST['query']) : '';
  113.  
  114.             // Note: This feature allows the user to run arbitrary SQL.
  115.             // This is a known risk, and the user must be trusted.
  116.             // Prepared statements are not used here as the query itself is dynamic.
  117.  
  118.             $html = '';
  119.             try {
  120.                 $result = $mysqli->query($query);
  121.                 if ($result === false) {
  122.                     // This case should be caught by the exception, but added as a failsafe
  123.                     throw new mysqli_sql_exception("Query returned false result.");
  124.                 }
  125.  
  126.                 $html .= '<div class="table-responsive mt-3">';
  127.                 $html .= '<table class="table table-striped table-bordered table-hover">';
  128.                 $html .= '<thead class="table-dark"><tr>';
  129.  
  130.                 // Fetch headers
  131.                 $field_info = $result->fetch_fields();
  132.                 $headers = [];
  133.                 foreach ($field_info as $val) {
  134.                     $headers[] = $val->name;
  135.                     $html .= '<th>' . htmlspecialchars($val->name) . '</th>';
  136.                 }
  137.                 $html .= '</tr></thead><tbody>';
  138.  
  139.                 // Fetch and display data
  140.                 $primary_key = isset($_POST['primary_key']) ? trim($_POST['primary_key']) : '';
  141.                 while ($row = $result->fetch_assoc()) {
  142.                     $pk_value = isset($row[$primary_key]) ? htmlspecialchars($row[$primary_key]) : '';
  143.                     $html .= '<tr data-pk-value="' . $pk_value . '">';
  144.                     foreach ($headers as $header) {
  145.                         $html .= '<td contenteditable="true" data-col-name="' . htmlspecialchars($header) . '">' . htmlspecialchars($row[$header]) . '</td>';
  146.                     }
  147.                     $html .= '</tr>';
  148.                 }
  149.                 $html .= '</tbody></table></div>';
  150.                 $html .= '<button id="updateChangesBtn" class="btn btn-primary mt-3 me-2">Save All Changes</button>';
  151.                 $result->free();
  152.             } catch (mysqli_sql_exception $e) {
  153.                 // Log the specific query error
  154.                 log_error("Query execution failed: '$query'. Error: " . $e->getMessage(), __FILE__, __LINE__);
  155.                 // OLD: $html = '<div class="alert alert-danger mt-3" role="alert">Query error: ' . $mysqli->error . '</div>';
  156.                 // NEW: A more polite and informative error message for the user
  157.                 $html = '<div class="alert alert-danger mt-3" role="alert">An error occurred while executing your query. Please double-check the syntax.</div>';
  158.             }
  159.  
  160.             echo $html;
  161.             exit;
  162.         }
  163.  
  164.         // Action 3: Update records in the database
  165.         if ($action === 'update_records') {
  166.             $table_name = isset($_POST['table']) ? clean_input($_POST['table']) : '';
  167.             $primary_key = isset($_POST['primary_key']) ? clean_input($_POST['primary_key']) : '';
  168.             $data = isset($_POST['data']) ? json_decode($_POST['data'], true) : [];
  169.            
  170.             if ($data === null) {
  171.                 log_error("Invalid JSON data received for update.", __FILE__, __LINE__);
  172.                 echo json_encode(['success' => false, 'message' => 'Invalid data received from the client.']);
  173.                 exit;
  174.             }
  175.  
  176.             if (empty($table_name) || empty($primary_key)) {
  177.                 log_error("Table name or primary key is missing for update.", __FILE__, __LINE__);
  178.                 echo json_encode(['success' => false, 'message' => 'Table name or primary key is missing.']);
  179.                 exit;
  180.             }
  181.  
  182.             if (empty($data)) {
  183.                 echo json_encode(['success' => false, 'message' => 'No changes were detected for the update.']);
  184.                 exit;
  185.             }
  186.  
  187.             $success_count = 0;
  188.             $error_messages = [];
  189.  
  190.             foreach ($data as $row) {
  191.                 try {
  192.                     if (isset($row['pk_value']) && isset($row['changes'])) {
  193.                         $pk_value = clean_input($row['pk_value']);
  194.                         $changes = $row['changes'];
  195.  
  196.                         $set_parts = [];
  197.                         foreach ($changes as $col => $val) {
  198.                             $sanitized_col = clean_input($col);
  199.                             $sanitized_val = clean_input($val);
  200.                             // Using prepared statements is the gold standard for security.
  201.                             // For this dynamic use case, we are still building the query string, but
  202.                             // we are sanitizing all inputs to minimize risk.
  203.                             $set_parts[] = "`" . $sanitized_col . "` = '" . $sanitized_val . "'";
  204.                         }
  205.  
  206.                         if (!empty($set_parts)) {
  207.                             $set_clause = implode(', ', $set_parts);
  208.                             $update_query = "UPDATE `$table_name` SET $set_clause WHERE `" . clean_input($primary_key) . "` = '$pk_value'";
  209.                            
  210.                             if ($mysqli->query($update_query) === TRUE) {
  211.                                 $success_count++;
  212.                             } else {
  213.                                 // This should be caught by the exception handler, but is kept as a failsafe
  214.                                 throw new mysqli_sql_exception("Update query returned false.");
  215.                             }
  216.                         }
  217.                     }
  218.                 } catch (mysqli_sql_exception $e) {
  219.                     // Log the update error and the specific record that failed
  220.                     log_error("Update failed for record with PK '$pk_value' on table '$table_name'. Error: " . $e->getMessage(), __FILE__, __LINE__);
  221.                     $error_messages[] = "An error occurred while updating the record with " . htmlspecialchars($primary_key) . " = " . htmlspecialchars($pk_value) . ".";
  222.                 }
  223.             }
  224.  
  225.             if (empty($error_messages)) {
  226.                 echo json_encode(['success' => true, 'message' => "Successfully updated " . htmlspecialchars($success_count) . " records."]);
  227.             } else {
  228.                 echo json_encode(['success' => false, 'message' => implode('<br>', $error_messages)]);
  229.             }
  230.             exit;
  231.         }
  232.  
  233.         // Action 4: Export data to a CSV file
  234.         if ($action === 'export_csv') {
  235.             $query = isset($_POST['query']) ? trim($_POST['query']) : '';
  236.             $table_name = isset($_POST['table']) ? clean_input($_POST['table']) : 'data';
  237.  
  238.             if (empty($query)) {
  239.                 die("The query is empty. Please enter a valid query to export data.");
  240.             }
  241.  
  242.             try {
  243.                 $result = $mysqli->query($query);
  244.                 if ($result === false) {
  245.                     throw new mysqli_sql_exception("Export query returned false.");
  246.                 }
  247.  
  248.                 // Set headers for CSV download
  249.                 $filename = $table_name . '_' . date('Y_m_d') . '.csv';
  250.                 header('Content-Type: text/csv; charset=utf-8');
  251.                 header('Content-Disposition: attachment; filename="' . $filename . '"');
  252.                
  253.                 $output = fopen('php://output', 'w');
  254.                
  255.                 // Get column headers and write to CSV
  256.                 $field_info = $result->fetch_fields();
  257.                 $headers = [];
  258.                 foreach ($field_info as $val) {
  259.                     $headers[] = $val->name;
  260.                 }
  261.                 fputcsv($output, $headers);
  262.  
  263.                 // Write data rows to CSV
  264.                 while ($row = $result->fetch_assoc()) {
  265.                     fputcsv($output, $row);
  266.                 }
  267.                
  268.                 fclose($output);
  269.                 $result->free();
  270.             } catch (mysqli_sql_exception $e) {
  271.                 log_error("CSV export failed for query '$query'. Error: " . $e->getMessage(), __FILE__, __LINE__);
  272.                 die("An error occurred during the CSV export process. Please check the logs.");
  273.             }
  274.             exit;
  275.         }
  276.     }
  277. } catch (Exception $e) {
  278.     // Catch-all for any uncaught exceptions
  279.     log_error("An unexpected error occurred: " . $e->getMessage(), __FILE__, __LINE__);
  280.     // Provide a generic, polite message to the user
  281.     die("An unexpected error occurred on the server. We apologize for the inconvenience.");
  282. }
  283.  
  284. // --- Main PHP logic for initial page load ---
  285. // Get the list of all tables in the database
  286. $tables = [];
  287. try {
  288.     $result = $mysqli->query("SHOW TABLES FROM `$db_name`");
  289.     if ($result) {
  290.         while ($row = $result->fetch_row()) {
  291.             $tables[] = $row[0];
  292.         }
  293.         $result->free();
  294.         sort($tables);
  295.     }
  296. } catch (mysqli_sql_exception $e) {
  297.     log_error("Failed to list tables: " . $e->getMessage(), __FILE__, __LINE__);
  298.     // Continue with an empty array of tables, the frontend will display an error message
  299. }
  300.  
  301. // Close the connection
  302. $mysqli->close();
  303. ?>
  304.  
  305. <!DOCTYPE html>
  306. <html lang="en">
  307. <head>
  308.     <meta charset="UTF-8">
  309.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  310.     <title>Database Explorer & Editor</title>
  311.     <!-- Minimal Bootstrap CSS for styling -->
  312.     <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  313.     <style>
  314.         body {
  315.             background-color: #f8f9fa;
  316.         }
  317.         .container {
  318.             max-width: 1200px;
  319.         }
  320.         .card {
  321.             border: 1px solid #dee2e6;
  322.             box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  323.         }
  324.         .table td[contenteditable="true"] {
  325.             cursor: pointer;
  326.             background-color: #fffaf0;
  327.         }
  328.         .table td:focus {
  329.             outline: 2px solid #0d6efd;
  330.             background-color: #fff;
  331.         }
  332.         /* Style for the floating Go to Top button */
  333.         #go-to-top-btn {
  334.             position: fixed;
  335.             bottom: 20px;
  336.             right: 20px;
  337.             display: none; /* Hidden by default */
  338.             z-index: 99;
  339.             border: none;
  340.             outline: none;
  341.             background-color: #0d6efd;
  342.             color: white;
  343.             cursor: pointer;
  344.             padding: 15px;
  345.             border-radius: 50%;
  346.             font-size: 18px;
  347.             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  348.             transition: opacity 0.3s;
  349.         }
  350.  
  351.         #go-to-top-btn:hover {
  352.             opacity: 0.8;
  353.         }
  354.     </style>
  355. </head>
  356. <body>
  357.     <div class="container my-5">
  358.         <h1 class="text-center mb-4">Database Explorer & Editor</h1>
  359.        
  360.         <div class="card p-4">
  361.             <!-- Table Selection -->
  362.             <div class="mb-4">
  363.                 <label for="tableSelect" class="form-label fw-bold">1. Please select a table:</label>
  364.                 <select id="tableSelect" class="form-select">
  365.                     <option value="" disabled selected>Choose a table</option>
  366.                     <?php
  367.                     foreach ($tables as $table) {
  368.                         echo '<option value="' . htmlspecialchars($table) . '">' . htmlspecialchars($table) . '</option>';
  369.                     }
  370.                     ?>
  371.                 </select>
  372.             </div>
  373.  
  374.             <div id="tableInfo" class="mb-4">
  375.                 <!-- Primary Key and Auto-Increment Info will be displayed here -->
  376.             </div>
  377.  
  378.             <!-- Column Selection -->
  379.             <div id="columnCheckboxes" class="mb-4 d-none">
  380.                 <label class="form-label fw-bold">2. Please select the columns to display:</label>
  381.                 <div class="mb-2">
  382.                     <button id="selectAllBtn" class="btn btn-sm btn-outline-secondary">Select All</button>
  383.                     <button id="selectNoneBtn" class="btn btn-sm btn-outline-secondary">Select None</button>
  384.                     <button id="toggleSelectionBtn" class="btn btn-sm btn-outline-secondary">Toggle Selection</button>
  385.                 </div>
  386.                 <div id="columnList" class="border p-3 rounded">
  387.                     <!-- Checkboxes will be dynamically added here -->
  388.                 </div>
  389.             </div>
  390.  
  391.             <!-- Query Box -->
  392.             <div id="queryContainer" class="mb-4 d-none">
  393.                 <label for="queryBox" class="form-label fw-bold">3. Your Query:</label>
  394.                 <textarea id="queryBox" class="form-control" rows="3"></textarea>
  395.             </div>
  396.  
  397.             <!-- Action Buttons -->
  398.             <div id="buttonContainer" class="mb-4 d-none">
  399.                 <button id="executeQueryBtn" class="btn btn-primary me-2">Execute Query</button>
  400.                 <button id="exportCsvBtn" class="btn btn-success">Export to CSV</button>
  401.             </div>
  402.         </div>
  403.  
  404.         <!-- Query Result and Update Section -->
  405.         <div class="mt-4">
  406.             <h2 class="text-center mb-3">Query Results</h2>
  407.             <div id="resultContainer">
  408.                 <!-- Query results will be displayed here -->
  409.             </div>
  410.             <div id="messageBox" class="mt-3">
  411.                 <!-- Status messages (e.g., success/error) will be displayed here -->
  412.             </div>
  413.         </div>
  414.        
  415.         <!-- Floating Go to Top button -->
  416.         <button id="go-to-top-btn" title="Go to top">&#8679;</button>
  417.  
  418.         <!-- Hidden form for CSV export -->
  419.         <form id="exportForm" method="POST" action="" class="d-none">
  420.             <input type="hidden" name="action" value="export_csv">
  421.             <input type="hidden" name="table" id="exportTableName">
  422.             <input type="hidden" name="query" id="exportQuery">
  423.         </form>
  424.  
  425.     </div>
  426.  
  427.     <!-- Minimal Bootstrap JS -->
  428.     <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  429.     <script>
  430.     // --- JavaScript Frontend Logic ---
  431.     document.addEventListener('DOMContentLoaded', function() {
  432.         const tableSelect = document.getElementById('tableSelect');
  433.         const tableInfo = document.getElementById('tableInfo');
  434.         const columnCheckboxes = document.getElementById('columnCheckboxes');
  435.         const columnList = document.getElementById('columnList');
  436.         const queryBox = document.getElementById('queryBox');
  437.         const queryContainer = document.getElementById('queryContainer');
  438.         const buttonContainer = document.getElementById('buttonContainer');
  439.         const executeQueryBtn = document.getElementById('executeQueryBtn');
  440.         const exportCsvBtn = document.getElementById('exportCsvBtn');
  441.         const resultContainer = document.getElementById('resultContainer');
  442.         const messageBox = document.getElementById('messageBox');
  443.         const exportForm = document.getElementById('exportForm');
  444.         const exportTableName = document.getElementById('exportTableName');
  445.         const exportQuery = document.getElementById('exportQuery');
  446.         const goToTopBtn = document.getElementById('go-to-top-btn');
  447.  
  448.         const selectAllBtn = document.getElementById('selectAllBtn');
  449.         const selectNoneBtn = document.getElementById('selectNoneBtn');
  450.         const toggleSelectionBtn = document.getElementById('toggleSelectionBtn');
  451.  
  452.         let selectedTable = '';
  453.         let primaryKey = '';
  454.         let autoIncrement = '';
  455.  
  456.         // Function to show a temporary message
  457.         function showMessage(msg, type) {
  458.             messageBox.innerHTML = `<div class="alert alert-${type}" role="alert">${msg}</div>`;
  459.             setTimeout(() => {
  460.                 messageBox.innerHTML = '';
  461.             }, 5000);
  462.         }
  463.  
  464.         // Event listener for table selection dropdown
  465.         tableSelect.addEventListener('change', function() {
  466.             selectedTable = this.value;
  467.             resultContainer.innerHTML = '';
  468.             messageBox.innerHTML = '';
  469.  
  470.             if (selectedTable) {
  471.                 const formData = new FormData();
  472.                 formData.append('action', 'get_columns');
  473.                 formData.append('table', selectedTable);
  474.  
  475.                 fetch('', {
  476.                     method: 'POST',
  477.                     body: formData
  478.                 })
  479.                 .then(response => response.json())
  480.                 .then(data => {
  481.                     if (data.error) {
  482.                         showMessage(data.error, 'danger');
  483.                         return;
  484.                     }
  485.  
  486.                     primaryKey = data.primary_key;
  487.                     autoIncrement = data.auto_increment;
  488.                     let infoHtml = `<p class="mb-1"><strong>Primary Key:</strong> ${primaryKey || 'None'}</p>`;
  489.                     if (autoIncrement) {
  490.                          infoHtml += `<p><strong>Auto-Increment Column:</strong> ${autoIncrement}</p>`;
  491.                     }
  492.                     tableInfo.innerHTML = infoHtml;
  493.                    
  494.                     columnCheckboxes.classList.remove('d-none');
  495.                     queryContainer.classList.remove('d-none');
  496.                     buttonContainer.classList.remove('d-none');
  497.                     columnList.innerHTML = '';
  498.                    
  499.                     data.columns.forEach(col => {
  500.                         const div = document.createElement('div');
  501.                         div.className = 'form-check form-check-inline';
  502.                         div.innerHTML = `
  503.                             <input class="form-check-input" type="checkbox" id="col-${col}" value="${col}" checked>
  504.                             <label class="form-check-label" for="col-${col}">${col}</label>
  505.                         `;
  506.                         columnList.appendChild(div);
  507.                     });
  508.  
  509.                     updateQueryBox();
  510.                 })
  511.                 .catch(error => {
  512.                     console.error('Error:', error);
  513.                     showMessage('An error occurred while fetching table information.', 'danger');
  514.                 });
  515.             } else {
  516.                 columnCheckboxes.classList.add('d-none');
  517.                 queryContainer.classList.add('d-none');
  518.                 buttonContainer.classList.add('d-none');
  519.                 tableInfo.innerHTML = '';
  520.             }
  521.         });
  522.  
  523.         // Event listener for column checkboxes to update the query box
  524.         columnList.addEventListener('change', function() {
  525.             updateQueryBox();
  526.         });
  527.  
  528.         selectAllBtn.addEventListener('click', function() {
  529.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  530.             checkboxes.forEach(checkbox => {
  531.                 checkbox.checked = true;
  532.             });
  533.             updateQueryBox();
  534.         });
  535.  
  536.         selectNoneBtn.addEventListener('click', function() {
  537.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  538.             checkboxes.forEach(checkbox => {
  539.                 checkbox.checked = false;
  540.             });
  541.             updateQueryBox();
  542.         });
  543.  
  544.         toggleSelectionBtn.addEventListener('click', function() {
  545.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  546.             checkboxes.forEach(checkbox => {
  547.                 checkbox.checked = !checkbox.checked;
  548.             });
  549.             updateQueryBox();
  550.         });
  551.        
  552.         function updateQueryBox() {
  553.             const checkedCols = Array.from(columnList.querySelectorAll('input:checked'))
  554.                 .map(input => input.value);
  555.             const selectClause = checkedCols.length > 0 ? checkedCols.map(col => `\`${col}\``).join(', ') : '*';
  556.             queryBox.value = `SELECT ${selectClause} FROM \`${selectedTable}\` WHERE 1 LIMIT 10`;
  557.         }
  558.  
  559.         // Event listener for "Execute Query" button
  560.         executeQueryBtn.addEventListener('click', function() {
  561.             const query = queryBox.value;
  562.             if (!query) {
  563.                 showMessage('The query box cannot be empty. Please enter a valid query.', 'warning');
  564.                 return;
  565.             }
  566.  
  567.             const formData = new FormData();
  568.             formData.append('action', 'execute_query');
  569.             formData.append('table', selectedTable);
  570.             formData.append('query', query);
  571.             formData.append('primary_key', primaryKey);
  572.            
  573.             executeQueryBtn.disabled = true;
  574.             executeQueryBtn.textContent = 'Executing...';
  575.  
  576.             fetch('', {
  577.                 method: 'POST',
  578.                 body: formData
  579.             })
  580.             .then(response => response.text())
  581.             .then(html => {
  582.                 resultContainer.innerHTML = html;
  583.                 executeQueryBtn.disabled = false;
  584.                 executeQueryBtn.textContent = 'Execute Query';
  585.                
  586.                 const updateChangesBtn = document.getElementById('updateChangesBtn');
  587.                 if (updateChangesBtn) {
  588.                     updateChangesBtn.addEventListener('click', updateRecords);
  589.                 }
  590.                
  591.                 const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  592.                 cells.forEach(cell => {
  593.                     cell.dataset.originalValue = cell.textContent.trim();
  594.                 });
  595.             })
  596.             .catch(error => {
  597.                 console.error('Error:', error);
  598.                 showMessage('An unexpected error occurred while executing the query. Please check the console for more details.', 'danger');
  599.                 executeQueryBtn.disabled = false;
  600.                 executeQueryBtn.textContent = 'Execute Query';
  601.             });
  602.         });
  603.  
  604.         // Event listener for "Export to CSV" button
  605.         exportCsvBtn.addEventListener('click', function() {
  606.             const query = queryBox.value;
  607.             if (!query) {
  608.                 showMessage('The query box cannot be empty. Please enter a valid query to export.', 'warning');
  609.                 return;
  610.             }
  611.            
  612.             exportTableName.value = selectedTable;
  613.             exportQuery.value = query;
  614.             exportForm.submit();
  615.         });
  616.  
  617.         // Function to handle updating multiple records
  618.         function updateRecords() {
  619.             const tableRows = resultContainer.querySelectorAll('tbody tr');
  620.             const updates = [];
  621.            
  622.             if (!primaryKey) {
  623.                 showMessage('Cannot update. A primary key was not found for this table.', 'danger');
  624.                 return;
  625.             }
  626.  
  627.             tableRows.forEach(row => {
  628.                 const pkValue = row.dataset.pkValue;
  629.                 if (!pkValue) return;
  630.  
  631.                 const changes = {};
  632.                 const cells = row.querySelectorAll('td[contenteditable="true"]');
  633.                 cells.forEach(cell => {
  634.                     const colName = cell.dataset.colName;
  635.                     const currentValue = cell.textContent.trim();
  636.                     const originalValue = cell.dataset.originalValue;
  637.                    
  638.                     if (currentValue !== originalValue) {
  639.                         changes[colName] = currentValue;
  640.                     }
  641.                 });
  642.                
  643.                 if (Object.keys(changes).length > 0) {
  644.                     updates.push({
  645.                         pk_value: pkValue,
  646.                         changes: changes
  647.                     });
  648.                 }
  649.             });
  650.            
  651.             if (updates.length === 0) {
  652.                 showMessage('No changes were detected in the table. Nothing to save.', 'info');
  653.                 return;
  654.             }
  655.            
  656.             const formData = new FormData();
  657.             formData.append('action', 'update_records');
  658.             formData.append('table', selectedTable);
  659.             formData.append('primary_key', primaryKey);
  660.             formData.append('data', JSON.stringify(updates));
  661.            
  662.             const updateBtn = document.getElementById('updateChangesBtn');
  663.             updateBtn.disabled = true;
  664.             updateBtn.textContent = 'Updating...';
  665.            
  666.             fetch('', {
  667.                 method: 'POST',
  668.                 body: formData
  669.             })
  670.             .then(response => response.json())
  671.             .then(data => {
  672.                 if (data.success) {
  673.                     showMessage(data.message, 'success');
  674.                     const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  675.                     cells.forEach(cell => {
  676.                         cell.dataset.originalValue = cell.textContent.trim();
  677.                     });
  678.                    
  679.                 } else {
  680.                     showMessage(data.message, 'danger');
  681.                 }
  682.                 updateBtn.disabled = false;
  683.                 updateBtn.textContent = 'Save All Changes';
  684.             })
  685.             .catch(error => {
  686.                 console.error('Error:', error);
  687.                 showMessage('An unexpected error occurred during the update process. Please check the console for more details.', 'danger');
  688.                 updateBtn.disabled = false;
  689.                 updateBtn.textContent = 'Save All Changes';
  690.             });
  691.         }
  692.        
  693.         // --- Go to Top button functionality ---
  694.         window.onscroll = function() {
  695.             if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
  696.                 goToTopBtn.style.display = "block";
  697.             } else {
  698.                 goToTopBtn.style.display = "none";
  699.             }
  700.         };
  701.  
  702.         goToTopBtn.addEventListener('click', function() {
  703.             document.body.scrollTop = 0; // For Safari
  704.             document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
  705.         });
  706.     });
  707.     </script>
  708. </body>
  709. </html>
  710.  
Advertisement
Add Comment
Please, Sign In to add comment