Rapport-Laravel/resources/views/contact/import.blade.php

657 lines
32 KiB
PHP

@extends('layouts.vertical', ['page_title' => 'Add & Edit Products'])
@section('css')
<link href="{{ asset('assets/libs/select2/select2.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('assets/libs/multiselect/multiselect.min.css') }}" rel="stylesheet" type="text/css" />
<link href="{{ asset('assets/libs/dropify/dropify.min.css') }}" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-grid.css" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/ag-grid-community/styles/ag-theme-alpine.css" rel="stylesheet"
type="text/css" />
<link href="{{ asset('assets/libs/sweetalert2/sweetalert2.min.css') }}" rel="stylesheet" type="text/css" />
@endsection
@section('content')
<div class="container-fluid">
@if (session()->has('message'))
<div class="alert alert-success mt-3">
{{ session()->get('message') }}
</div>
@endif
<div class="row mt-4 justify-content-center">
<div class="col-lg-11"
style="
margin-top: 10px;
padding-bottom: 90px;
">
<div class="card shadow rounded">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Import {{ $title }}s</h4>
</div>
<form id="step-one-form" enctype="multipart/form-data" {{-- style="display:none;" --}}>
<meta name="csrf-token" content="{{ csrf_token() }}">
<input type="hidden" name="operationid" id="operationid" value="">
<div class="card-body">
<div class="row">
<div class="col-md-6 mb-4">
<label class="form-label fw-bold">Upload CSV File</label>
<input type="file" name="file_path" class="dropify"
data-allowed-file-extensions="csv" required />
<small class="text-muted d-block mt-2">Max File Size: 1GB</small>
</div>
<div class="col-md-6 mb-4">
<div class="bg-light p-4 rounded h-100" id="step-one-summary">
<h5 class="text-primary mb-3 fw-bold">File Upload Summary</h5>
<div class="list-group" id="step-one-list">
<div class="text-muted">No file uploaded yet.</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer text-end">
<a href="{{ $base_url }}" class="btn btn-outline-dark me-2">Back</a>
<button type="submit" class="btn btn-success">Next</button>
</div>
</form>
<form id="step-two-form" enctype="multipart/form-data" style="display:none;">
<meta name="csrf-token" content="{{ csrf_token() }}">
<div class="card-body">
<div class="row">
<div class="col-md-12 mb-4">
<label class="form-label fw-bold">Confirm Step 2</label>
<div class="bg-light p-4 rounded h-100" id="step-two-summary">
<h5 class="text-primary mb-3 fw-bold">Position Code Summary</h5>
<div class="list-group" id="step-two-list">
<div class="text-muted">Waiting for position code validation...</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer text-end">
<button type="submit" class="btn btn-success">Back</button>
</div>
</form>
<!-- Grid Container -->
<div id="step-three-grid" class="ag-theme-alpine"
style="display: none; height: 400px; padding: 1rem; border-radius: 8px;">
</div>
<div id="step-three-actions" style="display:none;" class="text-end mt-3 px-3 pb-1">
<button id="next-btn" type="button" class="btn btn-success btn-lg shadow-sm px-4"
onclick="showFileActions()" disabled>
Next
</button>
</div>
<div class="row" id="fileOperationBlock" style="display: none;">
<div class="card-body">
<h5 class="card-title mb-3 text-primary">Select File Operations</h5>
<form id="submitfileoperation">
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="fileoperation" id="insertfile">
<label class="form-check-label" for="insertfile">Insert File</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" name="fileoperation" id="updatefile">
<label class="form-check-label" for="updatefile">Update File</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" type="checkbox" name="fileoperation"
id="deletefile">
<label class="form-check-label" for="deletefile">Delete File</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.ag-theme-alpine {
height: 500px;
width: 100%;
}
.step-icon {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 14px;
}
.list-group-item {
background: transparent;
border: none;
padding: 8px 0;
}
.swal-medium-modal {
max-width: 450px !important;
padding: 1rem !important;
}
</style>
@endsection
@section('script')
<script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.noStyle.js"></script>
<script src="{{ asset('assets/libs/sweetalert2/sweetalert2.min.js') }}"></script>
<script src="{{ asset('assets/libs/select2/select2.min.js') }}"></script>
<script src="{{ asset('assets/libs/multiselect/multiselect.min.js') }}"></script>
<script src="{{ asset('assets/libs/dropify/dropify.min.js') }}"></script>
<script>
$(document).ready(function() {
$('.dropify').dropify({
messages: {
'default': 'Drag and drop a file here or click',
'replace': 'Drag and drop or click to replace',
'remove': 'Remove',
'error': 'Ooops, something wrong appended.'
},
error: {
'fileSize': 'The file size is too big (1GB max).'
}
});
$('#step-two-form').on('submit', function(e) {
e.preventDefault();
location.reload();
});
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
let stepTwo = false;
$('#step-one-form').submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
$("#step-one-list").html(
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-primary text-white me-3"> <span class="spinner-border spinner-border-sm text-white"></span> </div> <div>Uploading file...</div> </div> `
);
$.ajax({
url: '/contact/import',
method: "POST",
data: formData,
contentType: false,
processData: false,
success: function(response) {
let stepsHtml = '';
if (response.status === "success") {
let item = response.data?.item;
let id = item.id;
let validationStatus = item?.validation_status;
let missingHeaders = item?.error?.[0]?.required_missing_header ||
[];
let emptyHeader = item?.error?.[0]?.empty_header || [];
stepsHtml +=
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-success text-white me-3"> <i class="bi bi-check-lg"></i> </div> <div>File uploaded successfully</div> </div> `;
if (validationStatus === "fail") {
if (missingHeaders.length > 0) {
missingHeaders.forEach(function(header) {
stepsHtml +=
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-danger text-white me-3"> <i class="bi bi-x-lg"></i> </div> <div>Missing Required Header: <strong>${header}</strong></div> </div> `;
});
}
if (emptyHeader > 0) {
stepsHtml +=
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-warning text-white me-3"> <i class="bi bi-exclamation-triangle-fill"></i> </div> <div><strong>${emptyHeader}</strong> Empty Header(s) Found</div> </div> `;
}
}
else if(validationStatus == "error"){
Swal.fire({
title: 'Error',
text: response.data?.item?.process_error || "Internal Server Error",
allowOutsideClick: false,
allowEscapeKey: false,
didOpen: () => {
Swal.showLoading();
}
});
}
else {
$('#operationid').val(id);
stepsHtml +=
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-success text-white me-3"> <i class="bi bi-check-lg"></i> </div> <div>File validated successfully</div> </div> <div class="text-end mt-3"> <button id="proceed-to-step-two" type="button" class="btn btn-primary">Proceed to Step 2</button> </div> `;
stepTwo = true;
}
} else {
stepsHtml =
`<div class="text-danger"> ${response.message ?? 'Something went wrong.'}</div>`;
}
$("#step-one-list").html(stepsHtml);
$('#proceed-to-step-two').on('click', function() {
submitStepTwo();
});
},
error: function(xhr) {
let errMessage = xhr.responseJSON?.message ?? 'Something went wrong.';
$("#step-one-list").html(
`<div class="text-danger"> ${errMessage}</div>`
);
}
});
});
function submitStepTwo() {
if (!stepTwo) return;
$('#step-one-form').hide();
$('#step-two-form').show();
$("#step_two_cards #step-two-list").html(
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-primary text-white me-3"> <span class="spinner-border spinner-border-sm text-white"></span> </div> <div>Processing Step 2...</div> </div> `
);
$.ajax({
url: '/contact/importfilegetbyid',
data: {
id: $('#operationid').val()
},
method: "GET",
success: function(response) {
if (response.status === "success") {
const errors = response.data?.item?.error ?? [];
let validationStatus = response.data?.item?.validation_status;
let is_validate = response.data?.item?.is_validate;
let row_count = response.data?.item?.row_count
console.log('getone eror', errors)
const positionErrors = errors.filter(err =>
err.toLowerCase().includes("position") || err.toLowerCase()
.includes("positioncode")
);
if (validationStatus == "error") {
Swal.fire({
title: 'Error',
text: response.data?.item?.process_error || "Internal Server Error",
icon: 'error',
allowOutsideClick: false,
allowEscapeKey: false,
confirmButtonText: 'OK',
}).then((result) => {
if (result.isConfirmed) {
window.location.reload();
}
});
return;
}
if (positionErrors.length > 0) {
let stepTwoHtml =
` <div class="list-group-item d-flex align-items-center px-0"> <div class="step-icon bg-danger text-white me-3"> <i class="bi bi-x-circle"></i> </div> <div class="fw-bold text-danger">Position Code Errors:</div> </div> `;
positionErrors.forEach(msg => {
stepTwoHtml +=
` <div class="list-group-item px-0 ps-5"> <div class="text-danger"> ${msg}</div> </div> `;
});
$("#step-two-list").html(stepTwoHtml);
} else if (errors.length > 0) {
loadStepThreeGrid();
}
else if (is_validate == true && row_count > 0) {
$('#step-two-form').hide();
showFileActions(false);
}
else {
setTimeout(() => {
submitStepTwo();
}, 5000);
}
} else {
$("#step-two-list").html(
`<div class="text-danger"> ${response.message ?? 'Something went wrong in Step 2.'}</div>`
);
}
},
error: function(xhr) {
const errMessage = xhr.responseJSON?.message ??
'Something went wrong in Step 2.';
$("#step-two-list").html(
`<div class="text-danger"> ${errMessage}</div>`
);
}
});
}
function loadStepThreeGrid() {
$.ajax({
url: "/contact/showfileimporterrors",
method: "GET",
data: {
id: $('#operationid').val()
},
success: function(response) {
if (response.status === "success") {
$('#step-two-form').hide();
$('#step-three-grid').show();
$('#step-three-actions').show();
const columnDefs = [...response.data.column];
columnDefs.push({
headerName: "Actions",
field: "refresh",
cellRenderer: function(params) {
const button = document.createElement('button');
button.innerHTML = 'Refresh';
button.classList.add('btn', 'btn-sm', 'btn-outline-primary');
button.onclick = function() {
const rowId = params.node.id;
const originalRow = originalRows.get(rowId) || { ...params.data };
const payload = {
new_data: params.data,
original_data: originalRow
};
$.ajax({
url: '/contact/fileimportupdate',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(payload),
success: function(updateResponse) {
const updatedRow = updateResponse.data?.row?.[0];
if (updatedRow) {
params.node.setData(updatedRow);
let allRowsValid = true;
params.api.forEachNode(function(rowNode) {
if (rowNode.data.error_list && rowNode.data.error_list.length > 0) {
allRowsValid = false;
}
});
$('#next-btn').prop('disabled', !allRowsValid);
originalRows.set(rowId, { ...updatedRow });
}
},
error: function(xhr) {
console.log('Error refreshing row: ' + (xhr.responseJSON?.message ?? 'Unknown error'));
}
});
};
return button;
},
editable: false,
filter: false,
sortable: false,
width: 120
});
const rowData = response.data.row;
const originalRows = new Map();
const gridOptions = {
columnDefs,
rowData,
editType: 'fullRow',
singleClickEdit: true,
stopEditingWhenCellsLoseFocus: true,
defaultColDef: {
resizable: true,
sortable: true,
filter: true,
wrapText: true,
autoHeight: true,
editable: true
},
domLayout: 'normal',
onCellEditingStarted: function(event) {
const rowId = event.node.id;
originalRows.set(rowId, {
...event.data
});
},
onCellValueChanged: function(event) {
const rowId = event.node.id;
const updatedRow = event.data;
const originalRowBeforeEdit = originalRows.get(rowId);
if (event.oldValue !== event.newValue) {
const payload = {
new_data: updatedRow,
original_data: originalRowBeforeEdit
};
$.ajax({
url: '/contact/fileimportupdate',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify(payload),
success: function(updateResponse) {
const updatedRowFromServer =
updateResponse.data?.row?.[0];
if (updatedRowFromServer) {
event.node.setData(
updatedRowFromServer);
let allRowsValid = true;
event.api.forEachNode(function(
rowNode) {
if (rowNode.data
.error_list &&
rowNode.data
.error_list
.length > 0) {
allRowsValid =
false;
}
});
$('#next-btn').prop('disabled',
!allRowsValid);
}
},
error: function(xhr) {
console.log('Error updating row: ' +
(xhr
.responseJSON
?.message ??
'Unknown error'));
}
});
}
originalRows.delete(rowId);
}
};
const gridDiv = document.getElementById('step-three-grid');
if (gridDiv) {
agGrid.createGrid(gridDiv, gridOptions);
} else {
console.error("Grid div not found");
}
} else {
console.log("Step 3 API failed: " + response.message);
}
},
error: function(xhr) {
console.log("Error loading Step 3 data: " + (xhr.responseJSON?.message ??
"Unknown error"));
}
});
}
$('#submitfileoperation').submit(function(e) {
e.preventDefault();
const export_id = $('#operationid').val();
const payload = {
id: export_id,
is_insert: $('#insertfile').is(':checked'),
is_update: $('#updatefile').is(':checked'),
is_disable: $('#deletefile').is(':checked')
};
$.ajax({
url: '/contact/importactions',
method: "POST",
contentType: 'application/json',
data: JSON.stringify(payload),
success: function(response) {
if (response.status === "success") {
console.log("File status updated successfully.");
showFileActions(true)
} else {
console.log("Failed to update file status:", response.message);
}
},
error: function(xhr) {
const message = xhr.responseJSON?.message ?? "Unknown error";
console.log("Error occurred while updating file status:", message);
}
});
});
});
</script>
<script>
function showFileActions(showAlert) {
$('#fileOperationBlock').show();
$('#step-three-grid').hide();
$('#step-three-actions').hide();
const export_id = $('#operationid').val();
const payload = {
is_validate: true,
file_status: "processing",
id: export_id
};
$.ajax({
url: '/contact/importactions',
method: "POST",
contentType: 'application/json',
data: JSON.stringify(payload),
success: function(response) {
if (response.status === "success") {
console.log("File status updated successfully.");
if (showAlert) {
Swal.fire({
title: 'Processing File...',
text: 'Please wait while the file importing is being completed.',
allowOutsideClick: false,
allowEscapeKey: false,
didOpen: () => {
Swal.showLoading();
}
});
pollRowCheck(export_id);
}
} else {
console.log("Failed to update file status:", response.message);
}
},
error: function(xhr) {
const message = xhr.responseJSON?.message ?? "Unknown error";
console.log("Error occurred while updating file status:", message);
}
});
}
let pollInterval;
function pollRowCheck(export_id) {
pollInterval = setInterval(() => {
const formData = new FormData();
formData.append('step_two', export_id);
$.ajax({
url: '/contact/importfilegetbyid',
data: {
id: $('#operationid').val()
},
method: "GET",
success: function(response) {
const fileStatus = response?.data?.item?.file_status;
const validationStatus = response?.data?.item?.validation_status;
const rowCount = response?.data?.item?.row_count;
const completedCount = response?.data?.item?.completed_count;
const createdCount = response?.data?.item?.created_count;
const updatedCount = response?.data?.item?.updated_count;
const deactiveCount = response?.data?.item?.deactive_count;
if (fileStatus === "completed" && validationStatus === "pass") {
clearInterval(pollInterval);
Swal.fire({
icon: 'success',
title: 'Operation Successful',
html: `
<div style="font-size: 14px; text-align: center; line-height: 1.5;">
<strong>Import completed successfully.</strong><br><br>
<strong>Total Rows:</strong> ${rowCount || 0}<br>
<strong>Completed:</strong> ${completedCount || 0}<br>
<strong>Created:</strong> ${createdCount || 0}<br>
<strong>Updated:</strong> ${updatedCount || 0}<br>
<strong>Deactivated:</strong> ${deactiveCount || 0}
</div>
`,
customClass: {
popup: 'swal-medium-modal'
},
confirmButtonText: 'OK'
}).then(() => {
location.reload();
});
} else if (fileStatus === "processing" && (validationStatus === "fail" || validationStatus === "error")) {
clearInterval(pollInterval);
Swal.fire({
icon: 'error',
title: 'Operation Failed',
text: 'Error while importing file.',
confirmButtonText: 'OK'
}).then(() => {
location.reload();
});
}
},
error: function(xhr) {
console.log("Polling error:", xhr.responseJSON?.message || "Unknown error");
}
});
}, 4000); // Every 4 seconds
}
</script>
@endsection