Merge pull request #1213 from itflow-org/develop

Develop to Master - 25.03.5 Release
This commit is contained in:
Johnny 2025-04-18 19:08:18 -04:00 committed by GitHub
commit b69a70cfc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 514 additions and 509 deletions

View File

@ -2,6 +2,19 @@
This file documents all notable changes made to ITFlow.
## [25.03.5]
### Fixed
- Fixed the user listing issue when copying a trip.
- Corrected the display of recurring invoice amounts on the dashboard.
- Fixed the linking of entities with assets and contacts.
- Resolved the issue with displaying the correct mobile country code in the contact listing.
- Set the default date to `9999-12-31` to ensure future items (like invoices) are displayed by default.
- Fixed the display issue where file folders were not showing properly during document creation.
- Migrated from Dragula to SortableJS for a more modern, mobile-friendly solution.
- Added Handlebars icons for drag-and-drop items.
- Changed behavior to open Contact and Asset Details pages directly instead of using a modal.
## [25.03.4]
### Fixed

View File

@ -93,6 +93,7 @@ If you want to improve ITFlow, feel free to fork the repo and create a pull requ
Were incredibly grateful to the organizations and individuals who support the project - a big thank you to:
- CompuMatter
- F1 for HELP
- JetBrains (PhpStorm)
## License
ITFlow is distributed "as is" under the GPL License, WITHOUT WARRANTY OF ANY KIND. See [`LICENSE`](https://github.com/itflow-org/itflow/blob/master/LICENSE) for details.

View File

@ -72,6 +72,31 @@ require_once "includes/inc_all_admin.php";
<input type="email" class="form-control" name="config_ticket_new_ticket_notification_email" placeholder="Address to notify for new tickets, leave blank for none" value="<?php echo nullable_htmlentities($config_ticket_new_ticket_notification_email); ?>">
</div>
</div>
<div class="form-group">
<label>Tickets Default View</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-eye"></i></span>
</div>
<select class="form-control" name="config_ticket_default_view">
<option value=0 <?php if ($config_ticket_default_view == 0) { echo "selected"; } ?>>List</option>
<option value=1 <?php if ($config_ticket_default_view == 1) { echo "selected"; } ?>>Compact</option>
<option value=2 <?php if ($config_ticket_default_view == 2) { echo "selected"; } ?>>Kanban</option>
</select>
</div>
</div>
<div class="form-group">
<label>Kanban Settings</label>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_ordering" <?php if ($config_ticket_ordering == 1) { echo "checked"; } ?> value="1" id="ticketOrderingSwitch">
<label class="custom-control-label" for="ticketOrderingSwitch">Allow ticket ordering within its column<small class="text-secondary">(uncheked will result in ordering it by priority and id)</small></label>
</div>
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" name="config_ticket_moving_columns" <?php if ($config_ticket_moving_columns == 1) { echo "checked"; } ?> value="1" id="ticketMovingColumnsSwitch">
<label class="custom-control-label" for="ticketMovingColumnsSwitch">Allow moving columns</label>
</div>
</div>
<hr>

View File

@ -30,156 +30,139 @@ $ticket_template_updated_at = nullable_htmlentities($row['ticket_template_update
$sql_task_templates = mysqli_query($mysqli, "SELECT * FROM task_templates WHERE task_template_ticket_template_id = $ticket_template_id ORDER BY task_template_order ASC, task_template_id ASC");
?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<ol class="breadcrumb d-print-none">
<li class="breadcrumb-item">
<a href="clients.php">Home</a>
</li>
<li class="breadcrumb-item">
<a href="admin_user.php">Admin</a>
</li>
<li class="breadcrumb-item">
<a href="admin_ticket_template.php">Ticket Templates</a>
</li>
<li class="breadcrumb-item active"><i class="fas fa-life-ring mr-2"></i><?php echo $ticket_template_name; ?></li>
</ol>
<ol class="breadcrumb d-print-none">
<li class="breadcrumb-item">
<a href="clients.php">Home</a>
</li>
<li class="breadcrumb-item">
<a href="admin_user.php">Admin</a>
</li>
<li class="breadcrumb-item">
<a href="admin_ticket_template.php">Ticket Templates</a>
</li>
<li class="breadcrumb-item active"><i class="fas fa-life-ring mr-2"></i><?php echo $ticket_template_name; ?></li>
</ol>
<div class="row">
<div class="col-8">
<div class="row">
<div class="col-8">
<div class="card card-dark">
<div class="card-header">
<h3 class="card-title mt-2">
<div class="media">
<i class="fa fa-fw fa-2x fa-life-ring mr-3"></i>
<div class="media-body">
<h3 class="mb-0"><?php echo $ticket_template_name; ?></h3>
<div><small class="text-secondary"><?php echo $ticket_template_description; ?></small></div>
</div>
<div class="card card-dark">
<div class="card-header">
<h3 class="card-title mt-2">
<div class="media">
<i class="fa fa-fw fa-2x fa-life-ring mr-3"></i>
<div class="media-body">
<h3 class="mb-0"><?php echo $ticket_template_name; ?></h3>
<div><small class="text-secondary"><?php echo $ticket_template_description; ?></small></div>
</div>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#editTicketTemplateModal">
<i class="fas fa-edit"></i>
</button>
</div>
</div>
<h5><?php echo $ticket_template_subject; ?></h5>
<div class="card-body prettyContent">
<?php echo $ticket_template_details; ?>
</h3>
<div class="card-tools">
<button type="button" class="btn btn-default btn-sm" data-toggle="modal" data-target="#editTicketTemplateModal">
<i class="fas fa-edit"></i>
</button>
</div>
</div>
</div>
<div class="col-4">
<div class="card card-dark">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-fw fa-tasks mr-2"></i>Tasks</h5>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tasks"></i></span>
</div>
<input type="text" class="form-control" name="task_name" placeholder="Create a task" required>
<div class="input-group-append">
<button type="submit" name="add_ticket_template_task" class="btn btn-primary"><i class="fas fa-fw fa-check"></i></button>
</div>
</div>
</div>
</form>
<table class="table table-striped table-sm">
<?php
while($row = mysqli_fetch_array($sql_task_templates)){
$task_id = intval($row['task_template_id']);
$task_name = nullable_htmlentities($row['task_template_name']);
$task_completion_estimate = intval($row['task_template_completion_estimate']);
$task_description = nullable_htmlentities($row['task_template_description']);
?>
<tr data-task-id="<?php echo $task_id; ?>">
<td><i class="far fa-fw fa-square text-secondary"></i></td>
<td>
<a href="#" class="grab-cursor">
<span class="text-secondary"><?php echo $task_completion_estimate; ?>m</span>
<span class="text-dark"> - <?php echo $task_name; ?></span>
</a>
</td>
<td class="text-right">
<div class="float-right">
<div class="dropdown dropleft text-center">
<button class="btn btn-link text-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
data-toggle = "ajax-modal"
data-ajax-url = "ajax/ajax_ticket_template_task_edit.php"
data-ajax-id = "<?php echo $task_id; ?>"
>
<i class="fas 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_task_template=<?php echo $task_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash-alt mr-2"></i>Delete
</a>
</div>
</div>
</div>
</td>
</tr>
<?php
}
?>
</table>
</div>
<h5><?php echo $ticket_template_subject; ?></h5>
<div class="card-body prettyContent">
<?php echo $ticket_template_details; ?>
</div>
</div>
</div>
<script src="js/pretty_content.js"></script>
<script src="plugins/dragula/dragula.min.js"></script>
<script>
$(document).ready(function() {
var container = $('.table tbody')[0];
<div class="col-4">
dragula([container])
.on('drop', function (el, target, source, sibling) {
// Handle the drop event to update the order in the database
var rows = $(container).children();
var positions = rows.map(function(index, row) {
return {
id: $(row).data('taskId'),
order: index
};
}).get();
// Send the new order to the server
$.ajax({
url: 'ajax.php',
method: 'POST',
data: {
update_task_templates_order: true, // Adjust the parameter name if needed
ticket_template_id: <?php echo $ticket_template_id; ?>,
positions: positions
},
success: function(data) {
// Handle success
},
error: function(error) {
console.error('Error updating order:', error);
<div class="card card-dark">
<div class="card-header">
<h5 class="card-title"><i class="fa fa-fw fa-tasks mr-2"></i>Tasks</h5>
</div>
<div class="card-body">
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="ticket_template_id" value="<?php echo $ticket_template_id; ?>">
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fa fa-fw fa-tasks"></i></span>
</div>
<input type="text" class="form-control" name="task_name" placeholder="Create a task" required>
<div class="input-group-append">
<button type="submit" name="add_ticket_template_task" class="btn btn-primary"><i class="fas fa-fw fa-check"></i></button>
</div>
</div>
</div>
</form>
<table class="table table-sm" id="tasks">
<?php
while($row = mysqli_fetch_array($sql_task_templates)){
$task_id = intval($row['task_template_id']);
$task_name = nullable_htmlentities($row['task_template_name']);
$task_completion_estimate = intval($row['task_template_completion_estimate']);
$task_description = nullable_htmlentities($row['task_template_description']);
?>
<tr data-task-id="<?php echo $task_id; ?>">
<td>
<a href="#" class="drag-handle"><i class="fas fa-bars text-muted mr-1"></i></a>
<span class="text-secondary"><?php echo $task_completion_estimate; ?>m</span>
<span class="text-dark"> - <?php echo $task_name; ?></span>
</td>
<td class="text-right">
<div class="float-right">
<div class="dropdown dropleft text-center">
<button class="btn btn-link text-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#"
data-toggle = "ajax-modal"
data-ajax-url = "ajax/ajax_ticket_template_task_edit.php"
data-ajax-id = "<?php echo $task_id; ?>"
>
<i class="fas 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_task_template=<?php echo $task_id; ?>&csrf_token=<?php echo $_SESSION['csrf_token'] ?>">
<i class="fas fa-fw fa-trash-alt mr-2"></i>Delete
</a>
</div>
</div>
</div>
</td>
</tr>
<?php
}
});
});
});
</script>
?>
</table>
</div>
</div>
</div>
</div>
<script src="js/pretty_content.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
new Sortable(document.querySelector('table#tasks tbody'), {
handle: '.drag-handle',
animation: 150,
onEnd: function (evt) {
const rows = document.querySelectorAll('table#tasks tbody tr');
const positions = Array.from(rows).map((row, index) => ({
id: row.dataset.taskId,
order: index
}));
$.post('ajax.php', {
update_task_templates_order: true,
ticket_template_id: <?php echo $ticket_template_id; ?>,
positions: positions
});
}
});
</script>
<?php

View File

@ -586,13 +586,13 @@ if (isset($_POST['update_recurring_invoice_items_order'])) {
enforceUserPermission('module_sales', 2);
$positions = $_POST['positions'];
$recurring_id = intval($_POST['recurring_id']);
$recurring_invoice_id = intval($_POST['recurring_invoice_id']);
foreach ($positions as $position) {
$id = intval($position['id']);
$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

View File

@ -115,7 +115,7 @@ ob_start();
$sql_users = mysqli_query($mysqli, "SELECT users.user_id, user_name FROM users
LEFT JOIN user_settings on users.user_id = user_settings.user_id
WHERE user_role > 1 AND user_archived_at IS NULL ORDER BY user_name ASC"
WHERE user_role_id > 1 AND user_archived_at IS NULL ORDER BY user_name ASC"
);
while ($row = mysqli_fetch_array($sql_users)) {
$user_id_select = intval($row['user_id']);

View File

@ -244,7 +244,7 @@ if (isset($_GET['asset_id'])) {
data-ajax-id="<?php echo $asset_id; ?>">
<i class="fas fa-fw fa-edit"></i>
</button>
<h3 class="text-bold"><i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-3"></i><?php echo $asset_name; ?></h3>
<h4 class="text-bold"><i class="fa fa-fw text-secondary fa-<?php echo $device_icon; ?> mr-3"></i><?php echo $asset_name; ?></h4>
<?php if ($asset_photo) { ?>
<img class="img-fluid img-circle p-3" alt="asset_photo" src="<?php echo "uploads/clients/$client_id/$asset_photo"; ?>">
<?php } ?>
@ -405,7 +405,7 @@ if (isset($_GET['asset_id'])) {
<div class="card card-dark">
<div class="card-header py-2">
<h3 class="card-title mt-2"><i class="fa fa-fw fa-ethernet mr-2"></i><?php echo $asset_name; ?> Network Interfaces</h3>
<h3 class="card-title mt-2"><i class="fa fa-fw fa-ethernet mr-2"></i>Interfaces</h3>
<div class="card-tools">
<div class="btn-group">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#addAssetInterfaceModal">

View File

@ -550,11 +550,7 @@ if (mysqli_num_rows($os_sql) > 0) {
</div>
</td>
<td>
<a class="text-dark" href="#"
data-toggle="ajax-modal"
data-modal-size="lg"
data-ajax-url="ajax/ajax_asset_details.php?<?php echo $client_url; ?>"
data-ajax-id="<?php echo $asset_id; ?>">
<a class="text-dark" href="asset_details.php?client_id=<?php echo $client_id; ?>&asset_id=<?php echo $asset_id; ?>">
<div class="media">
<i class="fa fa-fw fa-2x fa-<?php echo $device_icon; ?> mr-3 mt-1"></i>
<div class="media-body">

View File

@ -319,7 +319,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
} else {
$contact_phone_display = "<div><i class='fas fa-fw fa-phone mr-2'></i><a href='tel:$contact_phone'>$contact_phone$contact_extension_display</a></div>";
}
$contact_mobile_country_code = nullable_htmlentities($row['contact_phone_country_code']);
$contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']);
$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code));
if (empty($contact_mobile)) {
$contact_mobile_display = "";
@ -445,11 +445,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
</div>
</td>
<td>
<a class="text-dark" href="#"
data-toggle="ajax-modal"
data-modal-size="lg"
data-ajax-url="ajax/ajax_contact_details.php?<?php echo $client_url; ?>"
data-ajax-id="<?php echo $contact_id; ?>">
<a class="text-dark" href="contact_details.php?client_id=<?php echo $client_id; ?>&contact_id=<?php echo $contact_id; ?>">
<div class="media">
<?php if ($contact_photo) { ?>
<span class="fa-stack fa-2x mr-3 text-center">

View File

@ -20,10 +20,11 @@
}
}
.grab-cursor {
.drag-handle {
cursor: grab;
touch-action: none;
user-select: none;
}
.grab-cursor:active {
.drag-handle:active {
cursor: grabbing;
}
}

View File

@ -1,41 +1,83 @@
/* General Popover Styling */
.popover {
max-width: 600px;
}
/* Kanban Board Container */
#kanban-board {
display: flex;
box-sizing: border-box;
overflow-x: auto;
box-sizing: border-box;
min-width: 400px;
height: calc(100vh - 210px);
}
/* Kanban Column */
.kanban-column {
flex: 1; /* Allows columns to grow equally */
margin: 0 10px; /* Space between columns */
flex: 1;
min-width: 300px;
max-width: 300px;
margin: 0 10px;
background: #f4f4f4;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 10px;
min-height: calc(100vh - 230px);
max-height: calc(100vh - 230px);
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.kanban-column div {
max-height: calc(100vh - 280px); /* Set your desired max height */
overflow-y: auto; /* Adds a scrollbar when content exceeds max height */
/* Column Inner Scrollable Task Area */
.kanban-status {
flex: 1;
overflow-y: auto;
min-height: 60px;
position: relative;
padding: 5px;
background-color: #f9f9f9;
border-radius: 4px;
}
/* Individual Task Cards */
.task {
background: #fff;
margin: 5px 0;
padding: 10px;
border: 1px solid #ddd;
user-select: none; /* Prevent text selection */
border-radius: 4px;
cursor: grab;
user-select: none;
}
.drag-handle-class {
touch-action: none;
float: right;
/* Grabbing Cursor State */
.task:active {
cursor: grabbing;
}
/* Drag Handle (shown on mobile or with class targeting) */
.drag-handle-class {
float: right;
touch-action: none;
cursor: grab;
}
/* Placeholder shown in empty columns */
.empty-placeholder {
border: 2px dashed #ccc;
background-color: #fcfcfc;
color: #999;
font-style: italic;
padding: 12px;
margin: 10px 0;
text-align: center;
border-radius: 4px;
pointer-events: none;
}
/* Sortable drop zone feedback (optional visual cue) */
.kanban-status.sortable-over {
background-color: #eaf6ff;
transition: background-color 0.2s ease;
}

View File

@ -151,7 +151,7 @@ if ($user_config_dashboard_financial_enable == 1) {
$row = mysqli_fetch_array($sql_unbilled_tickets);
$unbilled_tickets = intval($row['unbilled_tickets']);
} else {
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_id) AS recurring_invoices_added FROM recurring WHERE YEAR(recurring_created_at) = $year"));
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(recurring_invoice_id) AS recurring_invoices_added FROM recurring_invoices WHERE YEAR(recurring_invoice_created_at) = $year"));
$recurring_invoices_added = intval($row['recurring_invoices_added']);
}

View File

@ -1614,10 +1614,14 @@ function getFieldById($table, $id, $field, $escape_method = 'sql') {
}
// Recursive function to display folder options - Used in folders files and documents
function display_folder_options($parent_folder_id, $client_id, $indent = 0) {
function display_folder_options($parent_folder_id, $client_id, $folder_location = 0, $indent = 0) {
global $mysqli;
$sql_folders = mysqli_query($mysqli, "SELECT * FROM folders WHERE parent_folder = $parent_folder_id AND folder_location = 1 AND folder_client_id = $client_id ORDER BY folder_name ASC");
$folder_location = intval($folder_location);
// 0 = Document Folders
// 1 = File Folders
$sql_folders = mysqli_query($mysqli, "SELECT * FROM folders WHERE parent_folder = $parent_folder_id AND folder_location = $folder_location AND folder_client_id = $client_id ORDER BY folder_name ASC");
while ($row = mysqli_fetch_array($sql_folders)) {
$folder_id = intval($row['folder_id']);
$folder_name = nullable_htmlentities($row['folder_name']);
@ -1627,13 +1631,14 @@ function display_folder_options($parent_folder_id, $client_id, $indent = 0) {
// Check if this folder is selected
$selected = '';
if ((isset($_GET['folder_id']) && $_GET['folder_id'] == $folder_id) || (isset($_POST['folder']) && $_POST['folder'] == $folder_id)) {
if ((isset($_GET['folder_id']) && intval($_GET['folder_id']) === $folder_id) ||
(isset($_POST['folder']) && intval($_POST['folder']) === $folder_id)) {
$selected = 'selected';
}
echo "<option value=\"$folder_id\" $selected>$indentation$folder_name</option>";
// Recursively display subfolders
display_folder_options($folder_id, $client_id, $indent + 1);
display_folder_options($folder_id, $client_id, $folder_location, $indent + 1);
}
}

View File

@ -5,4 +5,4 @@
* Update this file each time we merge develop into master. Format is YY.MM (add a .v if there is more than one release a month.
*/
DEFINE("APP_VERSION", "25.03.4");
DEFINE("APP_VERSION", "25.03.5");

View File

@ -100,7 +100,7 @@ if ($_GET['canned_date'] == "custom" && !empty($_GET['dtf'])) {
$dtt = date('Y-m-d', strtotime("last day of december last year"));
} else {
$dtf = "NULL";
$dtt = date('Y-m-d');
$dtt = '9999-12-31';
}
// Archived

View File

@ -14,7 +14,11 @@ if (file_exists("config.php")) {
<!-- Page Content -->
<h1>Blank Page</h1>
<hr>
<?php
<?php
if (isset($config_start_page)) { ?>
<meta http-equiv="refresh" content="0;url=<?php echo $config_start_page; ?>">
<?php }
require_once "includes/footer.php";

View File

@ -165,7 +165,6 @@ if (isset($_GET['invoice_id'])) {
?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<ol class="breadcrumb d-print-none">
<?php if (isset($_GET['client_id'])) { ?>
@ -381,26 +380,34 @@ if (isset($_GET['invoice_id'])) {
<tr data-item-id="<?php echo $item_id; ?>">
<td class="d-print-none">
<?php if ($invoice_status !== "Paid" && $invoice_status !== "Cancelled") { ?>
<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_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
<div class="row">
<div class="col">
<button type="button" class="btn btn-sm btn-light drag-handle">
<i class="fas fa-bars text-muted"></i>
</button>
</div>
<div class="col">
<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_invoice_item=<?php echo $item_id; ?>"><i class="fa fa-fw fa-trash mr-2"></i>Delete</a>
</div>
</div>
</div>
</div>
<?php } ?>
</td>
<td class="grab-cursor"><?php echo $item_name; ?></td>
<td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></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>
@ -1178,38 +1185,23 @@ require_once "includes/footer.php";
}
</script>
<script src="plugins/dragula/dragula.min.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
$(document).ready(function() {
var container = $('table#items tbody')[0];
new Sortable(document.querySelector('table#items tbody'), {
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])
.on('drop', function (el, target, source, sibling) {
// Handle the drop event to update the order in the database
var rows = $(container).children();
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);
}
});
$.post('ajax.php', {
update_invoice_items_order: true,
invoice_id: <?php echo $invoice_id; ?>,
positions: positions
});
}
});
</script>

View File

@ -1,146 +1,126 @@
$(document).ready(function() {
console.log('CONFIG_TICKET_MOVING_COLUMNS: ' + CONFIG_TICKET_MOVING_COLUMNS);
console.log('CONFIG_TICKET_ORDERING: ' + CONFIG_TICKET_ORDERING);
$(document).ready(function () {
console.log('CONFIG_TICKET_MOVING_COLUMNS:', CONFIG_TICKET_MOVING_COLUMNS);
console.log('CONFIG_TICKET_ORDERING:', CONFIG_TICKET_ORDERING);
// Function to detect touch devices
function isTouchDevice() {
return 'ontouchstart' in window || navigator.maxTouchPoints;
}
// Initialize Dragula for the Kanban board
let boardDrake = dragula([
document.querySelector('#kanban-board')
], {
moves: function(el, container, handle) {
return handle.classList.contains('panel-title');
},
accepts: function(el, target, source, sibling) {
return CONFIG_TICKET_MOVING_COLUMNS === 1;
}
});
// Log the event of moving the column panel-title
boardDrake.on('drag', function(el) {
//console.log('Dragging column:', el.querySelector('.panel-title').innerText);
});
boardDrake.on('drop', function(el, target, source, sibling) {
//console.log('Dropped column:', el.querySelector('.panel-title').innerText);
// Get all columns and their positions
let columns = document.querySelectorAll('#kanban-board .kanban-column');
let columnPositions = [];
columns.forEach(function(column, index) {
let statusId = $(column).data('status-id'); // Assuming you have a data attribute for status ID
columnPositions.push({
status_id: statusId,
// -------------------------------
// Drag: Kanban Columns (Statuses)
// -------------------------------
new Sortable(document.querySelector('#kanban-board'), {
animation: 150,
handle: '.panel-title',
draggable: '.kanban-column',
onEnd: function () {
const columnPositions = Array.from(document.querySelectorAll('#kanban-board .kanban-column')).map((col, index) => ({
status_id: $(col).data('status-id'),
status_kanban: index
});
});
}));
// Send AJAX request to update all column positions
$.ajax({
url: 'ajax.php',
type: 'POST',
data: {
update_kanban_status_position: true,
positions: columnPositions
},
success: function(response) {
console.log('Ticket status kanban orders updated successfully.');
// Optionally, you can refresh the page or update the UI here
},
error: function(xhr, status, error) {
console.error('Error updating ticket status kanban orders:', error);
}
});
});
// Initialize Dragula for the Kanban Cards
let drake = dragula([
...document.querySelectorAll('#status')
], {
moves: function(el, container, handle) {
if (isTouchDevice()) {
return handle.classList.contains('drag-handle-class');
} else {
return true; // Allow dragging on the entire task element for desktop
if (CONFIG_TICKET_MOVING_COLUMNS === 1) {
$.post('ajax.php', {
update_kanban_status_position: true,
positions: columnPositions
}).done(() => {
console.log('Ticket status kanban orders updated.');
}).fail((xhr) => {
console.error('Error updating status order:', xhr.responseText);
});
}
}
});
// -------------------------------
// Drag: Tasks within Columns
// -------------------------------
document.querySelectorAll('.kanban-status').forEach(statusCol => {
new Sortable(statusCol, {
group: 'tickets',
animation: 150,
handle: isTouchDevice() ? '.drag-handle-class' : undefined,
onStart: () => hidePlaceholders(),
onEnd: function (evt) {
const target = evt.to;
const movedEl = evt.item;
// Disallow reordering in same column if config says so
if (CONFIG_TICKET_ORDERING === 0 && evt.from === evt.to) {
evt.from.insertBefore(movedEl, evt.from.children[evt.oldIndex]);
showPlaceholders();
return;
}
const columnId = $(target).data('status-id');
const positions = Array.from(target.querySelectorAll('.task')).map((card, index) => {
const ticketId = $(card).data('ticket-id');
const oldStatus = ticketId === $(movedEl).data('ticket-id')
? $(movedEl).data('ticket-status-id')
: false;
$(card).data('ticket-status-id', columnId); // update DOM
return {
ticket_id: ticketId,
ticket_order: index,
ticket_oldStatus: oldStatus,
ticket_status: columnId
};
});
$.post('ajax.php', {
update_kanban_ticket: true,
positions: positions
}).done(() => {
console.log('Updated kanban ticket positions.');
}).fail((xhr) => {
console.error('Error updating ticket positions:', xhr.responseText);
});
// Refresh placeholders after update
showPlaceholders();
}
});
});
// -------------------------------
// 📱 Touch Support: Show drag handle on mobile
// -------------------------------
if (isTouchDevice()) {
const moveList = document.querySelectorAll('.task');
moveList.forEach(task => {
task.querySelector('.drag-handle-class').style.display = 'inline';
$('.drag-handle-class').css('display', 'inline');
}
// -------------------------------
// Placeholder Management
// -------------------------------
function showPlaceholders() {
document.querySelectorAll('.kanban-status').forEach(status => {
const placeholderClass = 'empty-placeholder';
// Remove existing placeholder
const existing = status.querySelector(`.${placeholderClass}`);
if (existing) existing.remove();
// Only show if there are no tasks
if (status.querySelectorAll('.task').length === 0) {
const placeholder = document.createElement('div');
placeholder.className = `${placeholderClass} text-muted text-center p-2`;
placeholder.innerText = 'Drop ticket here';
placeholder.style.pointerEvents = 'none';
status.appendChild(placeholder);
}
});
}
drake.on('drag', function(el) {
el.style.cursor = 'grabbing';
});
drake.on('dragend', function(el) {
el.style.cursor = 'grab';
});
// Add event listener for the drop event
drake.on('drop', function (el, target, source, sibling) {
// Log the target ID to the console
//console.log('Dropped into:', target.getAttribute('data-column-name'));
function hidePlaceholders() {
document.querySelectorAll('.empty-placeholder').forEach(el => el.remove());
}
if (CONFIG_TICKET_ORDERING === 0 && source == target) {
drake.cancel(true); // Move the card back to its original position
return;
}
// Get all cards in the target column and their positions
let cards = $(target).children('.task');
let positions = [];
// Run once on load
showPlaceholders();
//id of current status / column
let columnId = $(target).data('status-id');
let movedTicketId = $(el).data('ticket-id');
let movedTicketStatusId = $(el).data('ticket-status-id');
cards.each(function(index, card) {
let ticketId = $(card).data('ticket-id');
let statusId = $(card).data('ticket-status-id');
let oldStatus = false;
if (ticketId == movedTicketId) {
oldStatus = movedTicketStatusId;
}
//update the status id of the card if needed
if (statusId != columnId) {
$(card).data('ticket-status-id', columnId);
statusId = columnId;
}
positions.push({
ticket_id: ticketId,
ticket_order: index,
ticket_oldStatus: oldStatus,
ticket_status: statusId ?? null// Get the new status ID from the target column
});
});
//console.log(positions);
// Send AJAX request to update all ticket kanban orders and statuses
$.ajax({
url: 'ajax.php',
type: 'POST',
data: {
update_kanban_ticket: true,
positions: positions
},
success: function(response) {
//console.log('Ticket kanban orders and statuses updated successfully.');
},
error: function(xhr, status, error) {
console.error('Error updating ticket kanban orders and statuses:', error);
}
});
});
});
// -------------------------------
// Utility: Detect touch device
// -------------------------------
function isTouchDevice() {
return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
}
});

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
<input type="hidden" name="asset_id" value="<?php echo intval($_GET['asset_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
<input type="hidden" name="asset_id" value="<?php echo intval($_GET['asset_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
<input type="hidden" name="asset_id" value="<?php echo intval($_GET['asset_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
<input type="hidden" name="asset_id" value="<?php echo intval($_GET['asset_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="asset_id" value="<?php echo $asset_id; ?>">
<input type="hidden" name="asset_id" value="<?php echo intval($_GET['asset_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -37,7 +37,7 @@
<option value="0">/</option>
<?php
// Start displaying folder options from the root (parent_folder = 0)
display_folder_options(0, $client_id);
display_folder_options(0, $client_id, 1);
?>
</select>
</div>

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

View File

@ -8,7 +8,7 @@
</button>
</div>
<form action="post.php" method="post" autocomplete="off">
<input type="hidden" name="contact_id" value="<?php echo $contact_id; ?>">
<input type="hidden" name="contact_id" value="<?php echo intval($_GET['contact_id']); ?>">
<div class="modal-body bg-white">
<div class="form-group">

2
plugins/SortableJS/Sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.gu-mirror{position:fixed!important;margin:0!important;z-index:9999!important;opacity:.8}.gu-hide{display:none!important}.gu-unselectable{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.gu-transit{opacity:.2}

File diff suppressed because one or more lines are too long

View File

@ -123,7 +123,6 @@ if (isset($_GET['quote_id'])) {
);
?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<ol class="breadcrumb d-print-none">
<?php if (isset($_GET['client_id'])) { ?>
@ -326,27 +325,37 @@ if (isset($_GET['quote_id'])) {
<tr data-item-id="<?php echo $item_id; ?>">
<td class="d-print-none">
<?php if ($quote_status !== "Invoiced" && $quote_status !== "Accepted" && $quote_status !== "Declined" && lookupUserPermission("module_sales") >= 2) { ?>
<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_quote_item=<?php echo $item_id; ?>">
<i class="fa fa-fw fa-trash mr-2"></i>Delete
</a>
<div class="row">
<div class="col">
<button type="button" class="btn btn-sm btn-light drag-handle">
<i class="fas fa-bars text-muted"></i>
</button>
</div>
<div class="col">
<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_quote_item=<?php echo $item_id; ?>">
<i class="fa fa-fw fa-trash mr-2"></i>Delete
</a>
</div>
</div>
</div>
</div>
<?php } ?>
</td>
<td class="grab-cursor"><?php echo $item_name; ?></td>
<td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></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>
@ -992,38 +1001,23 @@ require_once "includes/footer.php";
}
</script>
<script src="plugins/dragula/dragula.min.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
$(document).ready(function() {
var container = $('table#items tbody')[0];
new Sortable(document.querySelector('table#items tbody'), {
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])
.on('drop', function (el, target, source, sibling) {
// Handle the drop event to update the order in the database
var rows = $(container).children();
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);
}
});
$.post('ajax.php', {
update_quote_items_order: true,
quote_id: <?php echo $quote_id; ?>,
positions: positions
});
}
});
</script>

View File

@ -272,26 +272,34 @@ if (isset($_GET['recurring_invoice_id'])) {
<tr data-item-id="<?php echo $item_id; ?>">
<td class="d-print-none">
<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 class="row">
<div class="col">
<button type="button" class="btn btn-sm btn-light drag-handle">
<i class="fas fa-bars text-muted"></i>
</button>
</div>
<div class="col">
<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>
</td>
<td class="grab-cursor"><?php echo $item_name; ?></td>
<td><?php echo $item_name; ?></td>
<td><?php echo nl2br($item_description); ?></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>
@ -483,39 +491,23 @@ require_once "includes/footer.php";
});
</script>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<script src="plugins/dragula/dragula.min.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
$(document).ready(function() {
var container = $('table#items tbody')[0];
new Sortable(document.querySelector('table#items tbody'), {
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])
.on('drop', function (el, target, source, sibling) {
// Handle the drop event to update the order in the database
var rows = $(container).children();
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);
}
});
$.post('ajax.php', {
update_recurring_invoice_items_order: true,
recurring_invoice_id: <?php echo $recurring_invoice_id; ?>,
positions: positions
});
}
});
</script>

View File

@ -341,7 +341,6 @@ if (isset($_GET['ticket_id'])) {
$ticket_collaborators = nullable_htmlentities($row['user_names']);
?>
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<!-- Breadcrumbs-->
<ol class="breadcrumb d-print-none">
@ -940,7 +939,7 @@ if (isset($_GET['ticket_id'])) {
</form>
<?php } ?>
<table class="table table-sm">
<table class="table table-sm" id="tasks">
<?php
while($row = mysqli_fetch_array($sql_tasks)){
$task_id = intval($row['task_id']);
@ -960,14 +959,14 @@ if (isset($_GET['ticket_id'])) {
<?php } ?>
</td>
<td>
<a href="#" class="grab-cursor">
<span class="text-secondary"><?php echo $task_completion_estimate; ?>m</span>
<span class="text-dark"> - <?php echo $task_name; ?></span>
</a>
<a href="#" class="drag-handle"><i class="fas fa-bars text-muted mr-1"></i></a>
<span class="text-secondary"><?php echo $task_completion_estimate; ?>m</span>
<span class="text-dark"> - <?php echo $task_name; ?></span>
</td>
<td>
<div class="float-right">
<?php if (empty($ticket_resolved_at) && lookupUserPermission("module_support") >= 2) { ?>
<div class="dropdown dropleft text-center">
<button class="btn btn-link text-secondary btn-sm" type="button" data-toggle="dropdown">
<i class="fas fa-fw fa-ellipsis-v"></i>
@ -991,6 +990,7 @@ if (isset($_GET['ticket_id'])) {
</a>
</div>
</div>
<?php } ?>
</div>
</td>
@ -1207,41 +1207,23 @@ require_once "includes/footer.php";
});
</script>
<script src="plugins/dragula/dragula.min.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script>
$(document).ready(function() {
var container = $('.table tbody')[0];
new Sortable(document.querySelector('table#tasks tbody'), {
handle: '.drag-handle',
animation: 150,
onEnd: function (evt) {
const rows = document.querySelectorAll('table#tasks tbody tr');
const positions = Array.from(rows).map((row, index) => ({
id: row.dataset.taskId,
order: index
}));
dragula([container])
.on('drop', function (el, target, source, sibling) {
// Handle the drop event to update the order in the database
var rows = $(container).children();
var positions = rows.map(function(index, row) {
return {
id: $(row).data('taskId'),
order: index
};
}).get();
//console.log('New positions:', positions);
// Send the new order to the server (example using fetch)
$.ajax({
url: 'ajax.php',
method: 'POST',
data: {
update_ticket_tasks_order: true,
ticket_id: <?php echo $ticket_id; ?>,
positions: positions
},
success: function(data) {
//console.log('Order updated:', data);
},
error: function(error) {
console.error('Error updating order:', error);
}
});
});
});
$.post('ajax.php', {
update_ticket_tasks_order: true,
ticket_id: <?php echo $ticket_id; ?>,
positions: positions
});
}
});
</script>

View File

@ -1,4 +1,3 @@
<link rel="stylesheet" href="plugins/dragula/dragula.min.css">
<link rel="stylesheet" href="css/tickets_kanban.css">
<?php
@ -82,7 +81,7 @@ $kanban = array_values($statuses);
?>
<div class="kanban-column card card-dark" data-status-id="<?=htmlspecialchars($kanban_column->id); ?>">
<h6 class="panel-title"><?=htmlspecialchars($kanban_column->name); ?></h6>
<div id="status" data-column-name="<?=$kanban_column->name?>" data-status-id="<?=htmlspecialchars($kanban_column->id); ?>" style="height: 100%;" >
<div class="kanban-status" data-column-name="<?=$kanban_column->name?>" data-status-id="<?=htmlspecialchars($kanban_column->id); ?>">
<?php
foreach($kanban_column->tickets as $item){
if ($item['ticket_priority'] == "High") {
@ -106,7 +105,7 @@ $kanban = array_values($statuses);
<span class='badge badge-secondary'>
<?php echo $item['category_name']; ?>
</span>
<div class='btn btn-secondary drag-handle-class' style="display: none;">
<div class='btn btn-light drag-handle-class' style="display: none;">
<i class="drag-handle-class fas fa-bars"></i>
</div>
<br>
@ -154,5 +153,5 @@ echo "const CONFIG_TICKET_MOVING_COLUMNS = " . json_encode($config_ticket_movi
echo "const CONFIG_TICKET_ORDERING = " . json_encode($config_ticket_ordering) . ";";
echo "</script>";
?>
<script src="plugins/dragula/dragula.min.js"></script>
<script src="plugins/SortableJS/Sortable.min.js"></script>
<script src="js/tickets_kanban.js"></script>