CHANGE_TicketHistory

This commit is contained in:
venba-Inspriron-3558 2025-08-28 15:29:34 +05:30
parent 3006b172e5
commit 44a8af4e2e
7 changed files with 350 additions and 131 deletions

View File

@ -562,6 +562,7 @@ $routes->group("employeeRest", ["filter" => "authJWT"], function ($routes) {
$routes->get("ticketList", "ThzController::ticketList");
$routes->post("ticketConversationSave", "ThzController::ticketConversationSave");
$routes->get("ticketConversationList", "ThzController::ticketConversationList");
$routes->get("ticketHistoryList", "ThzController::ticketHistoryList");
$routes->get("hrFileList", "EmployeeRestController::hrFileList");
$routes->get("hrFileUploadMasters", "EmployeeRestController::hrFileUploadMasters");
@ -660,4 +661,5 @@ $routes->get("ticketList", "ThzController::ticketList");
$routes->post("ticketConversationSave", "ThzController::ticketConversationSave");
$routes->get("ticketConversationList", "ThzController::ticketConversationList");
$routes->post('ticketAutoFetchDetails',"ThzController::ticketAutoFetchDetails");
// $routes->match(['get','post','put'], 'ticketType', 'ThzController::ticketType');
$routes->match(['get','post','put'], 'ticketType', 'ThzController::ticketType');
$routes->get("ticketHistoryList", "ThzController::ticketHistoryList");

View File

@ -8,12 +8,13 @@ use CodeIgniter\HTTP\ResponseInterface;
use App\Models\ThzMasterModel;
use App\Models\ThzMasterNotesModel;
use App\Models\ThzTypeModel;
use App\Models\ThzHistoryModel;
use App\ControllerCleaners\ThzControllerCleaner;
use App\Models\UserModel;
use App\Models\ClientPolicyModel;
use App\Models\ClientModel;
use App\Models\TicketMailTemplateModel;
// use App\Models\TicketTypesModel;
class ThzController extends BaseController
@ -21,7 +22,8 @@ class ThzController extends BaseController
protected $thzMasterModel;
protected $thzMasterNotesModel;
protected $thzTypeModel;
protected $thzHistoryModel;
protected $thzControllerCleaner;
protected $userModel;
@ -29,16 +31,17 @@ class ThzController extends BaseController
protected $clientModel;
protected $ticketMailTemplateModel;
// protected $ticketTypesModel;
public function __construct()
{
$this->thzMasterModel = new ThzMasterModel();
$this->thzMasterNotesModel = new ThzMasterNotesModel();
$this->thzTypeModel = new thzTypeModel();
$this->thzHistoryModel = new ThzHistoryModel();
$this->thzControllerCleaner = new ThzControllerCleaner();
$this->userModel = new UserModel();
$this->clientPolicyModel = new ClientPolicyModel();
$this->clientModel = new ClientModel();
$this->clientModel = new ClientModel();
$this->ticketMailTemplateModel = new TicketMailTemplateModel();
}
@ -55,6 +58,28 @@ class ThzController extends BaseController
$text = "update";
$result = $this->updateTicket($data);
// here insert history Status.
$old = $this->thzMasterModel->where('thz_id', $data['thz_id'])->get()->getRowArray();
if ($old && isset($data['status'])) {
if ($old['status'] != $data['status']) {
$history = [
'thz_id' => $data['thz_id'],
'field_name' => 'status',
'display_name' => 'Status',
'old_value' => $old['status'],
'new_value' => $data['status'],
'is_active' => 1
];
if (!empty(get_session_userid())) {
$history['created_by'] = get_session_userid();
}
$this->thzHistoryModel->insert($history);
}
}
$updateID = $data['thz_id'];
} else {
@ -228,43 +253,63 @@ class ThzController extends BaseController
}
// public function ticketType()
// {
// $method = $this->request->getMethod(); // get, post
public function ticketType()
{
$method = $this->request->getMethod(); // get, post
// if ($method === 'get') {
if ($method === 'get') {
// $types = $this->ticketTypesModel->where('is_active', 1)->findAll();
// if (empty($types)) {
// return $this->response->setJSON(['status' => 'error','message' => 'No ticket types found'])->setStatusCode(404);
// }
// return $this->response->setJSON(['status' => 'success','data' => $types])->setStatusCode(200);
$types = $this->thzTypeModel->where('is_active', 1)->findAll();
if (empty($types)) {
return $this->response->setJSON(['status' => 'error','message' => 'No ticket types found'])->setStatusCode(404);
}
return $this->response->setJSON(['status' => 'success','data' => $types])->setStatusCode(200);
// }
}
// if ($method === 'post') {
if ($method === 'post') {
// $data = $this->request->getPost();
// if (!empty($data['id'])) {
// $text = "update";
// $result = $this->ticketTypesModel->update($data['id'], $data);
// $updateID = $data['id'];
// } else {
// $text = "create";
// $insertID = $this->ticketTypesModel->insert($data);
// $result = true;
// }
$data = $this->request->getPost();
if (!empty($data['id'])) {
$text = "update";
$result = $this->thzTypeModel->update($data['id'], $data);
$updateID = $data['id'];
} else {
$text = "create";
$insertID = $this->thzTypeModel->insert($data);
$result = true;
}
// $id = isset($insertID) && !empty($insertID) ? $insertID : $updateID ;
$id = isset($insertID) && !empty($insertID) ? $insertID : $updateID ;
// return $this->response->setJSON([
// 'status' => $id ? 'success' : 'error',
// 'message' => $id ? "Ticket Type {$text}d successfully " : "Unable to {$text} ticket type. Please try again." ,
// 'id' => $id
// ])->setStatusCode($id ? 200 : 400);
// }
// return $this->response->setJSON(['status' => 'error','message' => 'No ticket types found'])->setStatusCode(404);
// }
return $this->response->setJSON([
'status' => $id ? 'success' : 'error',
'message' => $id ? "Ticket Type {$text}d successfully " : "Unable to {$text} ticket type. Please try again." ,
'id' => $id
])->setStatusCode($id ? 200 : 400);
}
return $this->response->setJSON(['status' => 'error','message' => 'No ticket types found'])->setStatusCode(404);
}
public function ticketHistoryList()
{
$data = $this->request->getGet();
$thz_id = $data['thz_id'];
// $details = $this->thzHistoryModal->whereIn('thz_id', $thz_id)->where('is_active', 1)->get()->getResultArray();
$details = $this->thzHistoryModel->select('thz_history.*, CONCAT(user_profiles.first_name, " ", user_profiles.last_name) as created_name')
->join('user_profiles', 'user_profiles.id = thz_history.created_by', 'left')
->where('thz_history.thz_id', $thz_id)->where('thz_history.is_active', 1)->get()->getResultArray();
if (empty($details)) {
return $this->response->setJSON([ 'status' => 'error','message' => 'No History found'])->setStatusCode(404);
}
return $this->response->setJSON(['status' => 'success','data' => $details])->setStatusCode(200);
}
/************************************************** PRIVATE FUNCTIONS ********************************************************/

View File

@ -0,0 +1,45 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class ThzHistoryModel extends Model
{
protected $table = 'thz_history';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['thz_id','field_name','display_name','old_value','new_value','created_by','created_at','is_active'];
protected bool $allowEmptyInserts = false;
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}

View File

@ -4,9 +4,9 @@ namespace App\Models;
use CodeIgniter\Model;
class TicketTypesModel extends Model
class ThzTypeModel extends Model
{
protected $table = 'ticket_types';
protected $table = 'thz_type';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';

View File

@ -665,6 +665,39 @@ a.app-badge-secondary:focus, a.app-badge-secondary.focus {
outline: 0;
box-shadow: 0 0 0 0.15rem rgba(226, 103, 40, 0.5); }
.app-table-head th {
background-color: #00999E;
color: #fff !important;
border-top: 0px solid #fff !important;
border-bottom: 0px solid #fff !important;
}
.app-table-body td {
background: #F5FFFF;
padding: 12px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.app-table-head th:first-child {
border-top-left-radius: 15px !important;
border-bottom-left-radius: 15px !important;
}
.app-table-head th:last-child {
border-top-right-radius: 15px !important;
border-bottom-right-radius: 15px !important;
}
.app-table-body tr td:first-child {
border-top-left-radius: 15px !important;
border-bottom-left-radius: 15px !important;
}
.app-table-body tr td:last-child {
border-top-right-radius: 15px !important;
border-bottom-right-radius: 15px !important;
}
</style>

View File

@ -7,52 +7,17 @@
width: 100%;
}
/* HEADER */
thead th {
background-color: #00999E;
color: #fff !important;
border-top: 0px solid #fff !important;
border-bottom: 0px solid #fff !important;
}
/* .table thead th {
vertical-align: bottom;
border-bottom: 2px solid #fff;
} */
/* Rounded header corners */
thead th:first-child {
border-top-left-radius: 15px !important;
border-bottom-left-radius: 15px !important;
}
thead th:last-child {
border-top-right-radius: 15px !important;
border-bottom-right-radius: 15px !important;
}
/* BODY ROWS */
tbody td {
background: #F5FFFF; /* apply bg to td not tr */
padding: 12px;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
/* Add shadow + spacing row "card" effect */
tbody tr {
/* box-shadow: 0 2px 5px rgba(0,0,0,0.08); */
}
/* Rounded corners for first + last cell of each row */
tbody tr td:first-child {
border-top-left-radius: 15px !important;
border-bottom-left-radius: 15px !important;
}
tbody tr td:last-child {
border-top-right-radius: 15px !important;
border-bottom-right-radius: 15px !important;
}
/* Hover effect */
#scroll-horizontal-datatable tbody tr:hover td {
background-color: #e0e0e0;
@ -123,8 +88,7 @@ tbody tr td:last-child {
white-space: nowrap !important;
} */
.text-custom-black{
.app-text-black{
color: #000000 !important;
}
.text-custom-grey{
@ -251,8 +215,8 @@ table thead th {
</div>
</div>
<div>
<table id="scroll-horizontal-datatable" class="table w-100 nowrap text-custom-black text-custom app-font-family">
<thead>
<table id="scroll-horizontal-datatable" class="table w-100 nowrap app-text-black text-custom app-font-family">
<thead class="app-table-head">
<tr>
<th>Ticket ID</th>
<th class="app-text-left">Name</th>
@ -265,7 +229,7 @@ table thead th {
<th>Updated At</th>
</tr>
</thead>
<tbody>
<tbody class="app-table-body">
<?php if (isset($ticket_data)) { ?>
<?php foreach($ticket_data as $index => $row){ ?>
@ -394,9 +358,17 @@ table thead th {
<label for="ticket_type">Ticket Type<span class="text-danger">*</span></label>
<select class="form-control" id="ticket_type" name="ticket_type" required onchange="hideandshowpolicy()">
<option value="">Select Ticket Type</option>
<option value="Sales" selected >Sales</option>
<option value="Service">Service</option>
<!-- <option value="0">+ Add New</option> -->
<!-- <option value="Sales" selected >Sales</option>
<option value="Service">Service</option> -->
<option value="0">+ Add New</option>
<?php if (!empty($ticket_type)): ?>
<?php foreach ($ticket_type as $t): ?>
<option value="<?= esc($t['name']) ?>"
<?= ($t['name'] === "Sales") ? 'selected' : '' ?>>
<?= esc($t['name']) ?>
</option>
<?php endforeach; ?>
<?php endif; ?>
</select>
</div>
@ -683,62 +655,66 @@ table thead th {
document.getElementById('policy_id').value = policyId || "";
}
function hideandshowpolicy() {
var select = document.getElementById('ticket_type').value;
var policyContainer = document.getElementById('policy_container');
// function hideandshowpolicy() {
// var select = document.getElementById('ticket_type').value;
// var policyContainer = document.getElementById('policy_container');
if (select === 'Service') {
policyContainer.style.display = "block"; // show
// if (select === 'Service') {
// policyContainer.style.display = "block"; // show
// } else {
// policyContainer.style.display = "none"; // hide
// document.getElementById('policy_no').value = "";
// document.getElementById('policy_id').value = "";
// }
// }
function hideandshowpolicy() {
var select = document.getElementById('ticket_type');
var value = select.value;
var policyContainer = document.getElementById('policy_container');
if (value === "0") {
let newType = prompt("Enter new ticket type:");
if (newType && newType.trim() !== "") {
var formData = new FormData(form);
$.ajax({
url: "<?= base_url('ticketType') ?>",
type: "POST",
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function(response) {
if (response.status === "success") {
let option = new Option(newType, newType);
let addNewOption = select.querySelector('option[value="0"]');
select.insertBefore(option, addNewOption);
select.value = newType;
value = newType;
console.log(response.message);
} else { console.log(response.message); value = ""; }
},
error: function(xhr) { console.log("Error: " + xhr.statusText); value = ""; }
});
} else {
policyContainer.style.display = "none"; // hide
document.getElementById('policy_no').value = "";
document.getElementById('policy_id').value = "";
select.value = "";
value = "";
}
}
// function hideandshowpolicy() {
// var select = document.getElementById('ticket_type');
// var value = select.value;
// var policyContainer = document.getElementById('policy_container');
if (value === 'Service') {
policyContainer.style.display = "block";
} else {
policyContainer.style.display = "none";
document.getElementById('policy_no').value = "";
document.getElementById('policy_id').value = "";
}
// if (value === "0") {
// let newType = prompt("Enter new ticket type:");
// if (newType && newType.trim() !== "") {
// $.ajax({
// url: "<?= base_url('ticketType') ?>",
// type: "POST",
// data: { name: newType },
// dataType: 'json',
// success: function(response) {
// if (response.status === "success") {
// let option = new Option(newType, newType);
// let addNewOption = select.querySelector('option[value="0"]');
// select.insertBefore(option, addNewOption);
// select.value = newType;
// value = newType;
// console.log(response.message);
// } else { console.log(response.message); value = ""; }
// },
// error: function(xhr) { console.log("Error: " + xhr.statusText); value = ""; }
// });
// } else {
// select.value = "";
// value = "";
// }
// }
// if (value === 'Service') {
// policyContainer.style.display = "block";
// } else {
// policyContainer.style.display = "none";
// document.getElementById('policy_no').value = "";
// document.getElementById('policy_id').value = "";
// }
// }
}
</script>
<script>

View File

@ -1,4 +1,12 @@
<style>
#historytable thead{
background-color: #00999E;
color: #ffffff !important;
}
#historytable tbody tr:hover td {
background-color: #e0e0e0;
}
.notes-box {
display: flex;
@ -209,7 +217,20 @@ hr{
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey app-font-family">Status</div>
<div class="col-8 app-text app-text-black text-end app-font-family" style="font-weight: 500 !important;"><?= $master[0]['status'];?></div>
<div class="col-8 app-text app-text-black text-end app-font-family" style="font-weight: 500 !important;">
<?= (!empty($master[0]['status']) && strtolower($master[0]['status']) !== 'null')
? '<u class="app-text-black">
<a href="javascript:void(0);"
class="app-text-black"
onclick="statusHistory('.$master[0]['thz_id'].')"
data-toggle="modal"
data-target="#StatusModal"
title="Click to view Status History">'
.$master[0]['status'].'
</a>
</u>'
: 'N/A'; ?>
</div>
</div>
<div class="row mb-2">
<div class="col-4 app-subtitle app-text-grey app-font-family">Ticket Type</div>
@ -447,6 +468,22 @@ hr{
</div>
</div>
<div class="modal fade app-font-family" id="StatusModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content" style="border-radius:15px;">
<div class="modal-header" style="border-bottom-width:0;">
<h4 class="modal-title title-text app-font-family">Status History</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body card-scroll container" style="padding:5px 11px 11px 11px!important;">
</div>
</div>
</div>
</div>
<script>
@ -566,4 +603,85 @@ hr{
}
});
}
</script>
<script>
function statusHistory(thzId) {
$.ajax({
url: "<?= base_url('ticketHistoryList?thz_id=') ?>" + thzId,
type: "GET",
dataType: "json",
success: function(response) {
let container = $(".container");
container.empty(); // clear old content
if (response.status === "success" && response.data.length > 0) {
let table = `
<table id="historytable" class="table table-bordered table-sm app-font-family">
<thead>
<tr>
<th class="app-text-white">S.No</th>
<th class="app-text-white">Old Value</th>
<th class="app-text-white">New Value</th>
<th class="app-text-white">Updated By</th>
<th class="app-text-white">Date/Time</th>
</tr>
</thead>
<tbody>
`;
response.data.forEach((row, index) => {
table += `
<tr>
<td style="text-align:left; vertical-align:middle;">${index + 1}</td>
<td style="text-align:left; vertical-align:middle;">${row.old_value ?? '-'}</td>
<td style="text-align:left; vertical-align:middle;">${row.new_value ?? '-'}</td>
<td style="text-align:left; vertical-align:middle;">${row.created_name ?? '-'}</td>
<td style="text-align:left; vertical-align:middle;">${row.created_at
? (() => {
let dt = new Date(row.created_at);
let dateStr = dt.toLocaleDateString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
});
let timeStr = dt.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true
}).toLowerCase();
return `${dateStr}<br>${timeStr}`;
})()
: '-'
}</td>
</tr>
`;
});
table += `</tbody></table>`;
container.html(table);
} else {
container.html(`
<div class="d-flex justify-content-center align-items-center" style="height: 50vh; width:100%;">
<p class="text-center"><i>${response.message ?? 'No history found'}</i></p>
</div>
`);
}
$("#StatusModal").modal("show");
},
error: function(xhr) {
$(".container").html(`
<div class="d-flex justify-content-center align-items-center" style="height: 50vh; width:100%;">
<p class="text-center"><i>No Data Found</i></p>
</div>
`);
$("#StatusModal").modal("show");
// toastr.error("Something went wrong!", "Error");
console.error(xhr.responseText);
}
});
}
</script>