Update db data without losing collapsed rows - javascript

Sorry for being dumb, I'm quite new to PHP.
I have a table filled with data from a database:
machine.php
<?php require "database.php"; // here the connection stuff
const DOT_ONLINE = "🟢";
const DOT_OFFLINE = "🔴";
?>
<table id="tableMachines" class="table table-hover">
<thead class="thead-dark">
<tr>
<th scope="col">Online</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Address</th>
</tr>
</thead>
<tbody>
<?php
try {
$stmt = $db->prepare("SELECT * FROM machines ORDER BY address;");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_OBJ);
foreach ($stmt->fetchAll() as $row) {
$rowId = "row" . $row->name;
$output = "<tr data-bs-toggle='collapse' data-bs-target='#$rowId'>"
. "<td>" . ($row->online ? DOT_ONLINE : DOT_OFFLINE) . "</td>"
. "<td>" . $row->name . "</td>"
. "<td>" . $row->type . "</td>"
. "<td>" . $row->address . "</td>"
."</tr>";
echo $output;
$output = "<tr id='" . $rowId . "' class='collapse fade'>"
."<td colspan='4'>"
."<table class='table table-borderless'>"
."<tr><td>Order: " . $row->job . "</td></tr>"
."<tr><td>Length: " . $row->length . "</td></tr>"
."</table>"
."</td>"
."</tr>";
echo $output;
}
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
}
?>
</tbody>
</table>
<?php
$db = null;
As you can see, the user can click on each row to show some further details.
In the main page I put the above file inside a div:
<div id="tableMachines">
<?php require 'machines.php' ?>
</div>
Periodically I fetch new data:
$(document).ready(function() {
setInterval(function () {
$('#tableMachines').load('machines.php');
}, 5000);
});
It works fine, the problem is when the div reloads I lose the rows that were shown by the user.
It's true I can keep track of which rows were expanded and manually trigger them again after reloading the PHP code, but I don't think this is the correct approach.
Would you mind to help me to understand how to update the data without interfere with the UI?

If this were me, I would track which last machine ID is pulled through and then send that on your load() call. You can then call $('#tableMachines').prepend(data) rather than reloading the whole table - you only reload the latest rows. That way your users choices will be unaffected however you may need to re-bind click/triggers so the new rows expand/hide as well.

Before loading machine.php, you can create a cookie named "expanded_set" in your jquery code that loads machine.php. Each time visitor toggles, you can populate the cookie. In machine.php file, your cookie will be accessible by $_COOKIE['user_expandeds']. Then you can customize machine.php to handle the already-toggled ones to show them also.
I'm not sure of this method for timing issues however, you may also send the "expanded_set" to PHP via a POST type form with a hidden input field with a name which is submitted by jquery automatically also. You can access the posted field with its name as $_POST['expanded_set'].
cookie method may fail if cookies are not allowed by the visitor but using a form method can not be disallowed.

Related

Using AJAX from another page to manipulate a database

I am building a warranty product website as a side project for a small business I work at (We deal with batteries for cars, heavy equipment...). There is a lot going on here.
Basically, when an item gets returned for a warranty inspection the user can input the customer's information and the product information for it to be tracked and managed. I began with making a database in PHP (It is only one table and not fully normalized since it really does not need to be unless there is expansion later on) with only one table that manages transactions.
Each transaction when submitted defaults to ACTIVE status meaning it can be manipulated (Deleted, resolved, or to add a comment) via buttons in the table. To be able to do this I have been using AJAX to be able to use buttons in a table and change the Status to DELETED or RESOLVED.
Now, there are four pages of tables (Like ALL, ACTIVE, DELETED, and RESOLVED) and each ran a very similar code (While loop that displayed the table) with the expectation of the SQL statement. For example the ALL inspections table has nowhere clause in the SQL statement, while the ACTIVE Inspections table would have a where Status = "ACTIVE", as so on for resolved and deleted. Since displaying the data seemed repetitive to have a while loop, I decided to make one table.php page with a function call GetTableData and on each page (ACTIVE, ALL, DELETED...) I would include the PHP file, and then call and pass through the SQL statement, like so...
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<title>Battery Wholesale Inspection Database</title>
</head>
<?php include 'headernNavbar.html'; include 'Actions/connection.php'; include 'Actions/table.php'?>
<body>
<h1 id="title">Active Inspection Database</h1>
</body>
</html>
<?php
$sql = "SELECT TransactionID, CustomerName, PhoneNumber, ReceivedDate, Item, DATE_FORMAT(ManufactureDate, '%m-%y') as Age , InitalVoltage, InitalCCA, Comments, WarrStatus FROM inspection.product WHERE product.WarrStatus = 'ACTIVE' ORDER BY ReceivedDate DESC";
GetTableData($sql, $conn);
?>
Here is what the table.php file looks like...
<html>
<head>
<title></title>
</head>
<?php
include 'Actions/connection.php';
function GetTableData($sql, $conn){
$result = mysqli_query($conn, $sql);
echo "<table id='inspectTable' class='inspectTable'>";
echo "<thead><tr>";
echo "<th>Customer</th>";
echo "<th>Phone</th>";
echo "<th>Received</th>";
echo "<th>Item</th>";
echo "<th>Age</th>";
echo "<th>Voltage</th>";
echo "<th>CCA/AH</th>";
echo "<th>Comments</th>";
echo "<th>Status</th>";
echo "<th colspan=3 >Tools</th>";
echo "</thead></tr>";
if (mysqli_num_rows($result) > 0) {
// output data of each row
echo "<tbody class='dbTable'>";
while($row = mysqli_fetch_assoc($result)) {
$id= $row['TransactionID'];
echo "
<tr><td>" . $row['CustomerName'] ."</td>
<td>" . $row['PhoneNumber'] . "</td>
<td>" . $row['ReceivedDate'] . "</td>
<td>" . $row['Item'] . "</td>
<td>" . $row['Age'] . "</td>
<td>" . $row['InitalVoltage'] . "</td>
<td>" . $row['InitalCCA'] . "</td>
<td>" . $row['Comments'] ."</td>
<td>" . $row['WarrStatus'] ."</td>";
if ($row['WarrStatus'] == 'ACTIVE'){
echo "<td><button class='deleteRow' id='".$id."' >Delete</button></td>
<td><button class='updateRow' id='".$id."' >Update</button></td>
<td><button class='resolveRow' id='".$id."' >Resolve</button></td>";
}
else{
echo "<td><button class='deleteRow' id='".$id."' disabled >Delete</button></td>
<td><button class='updateRow' id='".$id."'disabled >Update</button></td>
<td><button class='resolveRow' id='".$id."'disabled>Resolve</button></td>";
}
}
echo "</tbody>";
} else {
echo "Not Data Avaibable.";
}
}
?>
<script>
$(".deleteRow").click(function()
{
var del_id = $(this).attr('id');
$.ajax({
type:'POST',
url:'Action/deleteAction.php',
data:'id='+del_id,
success: function(data)
{
location.reload();
}
});
});
$(".resolveRow").click(function()
{
var del_id = $(this).attr('id');2
$.ajax({
type:'POST',
url:'resolveAction.php',
data:'id='+del_id,
success: function(data)
{
location.reload();
}
});
});
</script>
</html>
So, as an example when the user is on the ACTIVE inspections page, and the list gets called it is displayed. When the user wants to delete an entery and clicked delete, the deleteAction.php should be called and changes the status from ACTIVE to DELETE. Here is the php for that...
<html>
<head>
<title>delete action</title>
</head>
<?php include 'connection.php'?>
<?php
$id = $_POST['id'];
$sql = "UPDATE inspection.product
SET DeletedDate = now(), WarrStatus = 'DELETED'
WHERE TransactionID = $id";
mysqli_query($conn,$sql);
?>
</html>
Now here is the problem I am facing. When a button (For example the delete button) is clicked nothing changes. Unlike when I first had the while loop (in the table.php) on each page with the AJAX js stuff it worked like a charm. I am just not sure if I am missing something. Here are the things I have tried:
Had each while loop and SQL statement in each PHP page with the AJAX
Created a separate js file and referenced that in the table.php, as well as each PHP page
Only had the AJAX js stuff in the other pages but still called the GetTableData
I have also not accomplished the updating comment button as of yet
Could anyone let a hand here? I would greatly appreciate it! If you see any other things I should do to improve I would also appreciate that. This is my first web project out of the first year!
EDIT:
I also forgot to include that reference to ajax in the table.php, it is there on my system, but I was playing around and forgot to add it in the post! This didn't fix the issue.
EDIT:
So after playing around with it, it does work when putting the ajax js in each php file, however it does not work when I have a separate js file and including that into each php file. Is there a different way to do this for it is a little cleaner?

How do I create a link in <tr> using the same data requested from an SQL database?

I am trying to create a table which pulls data from an SQL database and each row on the table is a clickable link to it's corresponding page depending on the first column data in the database.
I have managed to do both parts separately i.e create a table with SQL data and i've managed making a table with each row a clickable link however I am struggling to combine the two.
I am using a .js to listen for clicks and send you off depending on the "tr data-href='url'".
My php script to create the table form SQL data creates the "tr" first, then populates the row with each cell, closes the "/tr" and repeats for all rows.
How can I make the php write the "tr data-href='url'" where 'url' is the same as the first column of data for each row?
My PHP script is below, followed by the five lines of JS.
<?php
$host = "localhost";
$user = "user";
$pass = "pass";
$db_name = "FFD";
//create connection
$connection = mysqli_connect($host, $user, $pass, $db_name);
//test if connection failed
if(mysqli_connect_errno()){
die("connection failed: "
. mysqli_connect_error()
. " (" . mysqli_connect_errno()
. ")");
}
//get results from database
$result = mysqli_query($connection,"SELECT id,name,location,plots,blurb FROM Sites");
$all_property = array(); //declare an array for saving property
//showing property
echo '
<div class="content-container">
<h1>Table heading</h1>
<section>
<div class="tbl-header">
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>'; //initialize table tag
while ($property = mysqli_fetch_field($result)) {
echo '<th>' . $property->name . '</th>'; //get field name for header
array_push($all_property, $property->name); //save those to array
}
echo '</tr>
</thead>
</table>
</div>
<div class="tbl-content">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>';
//end tr tag
//showing all data
while ($row = mysqli_fetch_array($result)) {
echo '<tr>';
foreach ($all_property as $item) {
echo '<td>' . $row[$item] . '</td>'; //get items using property value
}
echo '</tr>';
}
echo '</tbody></table></div></section></div>';
?>
$(document).ready(function(){
$('tr[data-href]').on("click", function() {
document.location = $(this).data('href');
});
});
I could just be thick and missing something simple and obvious.
Thanks in advance!
You can concatenate your url, like this (assuming id is the column with the url)
while ($row = mysqli_fetch_array($result)) {
echo '<tr data-href="' . $row['id'] . '">';
foreach ($all_property as $item) {
echo '<td>' . $row[$item] . '</td>';
}
echo '</tr>';
}

Updating MySQL Database with checkboxes and PHP

I am trying to update a MySQL database with checkboxes and PHP. I have read a lot of code examples online and read many questions on here but I seem to be stuck at the last hurdle.
My code first queries MySQL to bring back a list of users and then a checkbox (which is either 0 or 1 in MySQL) next to each one, indicating whether or not the user is completed.
What I am wanting to do is when the checkbox is checked, for that to update the MySQL database and update the column with 1, or if it is unchecked, for it to change the column to 2.
Here is my code so far:
HTML Snippet (Checkboxes):
while($row = mysqli_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['Firstname'] . "</td>";
echo "<td>" . $row['Surname'] . "</td>";
echo "<td>" . $row['Department'] . "</td>";
echo "<td>" . $row['RequestedBy'] . "</td>";
echo "<td>" . $row['StartDate'] . "</td>";
echo "<td class='tbl-chk'> <input type='checkbox' name='WSCompleted' value='"; echo $row['ID'] . "'" ; if ($row['WSCompleted']=='1') { echo "checked='checked'";} echo "/></td>";
Here is my jQuery that successfully retrieves the values and IDs and then posts them:
$(document).ready(function(){
$('input[name=WSCompleted]').click(function(){
var wsCompleted = $(this).is(':checked') ? 1 : 0;
var wsCompletedID = $(this).attr('value');
$.ajax({
type: "POST",
url: "/usr-handler.php",
data: {id: wsCompletedID, wsCompleted: wsCompleted},
success: function(){
$('div.success').fadeIn();
}
});
return true;
});
});
And finally here is a snippet of my PHP:
$wsCompleted = $_POST['wsCompleted'];
$id = $_POST['wsCompletedID'];
$query = "UPDATE newusers SET WSCompleted = '$wsCompleted' WHERE id = '$id'";
mysqli_query($con, $query) or die(mysqli_error());
mysqli_close($con);
I am setting the value of the checkbox to what is actually the row ID in MySQL, then I can use the checkbox value to select the correct row in MySQL, and update it.
The problem I am having is that currently, how the code is, I get the following response from FireBug:
Unidentified index: WSCompletedID
After looking online it was suggested to change:
$id = $_POST['wsCompletedID'];
To:
$id = (ISSET($_POST['wsCompletedID']));
But doing so clears the error message, but then doesn't actually update the value on MySQL.
If I manually set the $id to something, the update works, but obviously that isn't right as it would only ever update the ID I have chosen it to.
I am completely stumped as to what is causing the problem. I have tried finding out online but cannot get anywhere with it.
Any help is greatly appreciated.
Thank you
You are loading data here in your javascript AJAX code
data: {id: wsCompletedID, wsCompleted: wsCompleted},
This will create the following $_POST array
id => whatever was in wsCompletedID
wsCompleted => whatever was in wsCompleted
So your PHP code should be looking for $_POST['id'] and $_POST['wsCompleted'] as these are the names you have given in your data:...
$wsCompleted = $_POST['wsCompleted'];
$id = $_POST['id'];
$query = "UPDATE newusers SET WSCompleted = '$wsCompleted' WHERE id = '$id'";
mysqli_query($con, $query) or die(mysqli_error());
mysqli_close($con);
HOWEVER: Your script is at risk of SQL Injection Attack
Have a look at what happened to Little Bobby Tables Even
if you are escaping inputs, its not safe!
Use prepared statement and parameterized statements

Allow editing of Text dynamic php table

I have a html table that is generated VIA PHP and data in a database, what I want to do is have a button in the last cell of each row that says edit and when you click that button the text in the other cells becomes textboxes or other types of input fields so that you can edit them and then press submit which would send that form off to be updated in the database. The code I have right now to generate the table is:
<table style="width:100%; " class = "table table-striped table-bordered table-hover">
<tr>
<th>Name</th>
<th>Status</th>
<th>Description</th>
<?php
if($_SESSION['editGroup'] != 0){
echo "<th>Edit</th>";
}
?>
</tr>
<?php
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$checkQuery = "SELECT userGiven, userStatus, userDesc FROM user_Status WHERE organization = 'myOrg' ORDER BY userGiven ASC";
$prepared = $dbh->prepare($checkQuery);
$prepared->execute();
$data = $prepared->fetchAll(PDO::FETCH_ASSOC);
foreach($data as $row){
echo "<tr>";
if($_SESSION['editGroup'] != 0){
echo "<td width='20%'>" . $row['userGiven'] . "</td><td width='10%'>" . $row['userStatus'] . "</td><td width='70%'>" . $row['userDesc'] . "</td><td width='10%'><button type='button' class='btn btn-info'>Edit</button></td>";
}else{
echo "<td width='20%'>" . $row['userGiven'] . "</td><td width='15%'>" . $row['userStatus'] . "</td><td width='75%'>" . $row['userDesc'] . "</td>";
}
echo "</tr>";
}
?>
</table>
What I am trying to do is change the cell with userStatus to a drop down field with the current value as the starting value and the other value in/out as the other value to select between.
I also want to change the userDesc to a textarea and I think I know how to do all this but I am running into a problem conceptually when I try to apply it to the dynamic table.
What I was thinking was that I could use jquery/javascript to get the innerhtml of span variable that could surround those two cells and then change the html to the various input fields containing the current text allowing the user editing them to change those values.
How do I do this for this sort of problem though, I would need onClick events for all the buttons and I wouldn't know how many buttons there would be, that's based off of the number of rows in the table.
That would result in hundreds of lines of redundant code so I assume there has to be a much better way. Anyone know what a way to accomplish this? I found this: http://stackoverflow.com/questions/16202723/how-to-edit-data-onclick which is close to what I want but that seems to be static values where I want to be able to do this for any of the rows in the table.
In your for loop, you'll want to put something identifiable in the <tr> and <td> elements. I'd personally go with a data-attribute. For example:
Echo Row Code
foreach($data as $row){
echo "<tr data-row='{$row[id]}'>";
if($_SESSION['editGroup'] != 0){
echo "<td width='20%' data-column='name'>" . $row['userGiven'] . "</td><td width='10%' data-column='status'>" . $row['userStatus'] . "</td><td width='70%' data-column='description'>" . $row['userDesc'] . "</td><td width='10%'><button type='button' class='btn btn-info'>Edit</button></td>";
}else{
echo "<td width='20%'>" . $row['userGiven'] . "</td><td width='15%'>" . $row['userStatus'] . "</td><td width='75%'>" . $row['userDesc'] . "</td>";
}
echo "</tr>";
}
So, as you can see I've added a data-row attribute to <tr> which should get the value of the database record's ID. Change it as necessary - I made the assumption it'd be named 'id'. Also, I added the data-column attribute to <td> which should identify each column for us. This is all the modification needed in the PHP.
Now, here's what the JQuery for the edit button looks like:
Front-End Event Listener: Part 1
$( function(){
$(document).on("click", ".btn-info", function(){
var parent = $(this).closest("tr");
var id = $(parent).attr("data-row");
var name = $(parent).children("[data-column='name']");
var status = $(parent).children("[data-column='status']");
var desc = $(parent).children("[data-column='description']");
var nameTxt = $(name).html();
var statusTxt = $(status).html();
var descTxt = $(desc).html();
$(name).html("<input name='name' data-dc='name' value='" + nameTxt + "'>");
$(status).html("<input name='status' data-dc='status' value='" + statusTxt + "'>");
$(desc).html("<textarea name='desc' data-dc='description'>" + descTxt + "</textarea>");
$(this).replaceWith("<button class='btn-info-save'>Save</button>");
});
}
Finally, we need to define what happens upon hitting save (the above example changes the "edit" button into a "save" button). That could be anything, but we'll assume it'll be an AJAX call:
Front-End Event Listener: Part 2
$( function(){
$(document).on("click", ".btn-info-save", function(){
var parent = $(this).closest("tr");
var id = $(parent).attr("data-row");
var data = {id: id};
$("[data-dc]").each( function(){
var col = $(this).attr("data-dc");
var val = $(this).val();
data[col] = val;
});
$.ajax({
url: "/dynamic-edit/edit.php", // Change this to your PHP update script!
type: 'POST',
dataType: 'json',
data: data,
success: function(ret){
//Do Something
console.log(ret.response);
},
error: function(ret){
console.log(ret.response);
}
});
});
}
Now, in your PHP script that handles the AJAX request:
PHP Code for 'edit.php'
$name = $_POST['data_name'];
$status = $_POST['data_status'];
$description = $_POST['data_description'];
// Do whatever with the data
// Output JSON - get the response in JS with ret.response
// either inside the success or error portion of the AJAX
echo json_encode( ["response"=>"Row edited successfully."] );
This is a very simple example, but it gets the point across. Be sure to change the AJAX url from "/dynamic-edit/edit.php" to wherever you'll make your PHP script that will actually make the updates after submitting.
You'll likely want to do cleanup after a successful edit; for example, changing the text boxes back to just text in a <td>. Also, please note that I just changed them to textboxes. I know you said in your post you wanted to make one the status a dropdown and the description a textarea, but this example should be easy enough to change. I don't know what the values of the dropdown should be, so you'll have to do that part.
Notes
I went with $(document).on("click" ... instead of $(".btn-info").on("click" ... because whenever you're dealing with dynamic content, you always want the event listener on the document, not the element. Why? Because if you click the "edit" button, it disappears and a "save" button appears, you now lose that event listener forever. If you were to re-add the "edit" button (say, after a successful save), that button would need the event listener added again. When you go the route of attaching the event listener to the document, however, you can remove/add all you want and it'll still work.
You can add 'data' attribute to each button with the user id that you want to update. For example:
<button data-iduser='<?= $use["id"] ?>' class='btn btn-info'>Edit</button>
$("btn btn-info").click( function() {
var idUser = $(this).attr("data-iduser");
// some ajax if you want with that iD
});

Hide table row that contains specified string

$row['attended'] can be either 'YES' or 'NO' (strings). I want to hide every row that has 'Attended' as 'NO'. So in pseudo code:
if (<tr> contains 'NO') {
hide that row (<-That's the tricky part to me)
}
My code is on the server side, but I'd be happy to echo out some JavaScript if that's what's necessary.
<?php
include 'connect2.php';
date_default_timezone_set('Europe/Lisbon');
$today = date('Y-m-d');
$sql="Select * FROM detentions WHERE date = '$today'";
$result = mysqli_query($con,$sql);
echo "<center><table style='width:400px; border:3px solid #444'>
<tr>
<th style='background-color:#444'>First</th>
<th style='background-color:#444'>Last</th>
<th style='background-color:#444'>Attended</th>
<th style='background-color:#444'></th>
</tr>";
while($row=mysqli_fetch_array($result)){
echo "<tr>";
echo "<td>" . $row['fname'] . "</td>";
echo "<td>" . $row['lname'] . "</td>";
echo "<td><center>" . $row['attended'] . "</center></td>";
echo "<td><button type = 'button' class='unattended button-error pure-button button-xsmall' style='float:right; margin-left:0.2cm' name='" . $row['lname'] . "' id='" . $row['fname'] . "'><b>Unattended</b></button> <button type = 'button' class='editDet2 button-success pure-button button-xsmall' style='float:right' id=" . $row['det_id'] . "><b>Attended</b></button></td>";
echo "</tr>";
}
echo "</table>";
mysqli_close($con);
?>
Another option is to skip the record with $row['attended']="No" in your while statement... An "if ($result.attended=='yes') {add ...} stamement" will do... Though the more efficient and elegant way would be to add a clause on the query statement like:
Select * FROM detentions WHERE date = '$today' and attended='yes'"
As suggested on the previous answer...
replace your select statement
$sql="Select * FROM detentions WHERE date = '$today' AND attended = 'YES' OR attended = ''";
and remove your if condition.
You can add a WHERE clause unto your SELECT statement, which specifies that the Attended field should be equal to no. This would prevent the unwanted rows from being returned in the first place.
Change your SELECT statement to:
SELECT * FROM detentions WHERE date = '$today' AND attended='yes'

Categories

Resources