|
- <h2>Import Jurisdiction</h2>
-
- <% Flash().ShowErrorsIfPresent %>
- <% Flash().ShowSuccessIfPresent %>
-
- <%= HTML.FormTag("Jurisdiction", "ImportPost", Array("_P","1"), Array("enctype","multipart/form-data","id","jurisdiction-import-form")) %>
- <%= HTML.Hidden("nonce", HTMLSecurity.GetAntiCSRFToken("JurisdictionImportForm")) %>
- <hr />
- <div>
- <p>Upload the BRM permit workbook as an Excel file. Row 1 is ignored, row 2 must contain the expected headers, and jurisdictions are updated or inserted using the JCode inside the <code>Jurisdiction</code> column.</p>
- </div>
-
- <div class="form-group">
- <div class="row">
- <div class="col-md-5">
- <div class="form-group">
- <label for="filename">Select XLSX File</label>
- <input type="file" id="filename" name="filename" accept=".xlsx" required class="form-control">
- </div>
- </div>
- </div>
- <p></p>
- <button type="submit" id="import-submit" class="btn btn-primary"><i class="glyphicon glyphicon-upload"></i> Start Import</button>
- <small class="form-text text-muted">Accepted format: .xlsx | Max size: 10 MB</small>
- </div>
-
- </form>
-
- <div id="import-feedback" class="panel panel-default" style="display:none; margin-top:20px;">
- <div class="panel-heading">
- <strong>Import Progress</strong>
- </div>
- <div class="panel-body">
- <p id="import-status">Waiting to start.</p>
- <div class="progress">
- <div id="import-progress-bar" class="progress-bar progress-bar-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" style="width:0%;">
- 0%
- </div>
- </div>
- <div class="row">
- <div class="col-sm-2"><strong>Total</strong><div id="summary-total">0</div></div>
- <div class="col-sm-2"><strong>Processed</strong><div id="summary-processed">0</div></div>
- <div class="col-sm-2"><strong>Updated</strong><div id="summary-updated">0</div></div>
- <div class="col-sm-2"><strong>Inserted</strong><div id="summary-inserted">0</div></div>
- <div class="col-sm-2"><strong>Invalid</strong><div id="summary-invalid">0</div></div>
- <div class="col-sm-2"><strong>Failed</strong><div id="summary-failed">0</div></div>
- </div>
- <div class="row" style="margin-top:12px;">
- <div class="col-sm-2"><strong>Duplicates</strong><div id="summary-duplicates">0</div></div>
- </div>
- <div id="import-error-panel" class="alert alert-warning" style="display:none; margin-top:15px;">
- <strong>Row Issues</strong>
- <ul id="import-error-list" style="margin-top:10px; margin-bottom:0;"></ul>
- </div>
- </div>
- </div>
-
- <div id="import-message" class="alert" style="display:none; margin-top:20px;"></div>
-
- <script type="text/javascript">
- (function () {
- var form = document.getElementById('jurisdiction-import-form');
- var fileInput = document.getElementById('filename');
- var submitButton = document.getElementById('import-submit');
- var nonceField = form.querySelector('input[name="nonce"]');
- var feedbackPanel = document.getElementById('import-feedback');
- var messageBox = document.getElementById('import-message');
- var statusText = document.getElementById('import-status');
- var progressBar = document.getElementById('import-progress-bar');
- var pollTimer = null;
-
- var summaryFields = {
- totalRows: document.getElementById('summary-total'),
- processedRows: document.getElementById('summary-processed'),
- updatedCount: document.getElementById('summary-updated'),
- insertedCount: document.getElementById('summary-inserted'),
- invalidCount: document.getElementById('summary-invalid'),
- failedCount: document.getElementById('summary-failed'),
- duplicateCount: document.getElementById('summary-duplicates')
- };
-
- var errorPanel = document.getElementById('import-error-panel');
- var errorList = document.getElementById('import-error-list');
- var pollUrlBase = '<%= Routes.UrlTo("Jurisdiction", "ImportProgress", Array("_P","1")) %>';
-
- function setMessage(cssClass, message) {
- messageBox.className = 'alert ' + cssClass;
- messageBox.textContent = message;
- messageBox.style.display = 'block';
- }
-
- function clearMessage() {
- messageBox.style.display = 'none';
- messageBox.textContent = '';
- }
-
- function updateSummary(data) {
- summaryFields.totalRows.textContent = data.totalRows || 0;
- summaryFields.processedRows.textContent = data.processedRows || 0;
- summaryFields.updatedCount.textContent = data.updatedCount || 0;
- summaryFields.insertedCount.textContent = data.insertedCount || 0;
- summaryFields.invalidCount.textContent = data.invalidCount || 0;
- summaryFields.failedCount.textContent = data.failedCount || 0;
- summaryFields.duplicateCount.textContent = data.duplicateCount || 0;
- }
-
- function refreshNonce(data) {
- if (nonceField && data && data.nextNonce) {
- nonceField.value = data.nextNonce;
- }
- }
-
- function updateProgress(data) {
- var percent = data.percentComplete || 0;
- progressBar.style.width = percent + '%';
- progressBar.setAttribute('aria-valuenow', percent);
- progressBar.textContent = percent + '%';
- statusText.textContent = data.statusMessage || 'Processing import.';
- updateSummary(data);
- renderErrors(data.errors || []);
-
- if (data.phase === 'complete') {
- progressBar.className = 'progress-bar';
- setMessage('alert-success', 'Jurisdiction import complete.');
- } else if (data.phase === 'error') {
- progressBar.className = 'progress-bar';
- setMessage('alert-danger', data.statusMessage || 'Import failed.');
- }
- }
-
- function renderErrors(errors) {
- errorList.innerHTML = '';
- if (!errors.length) {
- errorPanel.style.display = 'none';
- return;
- }
-
- for (var i = 0; i < errors.length; i += 1) {
- var item = document.createElement('li');
- item.textContent = errors[i];
- errorList.appendChild(item);
- }
- errorPanel.style.display = 'block';
- }
-
- function stopPolling() {
- if (pollTimer) {
- window.clearTimeout(pollTimer);
- pollTimer = null;
- }
- submitButton.disabled = false;
- }
-
- function sendJsonRequest(url, options, onSuccess, onError) {
- var xhr = new XMLHttpRequest();
- xhr.open(options.method || 'GET', url, true);
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
-
- xhr.onreadystatechange = function () {
- var data;
- if (xhr.readyState !== 4) {
- return;
- }
-
- try {
- data = JSON.parse(xhr.responseText || '{}');
- } catch (parseError) {
- onError('The server returned an invalid JSON response.');
- return;
- }
-
- if (xhr.status < 200 || xhr.status >= 300 || !data.ok) {
- refreshNonce(data);
- onError((data && data.message) || 'The request failed.');
- return;
- }
-
- refreshNonce(data);
- onSuccess(data);
- };
-
- xhr.onerror = function () {
- onError('The request failed.');
- };
-
- xhr.send(options.body || null);
- }
-
- function pollProgress(token) {
- sendJsonRequest(
- pollUrlBase + '&token=' + encodeURIComponent(token),
- { method: 'GET' },
- function (data) {
- updateProgress(data);
- if (data.phase === 'complete' || data.phase === 'error') {
- stopPolling();
- return;
- }
-
- pollTimer = window.setTimeout(function () {
- pollProgress(token);
- }, 1200);
- },
- function (error) {
- stopPolling();
- setMessage('alert-danger', error || 'Import progress request failed.');
- }
- );
- }
-
- form.addEventListener('submit', function (event) {
- event.preventDefault();
- clearMessage();
- renderErrors([]);
-
- if (!fileInput.files.length) {
- setMessage('alert-danger', 'Select an .xlsx file before starting the import.');
- return;
- }
-
- submitButton.disabled = true;
- feedbackPanel.style.display = 'block';
- statusText.textContent = 'Uploading workbook...';
- progressBar.className = 'progress-bar progress-bar-striped active';
- updateSummary({
- totalRows: 0,
- processedRows: 0,
- updatedCount: 0,
- insertedCount: 0,
- invalidCount: 0,
- failedCount: 0,
- duplicateCount: 0,
- percentComplete: 0
- });
- updateProgress({ percentComplete: 0, statusMessage: 'Uploading workbook...', errors: [] });
-
- var formData = new FormData(form);
- sendJsonRequest(
- form.action,
- { method: 'POST', body: formData },
- function (data) {
- updateProgress(data);
- pollProgress(data.token);
- },
- function (error) {
- stopPolling();
- setMessage('alert-danger', error || 'Import request failed.');
- }
- );
- });
- })();
- </script>
|