mayankjoin3

Dynamic Tables Query with TextBox

Aug 18th, 2025
56
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 35.78 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.         // Action 5: New Action to get unique values for a column
  278.         // OLD: This action is now unused but kept in case a future feature needs it.
  279.         // It's no longer used for the textboxes.
  280.         if ($action === 'get_column_values') {
  281.             $table_name = isset($_POST['table']) ? clean_input($_POST['table']) : '';
  282.             $column_name = isset($_POST['column']) ? clean_input($_POST['column']) : '';
  283.            
  284.             if (empty($table_name) || empty($column_name)) {
  285.                 die(json_encode(['error' => 'Table name or column name is missing.']));
  286.             }
  287.  
  288.             $response = ['values' => []];
  289.  
  290.             try {
  291.                 // Fetch unique values from the column, sorted by value
  292.                 $query = "SELECT DISTINCT `" . $column_name . "` FROM `" . $table_name . "` ORDER BY `" . $column_name . "`";
  293.                 $result = $mysqli->query($query);
  294.                 if ($result) {
  295.                     while ($row = $result->fetch_row()) {
  296.                         $response['values'][] = $row[0];
  297.                     }
  298.                     $result->free();
  299.                 }
  300.  
  301.                 header('Content-Type: application/json');
  302.                 echo json_encode($response);
  303.             } catch (mysqli_sql_exception $e) {
  304.                 log_error("Failed to get unique values for column '$column_name' on table '$table_name': " . $e->getMessage(), __FILE__, __LINE__);
  305.                 die(json_encode(['error' => 'An error occurred while fetching column values.']));
  306.             }
  307.             exit;
  308.         }
  309.     }
  310. } catch (Exception $e) {
  311.     // Catch-all for any uncaught exceptions
  312.     log_error("An unexpected error occurred: " . $e->getMessage(), __FILE__, __LINE__);
  313.     // Provide a generic, polite message to the user
  314.     die("An unexpected error occurred on the server. We apologize for the inconvenience.");
  315. }
  316.  
  317. // --- Main PHP logic for initial page load ---
  318. // Get the list of all tables in the database
  319. $tables = [];
  320. try {
  321.     $result = $mysqli->query("SHOW TABLES FROM `$db_name`");
  322.     if ($result) {
  323.         while ($row = $result->fetch_row()) {
  324.             $tables[] = $row[0];
  325.         }
  326.         $result->free();
  327.         sort($tables);
  328.     }
  329. } catch (mysqli_sql_exception $e) {
  330.     log_error("Failed to list tables: " . $e->getMessage(), __FILE__, __LINE__);
  331.     // Continue with an empty array of tables, the frontend will display an error message
  332. }
  333.  
  334. // Close the connection
  335. $mysqli->close();
  336. ?>
  337.  
  338. <!DOCTYPE html>
  339. <html lang="en">
  340. <head>
  341.     <meta charset="UTF-8">
  342.     <meta name="viewport" content="width=device-width, initial-scale=1.0">
  343.     <title>Database Explorer & Editor</title>
  344.     <!-- Minimal Bootstrap CSS for styling -->
  345.     <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
  346.     <style>
  347.         body {
  348.             background-color: #f8f9fa;
  349.         }
  350.         .container {
  351.             max-width: 1200px;
  352.         }
  353.         .card {
  354.             border: 1px solid #dee2e6;
  355.             box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
  356.         }
  357.         .table td[contenteditable="true"] {
  358.             cursor: pointer;
  359.             background-color: #fffaf0;
  360.         }
  361.         .table td:focus {
  362.             outline: 2px solid #0d6efd;
  363.             background-color: #fff;
  364.         }
  365.         /* Style for the floating Go to Top button */
  366.         #go-to-top-btn {
  367.             position: fixed;
  368.             bottom: 20px;
  369.             right: 20px;
  370.             display: none; /* Hidden by default */
  371.             z-index: 99;
  372.             border: none;
  373.             outline: none;
  374.             background-color: #0d6efd;
  375.             color: white;
  376.             cursor: pointer;
  377.             padding: 15px;
  378.             border-radius: 50%;
  379.             font-size: 18px;
  380.             box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  381.             transition: opacity 0.3s;
  382.         }
  383.  
  384.         #go-to-top-btn:hover {
  385.             opacity: 0.8;
  386.         }
  387.     </style>
  388. </head>
  389. <body>
  390.     <div class="container my-5">
  391.         <h1 class="text-center mb-4">Database Explorer & Editor</h1>
  392.        
  393.         <div class="card p-4">
  394.             <!-- Table Selection -->
  395.             <div class="mb-4">
  396.                 <label for="tableSelect" class="form-label fw-bold">1. Please select a table:</label>
  397.                 <select id="tableSelect" class="form-select">
  398.                     <option value="" disabled selected>Choose a table</option>
  399.                     <?php
  400.                     foreach ($tables as $table) {
  401.                         echo '<option value="' . htmlspecialchars($table) . '">' . htmlspecialchars($table) . '</option>';
  402.                     }
  403.                     ?>
  404.                 </select>
  405.             </div>
  406.  
  407.             <div id="tableInfo" class="mb-4">
  408.                 <!-- Primary Key and Auto-Increment Info will be displayed here -->
  409.             </div>
  410.  
  411.             <!-- Column Selection -->
  412.             <div id="columnCheckboxes" class="mb-4 d-none">
  413.                 <label class="form-label fw-bold">2. Please select the columns to display and filter:</label>
  414.                 <div class="mb-2">
  415.                     <button id="selectAllBtn" class="btn btn-sm btn-outline-secondary">Select All</button>
  416.                     <button id="selectNoneBtn" class="btn btn-sm btn-outline-secondary">Select None</button>
  417.                     <button id="toggleSelectionBtn" class="btn btn-sm btn-outline-secondary">Toggle Selection</button>
  418.                 </div>
  419.                 <!-- OLD: <div id="columnList" class="border p-3 rounded"> -->
  420.                 <!-- NEW: Updated structure to accommodate inline textboxes -->
  421.                 <div id="columnList" class="border p-3 rounded d-flex flex-wrap gap-3">
  422.                     <!-- Checkboxes and textboxes will be dynamically added here -->
  423.                 </div>
  424.             </div>
  425.  
  426.             <!-- Query Box -->
  427.             <div id="queryContainer" class="mb-4 d-none">
  428.                 <label for="queryBox" class="form-label fw-bold">3. Your Query:</label>
  429.                 <textarea id="queryBox" class="form-control" rows="3"></textarea>
  430.             </div>
  431.  
  432.             <!-- Action Buttons -->
  433.             <div id="buttonContainer" class="mb-4 d-none">
  434.                 <button id="executeQueryBtn" class="btn btn-primary me-2">Execute Query</button>
  435.                 <button id="exportCsvBtn" class="btn btn-success">Export to CSV</button>
  436.             </div>
  437.         </div>
  438.  
  439.         <!-- Query Result and Update Section -->
  440.         <div class="mt-4">
  441.             <h2 class="text-center mb-3">Query Results</h2>
  442.             <div id="resultContainer">
  443.                 <!-- Query results will be displayed here -->
  444.             </div>
  445.             <div id="messageBox" class="mt-3">
  446.                 <!-- Status messages (e.g., success/error) will be displayed here -->
  447.             </div>
  448.         </div>
  449.        
  450.         <!-- Floating Go to Top button -->
  451.         <button id="go-to-top-btn" title="Go to top">&#8679;</button>
  452.  
  453.         <!-- Hidden form for CSV export -->
  454.         <form id="exportForm" method="POST" action="" class="d-none">
  455.             <input type="hidden" name="action" value="export_csv">
  456.             <input type="hidden" name="table" id="exportTableName">
  457.             <input type="hidden" name="query" id="exportQuery">
  458.         </form>
  459.  
  460.     </div>
  461.  
  462.     <!-- Minimal Bootstrap JS -->
  463.     <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
  464.     <script>
  465.     // --- JavaScript Frontend Logic ---
  466.     document.addEventListener('DOMContentLoaded', function() {
  467.         const tableSelect = document.getElementById('tableSelect');
  468.         const tableInfo = document.getElementById('tableInfo');
  469.         const columnCheckboxes = document.getElementById('columnCheckboxes');
  470.         const columnList = document.getElementById('columnList');
  471.         const queryBox = document.getElementById('queryBox');
  472.         const queryContainer = document.getElementById('queryContainer');
  473.         const buttonContainer = document.getElementById('buttonContainer');
  474.         const executeQueryBtn = document.getElementById('executeQueryBtn');
  475.         const exportCsvBtn = document.getElementById('exportCsvBtn');
  476.         const resultContainer = document.getElementById('resultContainer');
  477.         const messageBox = document.getElementById('messageBox');
  478.         const exportForm = document.getElementById('exportForm');
  479.         const exportTableName = document.getElementById('exportTableName');
  480.         const exportQuery = document.getElementById('exportQuery');
  481.         const goToTopBtn = document.getElementById('go-to-top-btn');
  482.  
  483.         const selectAllBtn = document.getElementById('selectAllBtn');
  484.         const selectNoneBtn = document.getElementById('selectNoneBtn');
  485.         const toggleSelectionBtn = document.getElementById('toggleSelectionBtn');
  486.  
  487.         let selectedTable = '';
  488.         let primaryKey = '';
  489.         let autoIncrement = '';
  490.  
  491.         // Function to show a temporary message
  492.         function showMessage(msg, type) {
  493.             messageBox.innerHTML = `<div class="alert alert-${type}" role="alert">${msg}</div>`;
  494.             setTimeout(() => {
  495.                 messageBox.innerHTML = '';
  496.             }, 5000);
  497.         }
  498.  
  499.         // Event listener for table selection dropdown
  500.         tableSelect.addEventListener('change', function() {
  501.             selectedTable = this.value;
  502.             resultContainer.innerHTML = '';
  503.             messageBox.innerHTML = '';
  504.  
  505.             if (selectedTable) {
  506.                 const formData = new FormData();
  507.                 formData.append('action', 'get_columns');
  508.                 formData.append('table', selectedTable);
  509.  
  510.                 fetch('', {
  511.                     method: 'POST',
  512.                     body: formData
  513.                 })
  514.                 .then(response => response.json())
  515.                 .then(data => {
  516.                     if (data.error) {
  517.                         showMessage(data.error, 'danger');
  518.                         return;
  519.                     }
  520.  
  521.                     primaryKey = data.primary_key;
  522.                     autoIncrement = data.auto_increment;
  523.                     let infoHtml = `<p class="mb-1"><strong>Primary Key:</strong> ${primaryKey || 'None'}</p>`;
  524.                     if (autoIncrement) {
  525.                          infoHtml += `<p><strong>Auto-Increment Column:</strong> ${autoIncrement}</p>`;
  526.                     }
  527.                     tableInfo.innerHTML = infoHtml;
  528.                    
  529.                     columnCheckboxes.classList.remove('d-none');
  530.                     queryContainer.classList.remove('d-none');
  531.                     buttonContainer.classList.remove('d-none');
  532.                     columnList.innerHTML = '';
  533.                    
  534.                     data.columns.forEach(col => {
  535.                         // Create a container for the checkbox and textbox
  536.                         const itemContainer = document.createElement('div');
  537.                         itemContainer.className = 'd-flex flex-column align-items-start';
  538.                         itemContainer.dataset.columnName = col;
  539.                        
  540.                         // Create checkbox and label
  541.                         const checkboxDiv = document.createElement('div');
  542.                         checkboxDiv.className = 'form-check';
  543.                         checkboxDiv.innerHTML = `
  544.                             <input class="form-check-input" type="checkbox" id="col-${col}" value="${col}" checked>
  545.                             <label class="form-check-label" for="col-${col}">${col}</label>
  546.                         `;
  547.                        
  548.                         // Create textbox
  549.                         const textbox = document.createElement('input');
  550.                         textbox.type = 'text';
  551.                         textbox.id = `filter-${col}`;
  552.                         textbox.className = 'form-control form-control-sm mt-1';
  553.                         textbox.placeholder = 'Enter values (e.g., a,b,c)';
  554.                        
  555.                         // Append to the list and set up a change listener for the checkbox
  556.                         itemContainer.appendChild(checkboxDiv);
  557.                         itemContainer.appendChild(textbox);
  558.                         columnList.appendChild(itemContainer);
  559.  
  560.                         // Attach event listeners for updating the query
  561.                         checkboxDiv.querySelector('input').addEventListener('change', updateQueryBox);
  562.                         textbox.addEventListener('input', updateQueryBox);
  563.  
  564.                     });
  565.  
  566.                     updateQueryBox();
  567.                 })
  568.                 .catch(error => {
  569.                     console.error('Error:', error);
  570.                     showMessage('An error occurred while fetching table information.', 'danger');
  571.                 });
  572.             } else {
  573.                 columnCheckboxes.classList.add('d-none');
  574.                 queryContainer.classList.add('d-none');
  575.                 buttonContainer.classList.add('d-none');
  576.                 tableInfo.innerHTML = '';
  577.             }
  578.         });
  579.  
  580.         // Event listener for column checkboxes to update the query box
  581.         columnList.addEventListener('change', function(e) {
  582.             if (e.target.matches('input[type="checkbox"]')) {
  583.                 const container = e.target.closest('[data-column-name]');
  584.                 const textbox = container.querySelector('input[type="text"]');
  585.                
  586.                 if (e.target.checked) {
  587.                     textbox.style.display = '';
  588.                 } else {
  589.                     textbox.style.display = 'none';
  590.                     textbox.value = ''; // Clear value when unchecked
  591.                 }
  592.                 updateQueryBox();
  593.             }
  594.         });
  595.  
  596.         selectAllBtn.addEventListener('click', function() {
  597.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  598.             checkboxes.forEach(checkbox => {
  599.                 checkbox.checked = true;
  600.                 const container = checkbox.closest('[data-column-name]');
  601.                 container.querySelector('input[type="text"]').style.display = '';
  602.             });
  603.             updateQueryBox();
  604.         });
  605.  
  606.         selectNoneBtn.addEventListener('click', function() {
  607.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  608.             checkboxes.forEach(checkbox => {
  609.                 checkbox.checked = false;
  610.                 const textbox = checkbox.closest('[data-column-name]').querySelector('input[type="text"]');
  611.                 textbox.style.display = 'none';
  612.                 textbox.value = ''; // Clear value when unchecked
  613.             });
  614.             updateQueryBox();
  615.         });
  616.  
  617.         toggleSelectionBtn.addEventListener('click', function() {
  618.             const checkboxes = columnList.querySelectorAll('input[type="checkbox"]');
  619.             checkboxes.forEach(checkbox => {
  620.                 checkbox.checked = !checkbox.checked;
  621.                 const textbox = checkbox.closest('[data-column-name]').querySelector('input[type="text"]');
  622.                 if (checkbox.checked) {
  623.                     textbox.style.display = '';
  624.                 } else {
  625.                     textbox.style.display = 'none';
  626.                     textbox.value = ''; // Clear value when unchecked
  627.                 }
  628.             });
  629.             updateQueryBox();
  630.         });
  631.        
  632.         // Function to build the query from selected columns and filter values
  633.         function updateQueryBox() {
  634.             const checkedCols = Array.from(columnList.querySelectorAll('input[type="checkbox"]:checked'))
  635.                 .map(input => `\`${input.value}\``);
  636.            
  637.             const whereClauses = [];
  638.             const allItemContainers = columnList.querySelectorAll('[data-column-name]');
  639.            
  640.             allItemContainers.forEach(container => {
  641.                 const column = container.dataset.columnName;
  642.                 const checkbox = container.querySelector('input[type="checkbox"]');
  643.                 const textbox = container.querySelector('input[type="text"]');
  644.                
  645.                 if (checkbox && checkbox.checked && textbox && textbox.style.display !== 'none') {
  646.                     const values = textbox.value.split(',').map(v => v.trim()).filter(v => v.length > 0);
  647.                     if (values.length > 0) {
  648.                         const formattedValues = values.map(val => `'${val.replace(/'/g, "\\'")}'`).join(', ');
  649.                         whereClauses.push(`\`${column}\` IN (${formattedValues})`);
  650.                     }
  651.                 }
  652.             });
  653.            
  654.             const selectClause = checkedCols.length > 0 ? checkedCols.join(', ') : '*';
  655.             const whereClause = whereClauses.length > 0 ? ' WHERE ' + whereClauses.join(' AND ') : '';
  656.            
  657.             queryBox.value = `SELECT ${selectClause} FROM \`${selectedTable}\`${whereClause} LIMIT 10`;
  658.         }
  659.  
  660.         // Event listener for "Execute Query" button
  661.         executeQueryBtn.addEventListener('click', function() {
  662.             const query = queryBox.value;
  663.             if (!query) {
  664.                 showMessage('The query box cannot be empty. Please enter a valid query.', 'warning');
  665.                 return;
  666.             }
  667.  
  668.             const formData = new FormData();
  669.             formData.append('action', 'execute_query');
  670.             formData.append('table', selectedTable);
  671.             formData.append('query', query);
  672.             formData.append('primary_key', primaryKey);
  673.            
  674.             executeQueryBtn.disabled = true;
  675.             executeQueryBtn.textContent = 'Executing...';
  676.  
  677.             fetch('', {
  678.                 method: 'POST',
  679.                 body: formData
  680.             })
  681.             .then(response => response.text())
  682.             .then(html => {
  683.                 resultContainer.innerHTML = html;
  684.                 executeQueryBtn.disabled = false;
  685.                 executeQueryBtn.textContent = 'Execute Query';
  686.                
  687.                 const updateChangesBtn = document.getElementById('updateChangesBtn');
  688.                 if (updateChangesBtn) {
  689.                     updateChangesBtn.addEventListener('click', updateRecords);
  690.                 }
  691.                
  692.                 const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  693.                 cells.forEach(cell => {
  694.                     cell.dataset.originalValue = cell.textContent.trim();
  695.                 });
  696.             })
  697.             .catch(error => {
  698.                 console.error('Error:', error);
  699.                 showMessage('An unexpected error occurred while executing the query. Please check the console for more details.', 'danger');
  700.                 executeQueryBtn.disabled = false;
  701.                 executeQueryBtn.textContent = 'Execute Query';
  702.             });
  703.         });
  704.  
  705.         // Event listener for "Export to CSV" button
  706.         exportCsvBtn.addEventListener('click', function() {
  707.             const query = queryBox.value;
  708.             if (!query) {
  709.                 showMessage('The query box cannot be empty. Please enter a valid query to export.', 'warning');
  710.                 return;
  711.             }
  712.            
  713.             exportTableName.value = selectedTable;
  714.             exportQuery.value = query;
  715.             exportForm.submit();
  716.         });
  717.  
  718.         // Function to handle updating multiple records
  719.         function updateRecords() {
  720.             const tableRows = resultContainer.querySelectorAll('tbody tr');
  721.             const updates = [];
  722.            
  723.             if (!primaryKey) {
  724.                 showMessage('Cannot update. A primary key was not found for this table.', 'danger');
  725.                 return;
  726.             }
  727.  
  728.             tableRows.forEach(row => {
  729.                 const pkValue = row.dataset.pkValue;
  730.                 if (!pkValue) return;
  731.  
  732.                 const changes = {};
  733.                 const cells = row.querySelectorAll('td[contenteditable="true"]');
  734.                 cells.forEach(cell => {
  735.                     const colName = cell.dataset.colName;
  736.                     const currentValue = cell.textContent.trim();
  737.                     const originalValue = cell.dataset.originalValue;
  738.                    
  739.                     if (currentValue !== originalValue) {
  740.                         changes[colName] = currentValue;
  741.                     }
  742.                 });
  743.                
  744.                 if (Object.keys(changes).length > 0) {
  745.                     updates.push({
  746.                         pk_value: pkValue,
  747.                         changes: changes
  748.                     });
  749.                 }
  750.             });
  751.            
  752.             if (updates.length === 0) {
  753.                 showMessage('No changes were detected in the table. Nothing to save.', 'info');
  754.                 return;
  755.             }
  756.            
  757.             const formData = new FormData();
  758.             formData.append('action', 'update_records');
  759.             formData.append('table', selectedTable);
  760.             formData.append('primary_key', primaryKey);
  761.             formData.append('data', JSON.stringify(updates));
  762.            
  763.             const updateBtn = document.getElementById('updateChangesBtn');
  764.             updateBtn.disabled = true;
  765.             updateBtn.textContent = 'Updating...';
  766.            
  767.             fetch('', {
  768.                 method: 'POST',
  769.                 body: formData
  770.             })
  771.             .then(response => response.json())
  772.             .then(data => {
  773.                 if (data.success) {
  774.                     showMessage(data.message, 'success');
  775.                     const cells = resultContainer.querySelectorAll('td[contenteditable="true"]');
  776.                     cells.forEach(cell => {
  777.                         cell.dataset.originalValue = cell.textContent.trim();
  778.                     });
  779.                    
  780.                 } else {
  781.                     showMessage(data.message, 'danger');
  782.                 }
  783.                 updateBtn.disabled = false;
  784.                 updateBtn.textContent = 'Save All Changes';
  785.             })
  786.             .catch(error => {
  787.                 console.error('Error:', error);
  788.                 showMessage('An unexpected error occurred during the update process. Please check the console for more details.', 'danger');
  789.                 updateBtn.disabled = false;
  790.                 updateBtn.textContent = 'Save All Changes';
  791.             });
  792.         }
  793.        
  794.         // --- Go to Top button functionality ---
  795.         window.onscroll = function() {
  796.             if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
  797.                 goToTopBtn.style.display = "block";
  798.             } else {
  799.                 goToTopBtn.style.display = "none";
  800.             }
  801.         };
  802.  
  803.         goToTopBtn.addEventListener('click', function() {
  804.             document.body.scrollTop = 0; // For Safari
  805.             document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
  806.         });
  807.     });
  808.     </script>
  809. </body>
  810. </html>
  811.  
Advertisement
Add Comment
Please, Sign In to add comment