614 lines
23 KiB
PHP
614 lines
23 KiB
PHP
<?php
|
||
/**
|
||
* Inspection History - Row level photo and comment upload
|
||
*/
|
||
use App\Models\InspectionHistory;
|
||
use App\Models\User;
|
||
|
||
// Add Magnific Popup assets
|
||
?>
|
||
<link rel="stylesheet" href="<?php echo asset('assets/admin/js/plugins/magnific-popup/magnific-popup.css'); ?>">
|
||
<?php
|
||
|
||
|
||
$rowId = get("row_id");
|
||
$table = get("table");
|
||
$moduleSlug = get("module_slug");
|
||
|
||
// Get settings configuration if module_slug provided
|
||
$inspectionConfig = null;
|
||
$inspectionHistoryPath = null;
|
||
$inspectionHistoryFileNamePattern = null;
|
||
|
||
if ($moduleSlug) {
|
||
$inspectionConfig = getInspectionHistoryConfig($moduleSlug);
|
||
if ($inspectionConfig) {
|
||
$inspectionHistoryPath = $inspectionConfig['path'] ?? null;
|
||
$inspectionHistoryFileNamePattern = $inspectionConfig['pattern'] ?? null;
|
||
}
|
||
}
|
||
|
||
// Get inspection history records for this specific row
|
||
$users = User::all();
|
||
$historyRecords = InspectionHistory::where('table_name', $table)
|
||
->where('record_id', $rowId)
|
||
->orderBy('created_at', 'DESC')
|
||
->get();
|
||
|
||
// Convert history records to JSON for DataGrid
|
||
$historyJson = json_encode($historyRecords);
|
||
// Convert users to a lookup object for DataGrid
|
||
$usersLookup = [];
|
||
foreach ($users as $user) {
|
||
$usersLookup[$user->id] = $user->name;
|
||
}
|
||
$usersJson = json_encode($usersLookup);
|
||
?>
|
||
|
||
<div class="card mb-3">
|
||
<div class="card-header">
|
||
<h5>
|
||
<span class="badge badge-primary">{{e2($table)}}</span> /
|
||
<span class="badge badge-secondary">Row #{{e2($rowId)}}</span>
|
||
<span class="badge badge-default">{{e2("Inspection History")}}</span>
|
||
</h5>
|
||
</div>
|
||
<div class="card-body">
|
||
<?php if ($inspectionHistoryFileNamePattern): ?>
|
||
<div class="alert alert-info mb-3">
|
||
<strong>{{e2("File Name Pattern")}}:</strong> {{$inspectionHistoryFileNamePattern}}
|
||
<br><small>{{e2("Files will be named using this pattern with column values from the row")}}</small>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<form id="inspectionHistoryForm" method="post" enctype="multipart/form-data">
|
||
@csrf
|
||
<input type="hidden" name="table_name" value="<?php echo $table; ?>">
|
||
<input type="hidden" name="record_id" value="<?php echo $rowId; ?>">
|
||
<?php if ($moduleSlug): ?>
|
||
<input type="hidden" name="module_slug" value="<?php echo $moduleSlug; ?>">
|
||
<?php endif; ?>
|
||
<?php if ($inspectionHistoryFileNamePattern): ?>
|
||
<input type="hidden" name="file_name_pattern" value="<?php echo htmlspecialchars($inspectionHistoryFileNamePattern); ?>">
|
||
<?php endif; ?>
|
||
|
||
<div class="form-group">
|
||
<label for="comment">{{e2("Comment")}} <span class="text-danger">*</span></label>
|
||
<textarea class="form-control" id="comment" name="comment" rows="3" required></textarea>
|
||
<small class="form-text text-muted">{{e2("Comment is required")}}</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label for="photo">{{e2("Photo")}} <span class="text-muted">({{e2("Optional")}})</span></label>
|
||
<input type="file" class="form-control-file" id="photo" name="photo[]" accept="image/*" multiple>
|
||
<small class="form-text text-muted">{{e2("Allowed formats: JPG, JPEG, PNG, GIF, WEBP. Max size: 20MB")}}</small>
|
||
<div id="photoPreview" class="mt-2" style="display: none; flex-wrap: wrap; gap: 10px;">
|
||
<!-- Preview images will be appended here -->
|
||
</div>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fa fa-upload"></i> {{e2("Upload Inspection History")}}
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card fullsize-grid">
|
||
<div class="card-header">
|
||
<h5>{{e2("Inspection History Records")}}</h5>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<!-- DevExtreme DataGrid container -->
|
||
<div id="inspectionHistoryDataGrid" style="width: 100%;"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
/* DataGrid için stil ayarları */
|
||
.dx-datagrid {
|
||
min-height: 500px;
|
||
}
|
||
|
||
/* Card gövdesi padding kaldırma */
|
||
.card-body.p-0 {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
/* Card kendisi için margin ayarları */
|
||
.card.fullsize-grid {
|
||
margin-bottom: 0 !important;
|
||
}
|
||
|
||
.photo-thumbnail {
|
||
max-width: 100px;
|
||
max-height: 100px;
|
||
cursor: pointer;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
padding: 5px;
|
||
}
|
||
|
||
.photo-thumbnail:hover {
|
||
border-color: #007bff;
|
||
}
|
||
|
||
/* Override Magnific Popup z-index to show above modals */
|
||
.mfp-bg, .mfp-wrap {
|
||
z-index: 2000 !important;
|
||
}
|
||
</style>
|
||
|
||
<script src="<?php echo asset('assets/admin/js/plugins/magnific-popup/jquery.magnific-popup.min.js'); ?>"></script>
|
||
<script>
|
||
$(function() {
|
||
// Users lookup table from server
|
||
const usersLookup = <?php echo $usersJson; ?>;
|
||
|
||
// Format user information
|
||
function formatUser(userId) {
|
||
return usersLookup[userId] || 'Unknown';
|
||
}
|
||
|
||
// Format file size
|
||
function formatFileSize(bytes) {
|
||
if (!bytes) return '';
|
||
if (bytes < 1024) return bytes + ' B';
|
||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
|
||
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
|
||
}
|
||
|
||
// Calculate grid height - responsive approach
|
||
function calculateGridHeight() {
|
||
return Math.max(500, window.innerHeight * 0.7);
|
||
}
|
||
|
||
// Initialize DataGrid with history records
|
||
const historyRecords = <?php echo $historyJson; ?>;
|
||
|
||
const dataGrid = $("#inspectionHistoryDataGrid").dxDataGrid({
|
||
dataSource: historyRecords,
|
||
keyExpr: "id",
|
||
showBorders: true,
|
||
height: calculateGridHeight(),
|
||
width: "100%",
|
||
selection: {
|
||
mode: "multiple",
|
||
showCheckBoxesMode: "always"
|
||
},
|
||
filterRow: {
|
||
visible: true,
|
||
applyFilter: "auto"
|
||
},
|
||
searchPanel: {
|
||
visible: true,
|
||
width: 240,
|
||
placeholder: "{{e2('Search...')}}"
|
||
},
|
||
headerFilter: {
|
||
visible: true
|
||
},
|
||
grouping: {
|
||
autoExpandAll: false
|
||
},
|
||
paging: {
|
||
pageSize: 10
|
||
},
|
||
pager: {
|
||
showPageSizeSelector: true,
|
||
allowedPageSizes: [5, 10, 20, 50],
|
||
showInfo: true
|
||
},
|
||
columnAutoWidth: true,
|
||
columns: [
|
||
{
|
||
dataField: "created_at",
|
||
caption: "{{e2('Date')}}",
|
||
dataType: "datetime",
|
||
format: "dd.MM.yyyy HH:mm",
|
||
sortOrder: "desc",
|
||
width: 160
|
||
},
|
||
{
|
||
dataField: "comment",
|
||
caption: "{{e2('Comment')}}",
|
||
dataType: "string",
|
||
width: 300
|
||
},
|
||
{
|
||
dataField: "file_path",
|
||
caption: "{{e2('Photo')}}",
|
||
dataType: "string",
|
||
width: 150,
|
||
cellTemplate: function(container, options) {
|
||
if (options.value) {
|
||
// Build correct photo URL - file_path is in format: documents/path/to/file.jpg
|
||
let photoUrl;
|
||
if (options.value.startsWith('storage/')) {
|
||
photoUrl = '<?php echo url(""); ?>' + '/' + options.value;
|
||
} else if (options.value.startsWith('documents/')) {
|
||
photoUrl = '<?php echo url("storage"); ?>' + '/' + options.value;
|
||
} else {
|
||
photoUrl = '<?php echo url("storage/documents"); ?>' + '/' + options.value;
|
||
}
|
||
|
||
// Create download URL
|
||
const downloadUrl = photoUrl;
|
||
|
||
// Create link for lightbox
|
||
const link = $('<a>')
|
||
.attr('href', photoUrl)
|
||
.addClass('image-popup-vertical-fit')
|
||
.attr('data-download-url', downloadUrl)
|
||
.attr('title', options.data.file_name || '{{e2("Photo")}}');
|
||
|
||
const img = $('<img>')
|
||
.attr('src', photoUrl)
|
||
.attr('alt', '{{e2("Photo")}}')
|
||
.addClass('photo-thumbnail');
|
||
|
||
link.append(img);
|
||
container.append(link);
|
||
} else {
|
||
container.append('<span class="text-muted">-</span>');
|
||
}
|
||
}
|
||
},
|
||
{
|
||
dataField: "file_name",
|
||
caption: "{{e2('File Name')}}",
|
||
dataType: "string",
|
||
width: 200
|
||
},
|
||
{
|
||
dataField: "file_size",
|
||
caption: "{{e2('File Size')}}",
|
||
dataType: "number",
|
||
width: 100,
|
||
cellTemplate: function(container, options) {
|
||
container.append(formatFileSize(options.value));
|
||
}
|
||
},
|
||
{
|
||
dataField: "user_id",
|
||
caption: "{{e2('User')}}",
|
||
lookup: {
|
||
dataSource: Object.entries(usersLookup).map(([id, name]) => ({ id: parseInt(id), name: name })),
|
||
displayExpr: "name",
|
||
valueExpr: "id"
|
||
},
|
||
width: 150
|
||
},
|
||
{
|
||
dataField: "ip_address",
|
||
caption: "{{e2('IP Address')}}",
|
||
dataType: "string",
|
||
width: 120
|
||
},
|
||
|
||
{
|
||
type: "buttons",
|
||
width: 120,
|
||
buttons: [
|
||
{
|
||
hint: "{{e2('Download')}}",
|
||
icon: "download",
|
||
visible: function(e) {
|
||
return !!e.row.data.file_path;
|
||
},
|
||
onClick: function(e) {
|
||
const filePath = e.row.data.file_path;
|
||
let downloadUrl;
|
||
if (filePath.startsWith('storage/')) {
|
||
downloadUrl = '<?php echo url(""); ?>' + '/' + filePath;
|
||
} else if (filePath.startsWith('documents/')) {
|
||
downloadUrl = '<?php echo url("storage"); ?>' + '/' + filePath;
|
||
} else {
|
||
downloadUrl = '<?php echo url("storage/documents"); ?>' + '/' + filePath;
|
||
}
|
||
|
||
const link = document.createElement('a');
|
||
link.href = downloadUrl;
|
||
link.download = e.row.data.file_name || 'download';
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
}
|
||
},
|
||
<?php if(isAuth($moduleSlug ?? $table, 'full_control')): ?>
|
||
{
|
||
hint: "{{e2('Delete')}}",
|
||
icon: "trash",
|
||
onClick: function(e) {
|
||
Swal.fire({
|
||
title: '{{e2("Are you sure?")}}',
|
||
text: '{{e2("You won't be able to revert this!")}}',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
confirmButtonColor: '#d33',
|
||
cancelButtonColor: '#3085d6',
|
||
confirmButtonText: '{{e2("Yes, delete it!")}}'
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
deleteInspectionHistory(e.row.data.id);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
<?php endif; ?>
|
||
]
|
||
}
|
||
],
|
||
export: {
|
||
enabled: true,
|
||
allowExportSelectedData: true
|
||
},
|
||
onToolbarPreparing: function(e) {
|
||
<?php if(isAuth($moduleSlug ?? $table, 'full_control')): ?>
|
||
e.toolbarOptions.items.unshift({
|
||
location: "after",
|
||
widget: "dxButton",
|
||
options: {
|
||
icon: "trash",
|
||
text: "{{e2('Delete Selected')}}",
|
||
type: "danger",
|
||
stylingMode: "text",
|
||
hint: "{{e2('Delete selected records')}}",
|
||
onClick: function() {
|
||
const selectedRows = dataGrid.getSelectedRowKeys();
|
||
if (selectedRows.length === 0) {
|
||
Swal.fire({
|
||
icon: 'info',
|
||
title: '{{e2("Info")}}',
|
||
text: '{{e2("Please select at least one record to delete.")}}'
|
||
});
|
||
return;
|
||
}
|
||
|
||
Swal.fire({
|
||
title: '{{e2("Are you sure?")}}',
|
||
text: '{{e2("You are about to delete")}} ' + selectedRows.length + ' {{e2("records. You cannot revert this!")}}',
|
||
icon: 'warning',
|
||
showCancelButton: true,
|
||
confirmButtonColor: '#d33',
|
||
cancelButtonColor: '#3085d6',
|
||
confirmButtonText: '{{e2("Yes, delete them!")}}'
|
||
}).then((result) => {
|
||
if (result.isConfirmed) {
|
||
bulkDeleteInspectionHistory(selectedRows);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
<?php endif; ?>
|
||
},
|
||
onContentReady: function(e) {
|
||
if(historyRecords.length === 0) {
|
||
e.component.option("noDataText", "{{e2('No inspection history found for this row')}}");
|
||
}
|
||
|
||
// Initialize Magnific Popup on cached/rendered elements
|
||
// We use delegation on the datagrid container
|
||
$('#inspectionHistoryDataGrid').magnificPopup({
|
||
delegate: 'a.image-popup-vertical-fit',
|
||
type: 'image',
|
||
closeOnContentClick: true,
|
||
mainClass: 'mfp-img-mobile',
|
||
image: {
|
||
verticalFit: true,
|
||
titleSrc: function(item) {
|
||
const title = item.el.attr('title');
|
||
const downloadUrl = item.el.attr('data-download-url');
|
||
return title + ' <a href="' + downloadUrl + '" download class="btn btn-xs btn-primary" style="padding: 2px 5px; font-size: 10px; color: white;"><i class="fa fa-download"></i> {{e2("Download")}}</a>';
|
||
}
|
||
},
|
||
gallery: {
|
||
enabled: true
|
||
}
|
||
});
|
||
}
|
||
}).dxDataGrid("instance");
|
||
|
||
// Window resize handler
|
||
$(window).on("resize", function() {
|
||
dataGrid.option("height", calculateGridHeight());
|
||
});
|
||
|
||
// Photo preview
|
||
$('#photo').on('change', function(e) {
|
||
const files = e.target.files;
|
||
const previewContainer = $('#photoPreview');
|
||
previewContainer.empty(); // Clear existing previews
|
||
|
||
if (files && files.length > 0) {
|
||
previewContainer.css('display', 'flex');
|
||
|
||
Array.from(files).forEach(file => {
|
||
if (!file.type.startsWith('image/')) return;
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
const img = $('<img>')
|
||
.attr('src', e.target.result)
|
||
.addClass('img-thumbnail')
|
||
.css({
|
||
'max-width': '150px',
|
||
'max-height': '150px',
|
||
'object-fit': 'cover'
|
||
});
|
||
previewContainer.append(img);
|
||
};
|
||
reader.readAsDataURL(file);
|
||
});
|
||
} else {
|
||
previewContainer.hide();
|
||
}
|
||
});
|
||
|
||
// Form submission handler
|
||
$('#inspectionHistoryForm').submit(function(e) {
|
||
e.preventDefault();
|
||
|
||
|
||
const formData = new FormData(this);
|
||
|
||
// Show loading state
|
||
Swal.fire({
|
||
title: '{{e2("Uploading...")}}',
|
||
text: '{{e2("Please wait while your files are being uploaded.")}}',
|
||
allowOutsideClick: false,
|
||
allowEscapeKey: false,
|
||
allowEnterKey: false,
|
||
showConfirmButton: false,
|
||
didOpen: () => {
|
||
Swal.showLoading();
|
||
}
|
||
});
|
||
|
||
$.ajax({
|
||
url: '<?php echo url('ajax/inspection-history/upload'); ?>',
|
||
type: 'POST',
|
||
data: formData,
|
||
processData: false,
|
||
contentType: false,
|
||
success: function(response) {
|
||
if(response.success) {
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: '{{e2("Success!")}}',
|
||
text: '{{e2("Inspection history has been saved successfully.")}}',
|
||
confirmButtonText: 'OK'
|
||
}).then((result) => {
|
||
refreshHistory();
|
||
// Clear form
|
||
$('#inspectionHistoryForm')[0].reset();
|
||
$('#photoPreview').hide();
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: response.message || '{{e2("Failed to save inspection history.")}}',
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
},
|
||
error: function(xhr) {
|
||
let errorMessage = '{{e2("An error occurred while processing your request.")}}';
|
||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||
errorMessage = xhr.responseJSON.message;
|
||
}
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: errorMessage,
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// Function to refresh history
|
||
function refreshHistory() {
|
||
$.ajax({
|
||
url: '<?php echo url('ajax/inspection-history/list'); ?>',
|
||
type: 'GET',
|
||
data: {
|
||
table_name: '<?php echo $table; ?>',
|
||
record_id: <?php echo $rowId; ?>
|
||
},
|
||
success: function(response) {
|
||
if (Array.isArray(response)) {
|
||
dataGrid.option('dataSource', response);
|
||
} else {
|
||
console.error('Invalid response format:', response);
|
||
}
|
||
},
|
||
error: function() {
|
||
Swal.fire('{{e2("Error")}}', '{{e2("Failed to refresh inspection history.")}}', 'error');
|
||
}
|
||
});
|
||
}
|
||
|
||
// Function to delete inspection history
|
||
function deleteInspectionHistory(id) {
|
||
$.ajax({
|
||
url: '<?php echo url('ajax/inspection-history/delete'); ?>/' + id,
|
||
type: 'DELETE',
|
||
data: {
|
||
_token: '<?php echo csrf_token(); ?>'
|
||
},
|
||
success: function(response) {
|
||
if(response.success) {
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: '{{e2("Deleted!")}}',
|
||
text: '{{e2("Inspection history has been deleted.")}}',
|
||
confirmButtonText: 'OK'
|
||
}).then(() => {
|
||
refreshHistory();
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: response.message || '{{e2("Failed to delete inspection history.")}}',
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
},
|
||
error: function(xhr) {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: '{{e2("An error occurred while deleting.")}}',
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
// Function to bulk delete inspection history
|
||
function bulkDeleteInspectionHistory(ids) {
|
||
$.ajax({
|
||
url: '<?php echo url('ajax/inspection-history/bulk-delete'); ?>',
|
||
type: 'POST',
|
||
data: {
|
||
_token: '<?php echo csrf_token(); ?>',
|
||
ids: ids
|
||
},
|
||
success: function(response) {
|
||
if(response.success) {
|
||
Swal.fire({
|
||
icon: 'success',
|
||
title: '{{e2("Deleted!")}}',
|
||
text: response.message || '{{e2("Selected records have been deleted.")}}',
|
||
confirmButtonText: 'OK'
|
||
}).then(() => {
|
||
refreshHistory();
|
||
// Clear selection
|
||
dataGrid.clearSelection();
|
||
});
|
||
} else {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: response.message || '{{e2("Failed to delete selected records.")}}',
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
},
|
||
error: function(xhr) {
|
||
Swal.fire({
|
||
icon: 'error',
|
||
title: '{{e2("Error!")}}',
|
||
text: '{{e2("An error occurred while deleting.")}}',
|
||
confirmButtonText: 'OK'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
});
|
||
</script>
|