mayankjoin3

Dynamic Tables Query and Manual Query

Aug 18th, 2025
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 28.81 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.                 // NEW: Use the new filename format
  250.                 $timestamp = date('Y_m_d_H_i_s');
  251.                 $filename = $table_name . '_' . $timestamp . '.csv';
  252.                 header('Content-Type: text/csv; charset=utf-8');
  253.                 header('Content-Disposition: attachment; filename="' . $filename . '"');
  254.                
  255.                 $output = fopen('php://output', 'w');
  256.                
  257.                 // Get column headers and write to CSV
  258.                 $field_info = $result->fetch_fields();
  259.                 $headers = [];
  260.                 foreach ($field_info as $val) {
  261.                     $headers[] = $val->name;
  262.                 }
  263.                 fputcsv($output, $headers);
  264.  
  265.                 // Write data rows to CSV
  266.                 while ($row = $result->fetch_assoc()) {
  267.                     fputcsv($output, $row);
  268.                 }
  269.                
  270.                 fclose($output);
  271.                 $result->free();
  272.             } catch (mysqli_sql_exception $e) {
  273.                 log_error("CSV export failed for query '$query'. Error: " . $e->getMessage(), __FILE__, __LINE__);
  274.                 die("An error occurred during the CSV export process. Please check the logs.");
  275.             }
  276.             exit;
  277.         }
  278.  
  279.         // Action 5: New Action to get unique values for a column
  280.         // This action is now unused but kept in case a future feature needs it.
  281.         // It's no longer used for the textboxes.
  282.         if ($action === 'get_column_values') {
  283.             $table_name = isset($_POST['table']) ? clean_input($_POST['table']) : '';
  284.             $column_name = isset($_POST['column']) ? clean_input($_POST['column']) : '';
  285.            
  286.             if (empty($table_name) || empty($column_name)) {
  287.                 die(json_encode(['error' => 'Table name or column name is missing.']));
  288.             }
  289.  
  290.             $response = ['values' => []];
  291.  
  292.             try {
  293.                 // Fetch unique values from the column, sorted by value
  294.                 $query = "SELECT DISTINCT `" . $column_name . "` FROM `" . $table_name . "` ORDER BY `" . $column_name . "`";
  295.                 $result = $mysqli->query($query);
  296.                 if ($result) {
  297.                     while ($row = $result->fetch_row()) {
  298.                         $response['values'][] = $row[0];
  299.                     }
  300.                     $result->free();
  301.                 }
  302.  
  303.                 header('Content-Type: application/json');
  304.                 echo json_encode($response);
  305.             } catch (mysqli_sql_exception $e) {
  306.                 log_error("Failed to get unique values for column '$column_name' on table '$table_name': " . $e->getMessage(), __FILE__, __LINE__);
  307.                 die(json_encode(['error' => 'An error occurred while fetching column values.']));
  308.             }
  309.             exit;
  310.         }
  311.     }
  312. } catch (Exception $e) {
  313.     // Catch-all for any uncaught exceptions
  314.     log_error("An unexpected error occurred: " . $e->getMessage(), __FILE__, __LINE__);
  315.     // Provide a generic, polite message to the user
  316.     die("An unexpected error occurred on the server. We apologize for the inconvenience.");
  317. }
  318.  
  319. // --- Main PHP logic for initial page load ---
  320. // Get the list of all tables in the database
  321. $tables = [];
  322. try {
  323.     $result = $mysqli->query("SHOW TABLES FROM `$db_name`");
  324.     if ($result) {
  325.         while ($row = $result->fetch_row()) {
  326.             $tables[] = $row[0];
  327.         }
  328.         $result->free();
  329.         sort($tables);
  330.     }
  331. } catch (mysqli_sql_exception $e) {
  332.     log_error("Failed to list tables: " . $e->getMessage(), __FILE__, __LINE__);
  333.     // Continue with an empty array of tables, the frontend will display an error message
  334. }
  335.  
  336. // Close the connection
  337. $mysqli->close();
  338. ?>
  339.  
  340. <!DOCTYPE html>
  341. <html lang="en">
  342. <head>
  343.     <meta charset="UTF-8">
  344.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  345.     <title>Database Explorer & Editor</title>
  346.     <!-- Minimal Bootstrap CSS for styling -->
  347.     <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  348.     <style>
  349.         body {
  350.             background-color: #f8f9fa;
  351.         }
  352.         .container {
  353.             max-width: 1200px;
  354.         }
  355.         .card {
  356.             border: 1px solid #dee2e6;
  357.             box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  358.         }
  359.         .table td[contenteditable="true"] {
  360.             cursor: pointer;
  361.             background-color: #fffaf0;
  362.         }
  363.         .table td:focus {
  364.             outline: 2px solid #0d6efd;
  365.             background-color: #fff;
  366.         }
  367.         /* Style for the floating Go to Top button */
  368.         #go-to-top-btn {
  369.             position: fixed;
  370.             bottom: 20px;
  371.             right: 20px;
  372.             display: none; /* Hidden by default */
  373.             z-index: 99;
  374.             border: none;
  375.             outline: none;
  376.             background-color: #0d6efd;
  377.             color: white;
  378.             cursor: pointer;
  379.             padding: 15px;
  380.             border-radius: 50%;
  381.             font-size: 18px;
  382.             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  383.             transition: opacity 0.3s;
  384.         }
  385.  
  386.         #go-to-top-btn:hover {
  387.             opacity: 0.8;
  388.         }
  389.     </style>
  390. </head>
  391. <body>
  392.     <div class="container my-5">
  393.         <h1 class="text-center mb-4">Database Explorer & Editor</h1>
  394.        
  395.         <div class="card p-4">
  396.             <!-- Table Selection -->
  397.             <div class="mb-4">
  398.                 <label for="tableSelect" class="form-label fw-bold">1. Please select a table:</label>
  399.                 <select id="tableSelect" class="form-select">
  400.                     <option value="" disabled selected>Choose a table</option>
  401.                     <?php
  402.                     foreach ($tables as $table) {
  403.                         echo '<option value="' . htmlspecialchars($table) . '">' . htmlspecialchars($table) . '</option>';
  404.                     }
  405.                     ?>
  406.                 </select>
  407.             </div>
  408.  
  409.             <div id="tableInfo" class="mb-4">
  410.                 <!-- Primary Key and Auto-Increment Info will be displayed here -->
  411.             </div>
  412.  
  413.             <!-- Query Box -->
  414.             <div id="queryContainer" class="mb-4 d-none">
  415.                 <label for="queryBox" class="form-label fw-bold">2. Your Query:</label>
  416.                 <textarea id="queryBox" class="form-control" rows="3"></textarea>
  417.             </div>
  418.  
  419.             <!-- Action Buttons -->
  420.             <div id="buttonContainer" class="mb-4 d-none">
  421.                 <button id="executeQueryBtn" class="btn btn-primary me-2">Execute Query</button>
  422.                 <button id="exportCsvBtn" class="btn btn-success">Export to CSV</button>
  423.             </div>
  424.         </div>
  425.  
  426.         <!-- Query Result and Update Section -->
  427.         <div class="mt-4">
  428.             <h2 class="text-center mb-3">Query Results</h2>
  429.             <div id="resultContainer">
  430.                 <!-- Query results will be displayed here -->
  431.             </div>
  432.             <div id="messageBox" class="mt-3">
  433.                 <!-- Status messages (e.g., success/error) will be displayed here -->
  434.             </div>
  435.         </div>
  436.        
  437.         <!-- Floating Go to Top button -->
  438.         <button id="go-to-top-btn" title="Go to top">&#8679;</button>
  439.  
  440.         <!-- Hidden form for CSV export -->
  441.         <form id="exportForm" method="POST" action="" class="d-none">
  442.             <input type="hidden" name="action" value="export_csv">
  443.             <input type="hidden" name="table" id="exportTableName">
  444.             <input type="hidden" name="query" id="exportQuery">
  445.         </form>
  446.  
  447.     </div>
  448.  
  449.     <!-- Minimal Bootstrap JS -->
  450.     <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  451.     <script>
  452.     // --- JavaScript Frontend Logic ---
  453.     document.addEventListener('DOMContentLoaded', function() {
  454.         const tableSelect = document.getElementById('tableSelect');
  455.         const tableInfo = document.getElementById('tableInfo');
  456.         const queryBox = document.getElementById('queryBox');
  457.         const queryContainer = document.getElementById('queryContainer');
  458.         const buttonContainer = document.getElementById('buttonContainer');
  459.         const executeQueryBtn = document.getElementById('executeQueryBtn');
  460.         const exportCsvBtn = document.getElementById('exportCsvBtn');
  461.         const resultContainer = document.getElementById('resultContainer');
  462.         const messageBox = document.getElementById('messageBox');
  463.         const exportForm = document.getElementById('exportForm');
  464.         const exportTableName = document.getElementById('exportTableName');
  465.         const exportQuery = document.getElementById('exportQuery');
  466.         const goToTopBtn = document.getElementById('go-to-top-btn');
  467.  
  468.         let selectedTable = '';
  469.         let primaryKey = '';
  470.         let autoIncrement = '';
  471.  
  472.         // Function to show a temporary message
  473.         function showMessage(msg, type) {
  474.             messageBox.innerHTML = `<div class="alert alert-${type}" role="alert">${msg}</div>`;
  475.             setTimeout(() => {
  476.                 messageBox.innerHTML = '';
  477.             }, 5000);
  478.         }
  479.  
  480.         // Event listener for table selection dropdown
  481.         tableSelect.addEventListener('change', function() {
  482.             selectedTable = this.value;
  483.             resultContainer.innerHTML = '';
  484.             messageBox.innerHTML = '';
  485.  
  486.             if (selectedTable) {
  487.                 const formData = new FormData();
  488.                 formData.append('action', 'get_columns');
  489.                 formData.append('table', selectedTable);
  490.  
  491.                 fetch('', {
  492.                     method: 'POST',
  493.                     body: formData
  494.                 })
  495.                 .then(response => response.json())
  496.                 .then(data => {
  497.                     if (data.error) {
  498.                         showMessage(data.error, 'danger');
  499.                         return;
  500.                     }
  501.  
  502.                     primaryKey = data.primary_key;
  503.                     autoIncrement = data.auto_increment;
  504.                     let infoHtml = `<p class="mb-1"><strong>Primary Key:</strong> ${primaryKey || 'None'}</p>`;
  505.                     if (autoIncrement) {
  506.                          infoHtml += `<p><strong>Auto-Increment Column:</strong> ${autoIncrement}</p>`;
  507.                     }
  508.                     tableInfo.innerHTML = infoHtml;
  509.                    
  510.                     queryContainer.classList.remove('d-none');
  511.                     buttonContainer.classList.remove('d-none');
  512.                     // Automatically pre-populate the query box with a basic SELECT statement
  513.                     queryBox.value = `SELECT * FROM \`${selectedTable}\` LIMIT 10`;
  514.  
  515.                 })
  516.                 .catch(error => {
  517.                     console.error('Error:', error);
  518.                     showMessage('An error occurred while fetching table information.', 'danger');
  519.                 });
  520.             } else {
  521.                 queryContainer.classList.add('d-none');
  522.                 buttonContainer.classList.add('d-none');
  523.                 tableInfo.innerHTML = '';
  524.             }
  525.         });
  526.  
  527.         // Event listener for "Execute Query" button
  528.         executeQueryBtn.addEventListener('click', function() {
  529.             const query = queryBox.value;
  530.             if (!query) {
  531.                 showMessage('The query box cannot be empty. Please enter a valid query.', 'warning');
  532.                 return;
  533.             }
  534.  
  535.             const formData = new FormData();
  536.             formData.append('action', 'execute_query');
  537.             formData.append('table', selectedTable);
  538.             formData.append('query', query);
  539.             formData.append('primary_key', primaryKey);
  540.            
  541.             executeQueryBtn.disabled = true;
  542.             executeQueryBtn.textContent = 'Executing...';
  543.  
  544.             fetch('', {
  545.                 method: 'POST',
  546.                 body: formData
  547.             })
  548.             .then(response => response.text())
  549.             .then(html => {
  550.                 resultContainer.innerHTML = html;
  551.                 executeQueryBtn.disabled = false;
  552.                 executeQueryBtn.textContent = 'Execute Query';
  553.                
  554.                 const updateChangesBtn = document.getElementById('updateChangesBtn');
  555.                 if (updateChangesBtn) {
  556.                     updateChangesBtn.addEventListener('click', updateRecords);
  557.                 }
  558.                
  559.                 const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  560.                 cells.forEach(cell => {
  561.                     cell.dataset.originalValue = cell.textContent.trim();
  562.                 });
  563.             })
  564.             .catch(error => {
  565.                 console.error('Error:', error);
  566.                 showMessage('An unexpected error occurred while executing the query. Please check the console for more details.', 'danger');
  567.                 executeQueryBtn.disabled = false;
  568.                 executeQueryBtn.textContent = 'Execute Query';
  569.             });
  570.         });
  571.  
  572.         // Event listener for "Export to CSV" button
  573.         exportCsvBtn.addEventListener('click', function() {
  574.             const query = queryBox.value;
  575.             if (!query) {
  576.                 showMessage('The query box cannot be empty. Please enter a valid query to export.', 'warning');
  577.                 return;
  578.             }
  579.            
  580.             exportTableName.value = selectedTable;
  581.             exportQuery.value = query;
  582.             exportForm.submit();
  583.         });
  584.  
  585.         // Function to handle updating multiple records
  586.         function updateRecords() {
  587.             const tableRows = resultContainer.querySelectorAll('tbody tr');
  588.             const updates = [];
  589.            
  590.             if (!primaryKey) {
  591.                 showMessage('Cannot update. A primary key was not found for this table.', 'danger');
  592.                 return;
  593.             }
  594.  
  595.             tableRows.forEach(row => {
  596.                 const pkValue = row.dataset.pkValue;
  597.                 if (!pkValue) return;
  598.  
  599.                 const changes = {};
  600.                 const cells = row.querySelectorAll('td[contenteditable="true"]');
  601.                 cells.forEach(cell => {
  602.                     const colName = cell.dataset.colName;
  603.                     const currentValue = cell.textContent.trim();
  604.                     const originalValue = cell.dataset.originalValue;
  605.                    
  606.                     if (currentValue !== originalValue) {
  607.                         changes[colName] = currentValue;
  608.                     }
  609.                 });
  610.                
  611.                 if (Object.keys(changes).length > 0) {
  612.                     updates.push({
  613.                         pk_value: pkValue,
  614.                         changes: changes
  615.                     });
  616.                 }
  617.             });
  618.            
  619.             if (updates.length === 0) {
  620.                 showMessage('No changes were detected in the table. Nothing to save.', 'info');
  621.                 return;
  622.             }
  623.            
  624.             const formData = new FormData();
  625.             formData.append('action', 'update_records');
  626.             formData.append('table', selectedTable);
  627.             formData.append('primary_key', primaryKey);
  628.             formData.append('data', JSON.stringify(updates));
  629.            
  630.             const updateBtn = document.getElementById('updateChangesBtn');
  631.             updateBtn.disabled = true;
  632.             updateBtn.textContent = 'Updating...';
  633.            
  634.             fetch('', {
  635.                 method: 'POST',
  636.                 body: formData
  637.             })
  638.             .then(response => response.json())
  639.             .then(data => {
  640.                 if (data.success) {
  641.                     showMessage(data.message, 'success');
  642.                     const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  643.                     cells.forEach(cell => {
  644.                         cell.dataset.originalValue = cell.textContent.trim();
  645.                     });
  646.                    
  647.                 } else {
  648.                     showMessage(data.message, 'danger');
  649.                 }
  650.                 updateBtn.disabled = false;
  651.                 updateBtn.textContent = 'Save All Changes';
  652.             })
  653.             .catch(error => {
  654.                 console.error('Error:', error);
  655.                 showMessage('An unexpected error occurred during the update process. Please check the console for more details.', 'danger');
  656.                 updateBtn.disabled = false;
  657.                 updateBtn.textContent = 'Save All Changes';
  658.             });
  659.         }
  660.        
  661.         // --- Go to Top button functionality ---
  662.         window.onscroll = function() {
  663.             if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
  664.                 goToTopBtn.style.display = "block";
  665.             } else {
  666.                 goToTopBtn.style.display = "none";
  667.             }
  668.         };
  669.  
  670.         goToTopBtn.addEventListener('click', function() {
  671.             document.body.scrollTop = 0; // For Safari
  672.             document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
  673.         });
  674.     });
  675.     </script>
  676. </body>
  677. </html>
  678.  
Advertisement
Add Comment
Please, Sign In to add comment