657 lines
32 KiB
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
|