Files
citrus-cms/resources/views/admin-ajax/inspection-history.blade.php
2026-04-28 21:15:09 +03:00

614 lines
23 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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 + ' &nbsp; <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>