mirror of
https://github.com/itflow-org/itflow
synced 2026-03-01 11:24:52 +00:00
Added SortableJS Library, and updated Invoice, Quote and Recurring to use it. Added Grab Bar Icons next to action buttons. Will now sort in Mobile much more efficiently, update ajax vars for recurring invoice
This commit is contained in:
4
ajax.php
4
ajax.php
@@ -586,13 +586,13 @@ if (isset($_POST['update_recurring_invoice_items_order'])) {
|
|||||||
enforceUserPermission('module_sales', 2);
|
enforceUserPermission('module_sales', 2);
|
||||||
|
|
||||||
$positions = $_POST['positions'];
|
$positions = $_POST['positions'];
|
||||||
$recurring_id = intval($_POST['recurring_id']);
|
$recurring_invoice_id = intval($_POST['recurring_invoice_id']);
|
||||||
|
|
||||||
foreach ($positions as $position) {
|
foreach ($positions as $position) {
|
||||||
$id = intval($position['id']);
|
$id = intval($position['id']);
|
||||||
$order = intval($position['order']);
|
$order = intval($position['order']);
|
||||||
|
|
||||||
mysqli_query($mysqli, "UPDATE invoice_items SET item_order = $order WHERE item_recurring_id = $recurring_id AND item_id = $id");
|
mysqli_query($mysqli, "UPDATE invoice_items SET item_order = $order WHERE item_recurring_invoice_id = $recurring_invoice_id AND item_id = $id");
|
||||||
}
|
}
|
||||||
|
|
||||||
// return a response
|
// return a response
|
||||||
|
|||||||
@@ -20,10 +20,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.grab-cursor {
|
button.drag-handle {
|
||||||
cursor: grab;
|
cursor: grab !important;
|
||||||
}
|
touch-action: none;
|
||||||
|
user-select: none;
|
||||||
.grab-cursor:active {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
}
|
||||||
|
button.drag-handle:active {
|
||||||
|
cursor: grabbing !important;
|
||||||
|
}
|
||||||
86
invoice.php
86
invoice.php
@@ -165,6 +165,7 @@ if (isset($_GET['invoice_id'])) {
|
|||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
|
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
|
||||||
|
|
||||||
<ol class="breadcrumb d-print-none">
|
<ol class="breadcrumb d-print-none">
|
||||||
@@ -381,26 +382,34 @@ if (isset($_GET['invoice_id'])) {
|
|||||||
<tr data-item-id="<?php echo $item_id; ?>">
|
<tr data-item-id="<?php echo $item_id; ?>">
|
||||||
<td class="d-print-none">
|
<td class="d-print-none">
|
||||||
<?php if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled") { ?>
|
<?php if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled") { ?>
|
||||||
<div class="dropdown">
|
<div class="row">
|
||||||
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
<div class="col">
|
||||||
<i class="fas fa-ellipsis-v"></i>
|
<button type="button" class="btn btn-sm btn-light drag-handle">
|
||||||
</button>
|
<i class="fas fa-bars text-muted"></i>
|
||||||
<div class="dropdown-menu">
|
</button>
|
||||||
<a class="dropdown-item" href="#"
|
</div>
|
||||||
data-toggle="ajax-modal"
|
<div class="col">
|
||||||
data-ajax-url="ajax/ajax_item_edit.php"
|
<div class="dropdown">
|
||||||
data-ajax-id="<?php echo $item_id; ?>"
|
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
||||||
>
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
</button>
|
||||||
</a>
|
<div class="dropdown-menu">
|
||||||
<div class="dropdown-divider"></div>
|
<a class="dropdown-item" href="#"
|
||||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
|
data-toggle="ajax-modal"
|
||||||
|
data-ajax-url="ajax/ajax_item_edit.php"
|
||||||
|
data-ajax-id="<?php echo $item_id; ?>"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="grab-cursor"><?php echo $item_name; ?></td>
|
<td><?php echo $item_name; ?></td>
|
||||||
<td><?php echo nl2br($item_description); ?></td>
|
<td><?php echo nl2br($item_description); ?></td>
|
||||||
<td class="text-center"><?php echo number_format($item_quantity, 2); ?></td>
|
<td class="text-center"><?php echo number_format($item_quantity, 2); ?></td>
|
||||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $invoice_currency_code); ?></td>
|
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $invoice_currency_code); ?></td>
|
||||||
@@ -1178,38 +1187,23 @@ require_once "includes/footer.php";
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="plugins/dragula/dragula.min.js"></script>
|
<script src="plugins/SortableJS/Sortable.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
new Sortable(document.querySelector('table#items tbody'), {
|
||||||
var container = $('table#items tbody')[0];
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
onEnd: function (evt) {
|
||||||
|
const rows = document.querySelectorAll('table#items tbody tr');
|
||||||
|
const positions = Array.from(rows).map((row, index) => ({
|
||||||
|
id: row.dataset.itemId,
|
||||||
|
order: index
|
||||||
|
}));
|
||||||
|
|
||||||
dragula([container])
|
$.post('ajax.php', {
|
||||||
.on('drop', function (el, target, source, sibling) {
|
update_invoice_items_order: true,
|
||||||
// Handle the drop event to update the order in the database
|
invoice_id: <?php echo $invoice_id; ?>,
|
||||||
var rows = $(container).children();
|
positions: positions
|
||||||
var positions = rows.map(function(index, row) {
|
|
||||||
return {
|
|
||||||
id: $(row).data('itemId'),
|
|
||||||
order: index
|
|
||||||
};
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
// Send the new order to the server
|
|
||||||
$.ajax({
|
|
||||||
url: 'ajax.php',
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
update_invoice_items_order: true,
|
|
||||||
invoice_id: <?php echo $invoice_id; ?>,
|
|
||||||
positions: positions
|
|
||||||
},
|
|
||||||
success: function(data) {
|
|
||||||
// Handle success
|
|
||||||
},
|
|
||||||
error: function(error) {
|
|
||||||
console.error('Error updating order:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
2
plugins/SortableJS/Sortable.min.js
vendored
Normal file
2
plugins/SortableJS/Sortable.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
89
quote.php
89
quote.php
@@ -326,27 +326,37 @@ if (isset($_GET['quote_id'])) {
|
|||||||
<tr data-item-id="<?php echo $item_id; ?>">
|
<tr data-item-id="<?php echo $item_id; ?>">
|
||||||
<td class="d-print-none">
|
<td class="d-print-none">
|
||||||
<?php if ($quote_status !== "Invoiced" && $quote_status !== "Accepted" && $quote_status !== "Declined" && lookupUserPermission("module_sales") >= 2) { ?>
|
<?php if ($quote_status !== "Invoiced" && $quote_status !== "Accepted" && $quote_status !== "Declined" && lookupUserPermission("module_sales") >= 2) { ?>
|
||||||
<div class="dropdown">
|
<div class="row">
|
||||||
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
<div class="col">
|
||||||
<i class="fas fa-ellipsis-v"></i>
|
<button type="button" class="btn btn-sm btn-light drag-handle">
|
||||||
</button>
|
<i class="fas fa-bars text-muted"></i>
|
||||||
<div class="dropdown-menu">
|
</button>
|
||||||
<a class="dropdown-item" href="#"
|
</div>
|
||||||
data-toggle="ajax-modal"
|
<div class="col">
|
||||||
data-ajax-url="ajax/ajax_item_edit.php"
|
|
||||||
data-ajax-id="<?php echo $item_id; ?>"
|
<div class="dropdown">
|
||||||
>
|
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
||||||
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
</a>
|
</button>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-menu">
|
||||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_quote_item=<?php echo $item_id; ?>">
|
<a class="dropdown-item" href="#"
|
||||||
<i class="fa fa-fw fa-trash mr-2"></i>Delete
|
data-toggle="ajax-modal"
|
||||||
</a>
|
data-ajax-url="ajax/ajax_item_edit.php"
|
||||||
|
data-ajax-id="<?php echo $item_id; ?>"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_quote_item=<?php echo $item_id; ?>">
|
||||||
|
<i class="fa fa-fw fa-trash mr-2"></i>Delete
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="grab-cursor"><?php echo $item_name; ?></td>
|
<td><?php echo $item_name; ?></td>
|
||||||
<td><?php echo nl2br($item_description); ?></td>
|
<td><?php echo nl2br($item_description); ?></td>
|
||||||
<td class="text-center"><?php echo number_format($item_quantity, 2); ?></td>
|
<td class="text-center"><?php echo number_format($item_quantity, 2); ?></td>
|
||||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $quote_currency_code); ?></td>
|
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $quote_currency_code); ?></td>
|
||||||
@@ -992,38 +1002,23 @@ require_once "includes/footer.php";
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="plugins/dragula/dragula.min.js"></script>
|
<script src="plugins/SortableJS/Sortable.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
new Sortable(document.querySelector('table#items tbody'), {
|
||||||
var container = $('table#items tbody')[0];
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
onEnd: function (evt) {
|
||||||
|
const rows = document.querySelectorAll('table#items tbody tr');
|
||||||
|
const positions = Array.from(rows).map((row, index) => ({
|
||||||
|
id: row.dataset.itemId,
|
||||||
|
order: index
|
||||||
|
}));
|
||||||
|
|
||||||
dragula([container])
|
$.post('ajax.php', {
|
||||||
.on('drop', function (el, target, source, sibling) {
|
update_quote_items_order: true,
|
||||||
// Handle the drop event to update the order in the database
|
quote_id: <?php echo $quote_id; ?>,
|
||||||
var rows = $(container).children();
|
positions: positions
|
||||||
var positions = rows.map(function(index, row) {
|
|
||||||
return {
|
|
||||||
id: $(row).data('itemId'),
|
|
||||||
order: index
|
|
||||||
};
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
// Send the new order to the server
|
|
||||||
$.ajax({
|
|
||||||
url: 'ajax.php',
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
update_quote_items_order: true,
|
|
||||||
quote_id: <?php echo $quote_id; ?>,
|
|
||||||
positions: positions
|
|
||||||
},
|
|
||||||
success: function(data) {
|
|
||||||
// Handle success
|
|
||||||
},
|
|
||||||
error: function(error) {
|
|
||||||
console.error('Error updating order:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -272,26 +272,34 @@ if (isset($_GET['recurring_invoice_id'])) {
|
|||||||
|
|
||||||
<tr data-item-id="<?php echo $item_id; ?>">
|
<tr data-item-id="<?php echo $item_id; ?>">
|
||||||
<td class="d-print-none">
|
<td class="d-print-none">
|
||||||
<div class="dropdown">
|
<div class="row">
|
||||||
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
<div class="col">
|
||||||
<i class="fas fa-ellipsis-v"></i>
|
<button type="button" class="btn btn-sm btn-light drag-handle">
|
||||||
</button>
|
<i class="fas fa-bars text-muted"></i>
|
||||||
<div class="dropdown-menu">
|
</button>
|
||||||
<a class="dropdown-item" href="#"
|
</div>
|
||||||
data-toggle="ajax-modal"
|
<div class="col">
|
||||||
data-ajax-url="ajax/ajax_item_edit.php"
|
|
||||||
data-ajax-id="<?php echo $item_id; ?>"
|
|
||||||
>
|
|
||||||
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_recurring_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-sm btn-light" type="button" data-toggle="dropdown">
|
||||||
|
<i class="fas fa-ellipsis-v"></i>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="#"
|
||||||
|
data-toggle="ajax-modal"
|
||||||
|
data-ajax-url="ajax/ajax_item_edit.php"
|
||||||
|
data-ajax-id="<?php echo $item_id; ?>"
|
||||||
|
>
|
||||||
|
<i class="fa fa-fw fa-edit mr-2"></i>Edit
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item text-danger confirm-link" href="post.php?delete_recurring_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="grab-cursor"><?php echo $item_name; ?></td>
|
<td><?php echo $item_name; ?></td>
|
||||||
<td><?php echo nl2br($item_description); ?></td>
|
<td><?php echo nl2br($item_description); ?></td>
|
||||||
<td class="text-center"><?php echo $item_quantity; ?></td>
|
<td class="text-center"><?php echo $item_quantity; ?></td>
|
||||||
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $recurring_invoice_currency_code); ?></td>
|
<td class="text-right"><?php echo numfmt_format_currency($currency_format, $item_price, $recurring_invoice_currency_code); ?></td>
|
||||||
@@ -483,39 +491,23 @@ require_once "includes/footer.php";
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
|
<script src="plugins/SortableJS/Sortable.min.js"></script>
|
||||||
<script src="plugins/dragula/dragula.min.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
new Sortable(document.querySelector('table#items tbody'), {
|
||||||
var container = $('table#items tbody')[0];
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
onEnd: function (evt) {
|
||||||
|
const rows = document.querySelectorAll('table#items tbody tr');
|
||||||
|
const positions = Array.from(rows).map((row, index) => ({
|
||||||
|
id: row.dataset.itemId,
|
||||||
|
order: index
|
||||||
|
}));
|
||||||
|
|
||||||
dragula([container])
|
$.post('ajax.php', {
|
||||||
.on('drop', function (el, target, source, sibling) {
|
update_recurring_invoice_items_order: true,
|
||||||
// Handle the drop event to update the order in the database
|
recurring_invoice_id: <?php echo $recurring_invoice_id; ?>,
|
||||||
var rows = $(container).children();
|
positions: positions
|
||||||
var positions = rows.map(function(index, row) {
|
|
||||||
return {
|
|
||||||
id: $(row).data('itemId'),
|
|
||||||
order: index
|
|
||||||
};
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
// Send the new order to the server
|
|
||||||
$.ajax({
|
|
||||||
url: 'ajax.php',
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
update_recurring_invoice_items_order: true,
|
|
||||||
recurring_invoice_id: <?php echo $recurring_invoice_id; ?>,
|
|
||||||
positions: positions
|
|
||||||
},
|
|
||||||
success: function(data) {
|
|
||||||
// Handle success
|
|
||||||
},
|
|
||||||
error: function(error) {
|
|
||||||
console.error('Error updating order:', error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user