|
@@ -0,0 +1,447 @@
|
|
|
|
|
+{% load static %}
|
|
|
|
|
+<!DOCTYPE html>
|
|
|
|
|
+<html lang="en">
|
|
|
|
|
+<head>
|
|
|
|
|
+<meta charset="UTF-8">
|
|
|
|
|
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
+<title>Column Mapping</title>
|
|
|
|
|
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
|
|
|
|
|
+<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
|
|
|
|
|
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource/source-sans-3@5.0.12/index.css"
|
|
|
|
|
+ integrity="sha256-tXJfXfp6Ewt1ilPzLDtQnJV4hclT9XuaZUKyUvmyr+Q=" crossorigin="anonymous">
|
|
|
|
|
+ <!--end::Fonts--><!--begin::Third Party Plugin(OverlayScrollbars)-->
|
|
|
|
|
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/styles/overlayscrollbars.min.css"
|
|
|
|
|
+ integrity="sha256-dSokZseQNT08wYEWiz5iLI8QPlKxG+TswNRD8k35cpg=" crossorigin="anonymous">
|
|
|
|
|
+ <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Third Party Plugin(Bootstrap Icons)-->
|
|
|
|
|
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.min.css"
|
|
|
|
|
+ integrity="sha256-Qsx5lrStHZyR9REqhUF8iQt73X06c8LGIUPzpOhwRrI=" crossorigin="anonymous">
|
|
|
|
|
+ <!--end::Third Party Plugin(Bootstrap Icons)--><!--begin::Required Plugin(AdminLTE)-->
|
|
|
|
|
+ <link rel="stylesheet" href="{% static './css/adminlte.css' %}"><!--end::Required Plugin(AdminLTE)--><!-- apexcharts -->
|
|
|
|
|
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/apexcharts@3.37.1/dist/apexcharts.css"
|
|
|
|
|
+ integrity="sha256-4MX+61mt9NVvvuPjUWdUdyfZfxSB1/Rf9WtqRHgG5S0=" crossorigin="anonymous">
|
|
|
|
|
+ <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
|
|
|
+ <link rel="stylesheet" href="{% static './css/select2-bootstrap4.min.css' %}">
|
|
|
|
|
+ <link rel="stylesheet" href="{% static './css/custom.css' %}">
|
|
|
|
|
+<style>
|
|
|
|
|
+ /* General Container and Layout */
|
|
|
|
|
+ body {
|
|
|
|
|
+ background-color: #f8f9fa; /* Light grey background */
|
|
|
|
|
+ }
|
|
|
|
|
+ .mapping-container {
|
|
|
|
|
+ max-width: 1200px;
|
|
|
|
|
+ margin: 40px auto;
|
|
|
|
|
+ background: #ffffff;
|
|
|
|
|
+ padding: 30px;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ box-shadow: 0 8px 25px rgba(0,0,0,0.08);
|
|
|
|
|
+ }
|
|
|
|
|
+ /* Uploaded Columns (Source) */
|
|
|
|
|
+ #uploaded-columns-wrapper {
|
|
|
|
|
+ border: 1px solid #dee2e6;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ min-height: 200px;
|
|
|
|
|
+ background-color: #e9ecef; /* Slightly darker background for source */
|
|
|
|
|
+ }
|
|
|
|
|
+ .drag-item {
|
|
|
|
|
+ padding: 8px 15px;
|
|
|
|
|
+ margin: 8px 0;
|
|
|
|
|
+ background: #007bff; /* Primary blue for drag items */
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ cursor: grab;
|
|
|
|
|
+ transition: transform 0.1s, box-shadow 0.1s;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drag-item:hover {
|
|
|
|
|
+ background: #0056b3;
|
|
|
|
|
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
|
|
|
|
+ }
|
|
|
|
|
+ /* System Columns (Destination/Drop Zones) */
|
|
|
|
|
+ .drop-zone-container {
|
|
|
|
|
+ padding: 15px;
|
|
|
|
|
+ border: 1px solid #dee2e6;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ min-height: 200px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone {
|
|
|
|
|
+ min-height: 45px;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ border: 2px dashed #adb5bd; /* Subtler dashed border */
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ background: #f1f3f5; /* Light grey background for drop zone */
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #495057;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ transition: background-color 0.2s, border-color 0.2s;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone.hover {
|
|
|
|
|
+ border-color: #28a745; /* Green on hover */
|
|
|
|
|
+ background: #e6ffed; /* Light green background on hover */
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone strong {
|
|
|
|
|
+ color: #dc3545; /* Red for the mapped column name */
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ padding: 4px 8px;
|
|
|
|
|
+ background-color: #ffe8e8;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone .system-label {
|
|
|
|
|
+ color: #495057;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone .mapped-content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone .mapped-content .reset-map {
|
|
|
|
|
+ margin-left: 10px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ color: #6c757d;
|
|
|
|
|
+ font-size: 1.2rem;
|
|
|
|
|
+ transition: color 0.2s;
|
|
|
|
|
+ }
|
|
|
|
|
+ .drop-zone .mapped-content .reset-map:hover {
|
|
|
|
|
+ color: #dc3545;
|
|
|
|
|
+ }
|
|
|
|
|
+ /* Preview Table */
|
|
|
|
|
+ .preview-table {
|
|
|
|
|
+ margin-top: 30px;
|
|
|
|
|
+ border-top: 2px solid #007bff;
|
|
|
|
|
+ padding-top: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .preview-table th {
|
|
|
|
|
+ background-color: #007bff;
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|
|
|
|
|
+</head>
|
|
|
|
|
+<!-- <body> -->
|
|
|
|
|
+
|
|
|
|
|
+<body class="layout-fixed sidebar-expand-lg sidebar-mini app-loaded sidebar-collapse">
|
|
|
|
|
+ <!--begin::App Wrapper-->
|
|
|
|
|
+ <div class="app-wrapper"> <!--begin::Header-->
|
|
|
|
|
+ {% include 'header.html' %}
|
|
|
|
|
+ {% include 'sidebar.html' %}
|
|
|
|
|
+ <main class="app-main"> <!--begin::App Content Header-->
|
|
|
|
|
+ <div class="app-content-header"> <!--begin::Container-->
|
|
|
|
|
+ <div class="container-fluid"> <!--begin::Row-->
|
|
|
|
|
+ <div class="row">
|
|
|
|
|
+ <div class="col-sm-6">
|
|
|
|
|
+ <h3 class="mb-0">⚙️ Attribute Extraction File Column Mapping</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-sm-6">
|
|
|
|
|
+ <ol class="breadcrumb float-sm-end">
|
|
|
|
|
+ <li class="breadcrumb-item"><a href="{% url 'file-upload' %}">Home</a></li>
|
|
|
|
|
+ <li class="breadcrumb-item active" aria-current="page"><a href="{% url 'content-scorecard' %}"></a>
|
|
|
|
|
+ ⚙️ Attribute Extraction File Column Mapping</a>
|
|
|
|
|
+ </li>
|
|
|
|
|
+ </ol>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div> <!--end::Row-->
|
|
|
|
|
+ </div> <!--end::Container-->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="app-content"> <!--begin::Container-->
|
|
|
|
|
+ <div class="container-fluid"> <!-- Info boxes -->
|
|
|
|
|
+ <div id="full-page-loader" style="display: none;">
|
|
|
|
|
+ <div class="loader-overlay">
|
|
|
|
|
+ <div class="spinner-border text-light" role="status">
|
|
|
|
|
+ <!-- <span class="sr-only">Loading...</span> -->
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="mapping-container">
|
|
|
|
|
+ <!-- <h2 class="text-center mb-4 text-primary">⚙️ Data Attribute Mapping Tool</h2> -->
|
|
|
|
|
+ <p class="text-center text-muted mb-4"><strong>Step 1:</strong> Upload your file. <strong>Step 2:</strong> Drag columns from the left to their corresponding system fields on the right. <strong>Step 3:</strong> Save Mapping.</p>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="mb-4 p-3 bg-light rounded border">
|
|
|
|
|
+ <label for="fileUpload" class="form-label fs-5 mb-2">📥 Upload Source File (CSV/Excel)</label>
|
|
|
|
|
+ <input type="file" class="form-control" id="fileUpload" accept=".csv, .xls, .xlsx">
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="row">
|
|
|
|
|
+ <div class="col-md-5">
|
|
|
|
|
+ <h5 class="mb-3 text-secondary">Source File Columns (Drag)</h5>
|
|
|
|
|
+ <div id="uploaded-columns-wrapper">
|
|
|
|
|
+ <div id="uploaded-columns" class="list-group">
|
|
|
|
|
+ <p class="text-center text-muted" id="upload-placeholder">Upload a file to see columns here.</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="col-md-7">
|
|
|
|
|
+ <h5 class="mb-3 text-success">Target System Attributes (Drop)</h5>
|
|
|
|
|
+ <div id="system-columns" class="drop-zone-container">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- <div class="preview-table">
|
|
|
|
|
+ <h5 class="text-primary">👀 Data Preview (First 5 Rows)</h5>
|
|
|
|
|
+ <div class="table-responsive">
|
|
|
|
|
+ <table class="table table-striped table-bordered">
|
|
|
|
|
+ <thead>
|
|
|
|
|
+ <tr id="preview-header">
|
|
|
|
|
+ <th colspan="100%" class="text-center text-white bg-secondary">No data loaded for preview.</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody id="preview-body">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td colspan="100%" class="text-center text-muted">A sample of your file data will appear here upon upload.</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div> -->
|
|
|
|
|
+
|
|
|
|
|
+ <div class="text-end mt-4 pt-3 border-top">
|
|
|
|
|
+ <button class="btn btn-outline-secondary me-2" id="resetBtn">↩️ Reset Mapping</button>
|
|
|
|
|
+ <button class="btn btn-success" id="saveBtn">✅ Save & Confirm Mapping</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ </div> <!--end::Container-->
|
|
|
|
|
+ </div> <!--end::App Content-->
|
|
|
|
|
+ </main> <!--end::App Main--> <!--begin::Footer-->
|
|
|
|
|
+ {% include 'footer.html' %}
|
|
|
|
|
+
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/overlayscrollbars@2.3.0/browser/overlayscrollbars.browser.es6.min.js"
|
|
|
|
|
+ integrity="sha256-H2VM7BKda+v2Z4+DRy69uknwxjyDRhszjXFhsL4gD3w=" crossorigin="anonymous"></script>
|
|
|
|
|
+ <!--end::Third Party Plugin(OverlayScrollbars)--><!--begin::Required Plugin(popperjs for Bootstrap 5)-->
|
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
|
|
|
|
|
+ integrity="sha256-whL0tQWoY1Ku1iskqPFvmZ+CHsvmRWx/PIoEvIeWh4I=" crossorigin="anonymous"></script>
|
|
|
|
|
+ <!--end::Required Plugin(popperjs for Bootstrap 5)--><!--begin::Required Plugin(Bootstrap 5)-->
|
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
|
|
|
|
|
+ integrity="sha256-YMa+wAM6QkVyz999odX7lPRxkoYAan8suedu4k2Zur8=" crossorigin="anonymous"></script>
|
|
|
|
|
+ <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
|
|
|
+ <!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
|
|
|
|
|
+ <script src="{% static './js/adminlte.js' %}"></script>
|
|
|
|
|
+ <!--end::Required Plugin(AdminLTE)--><!--begin::OverlayScrollbars Configure-->
|
|
|
|
|
+ <script>
|
|
|
|
|
+ const SELECTOR_SIDEBAR_WRAPPER = ".sidebar-wrapper";
|
|
|
|
|
+ const Default = {
|
|
|
|
|
+ scrollbarTheme: "os-theme-light",
|
|
|
|
|
+ scrollbarAutoHide: "leave",
|
|
|
|
|
+ scrollbarClickScroll: true,
|
|
|
|
|
+ };
|
|
|
|
|
+ document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
|
+ const sidebarWrapper = document.querySelector(SELECTOR_SIDEBAR_WRAPPER);
|
|
|
|
|
+ if (
|
|
|
|
|
+ sidebarWrapper &&
|
|
|
|
|
+ typeof OverlayScrollbarsGlobal?.OverlayScrollbars !== "undefined"
|
|
|
|
|
+ ) {
|
|
|
|
|
+ OverlayScrollbarsGlobal.OverlayScrollbars(sidebarWrapper, {
|
|
|
|
|
+ scrollbars: {
|
|
|
|
|
+ theme: Default.scrollbarTheme,
|
|
|
|
|
+ autoHide: Default.scrollbarAutoHide,
|
|
|
|
|
+ clickScroll: Default.scrollbarClickScroll,
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ </script>
|
|
|
|
|
+ <script>
|
|
|
|
|
+ const uploadedColumns = [];
|
|
|
|
|
+ const systemColumns = ["PRODUCT NAME", "ITEM ID", "PRODUCT TYPE", "Product Short Description", "Product Long Description", "image_path"];
|
|
|
|
|
+ let globalHeaders = []; // To store headers for preview
|
|
|
|
|
+
|
|
|
|
|
+ $(document).ready(function() {
|
|
|
|
|
+ // --- Initial Drop Zone Setup ---
|
|
|
|
|
+ systemColumns.forEach(col => {
|
|
|
|
|
+ $("#system-columns").append(`
|
|
|
|
|
+ <div class="drop-zone" data-system="${col}">
|
|
|
|
|
+ <span class="system-label">${col}</span>
|
|
|
|
|
+ <span class="mapped-content">
|
|
|
|
|
+ <span class="text-muted">No column mapped</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ `);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // --- File Upload Handler (Supports CSV/Excel) ---
|
|
|
|
|
+ $("#fileUpload").on("change", function(e) {
|
|
|
|
|
+ const file = e.target.files[0];
|
|
|
|
|
+ if (!file) return;
|
|
|
|
|
+
|
|
|
|
|
+ const reader = new FileReader();
|
|
|
|
|
+ reader.onload = function(event) {
|
|
|
|
|
+ let jsonData;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data = new Uint8Array(event.target.result);
|
|
|
|
|
+ const workbook = XLSX.read(data, { type: "array" });
|
|
|
|
|
+ const sheetName = workbook.SheetNames[0];
|
|
|
|
|
+ const sheet = workbook.Sheets[sheetName];
|
|
|
|
|
+ jsonData = XLSX.utils.sheet_to_json(sheet, { header: 1 });
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ alert("Error reading file. Ensure it is a valid CSV or Excel file.");
|
|
|
|
|
+ console.error(err);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const headers = jsonData[0];
|
|
|
|
|
+ const sampleRows = jsonData.slice(1, 6);
|
|
|
|
|
+ globalHeaders = headers;
|
|
|
|
|
+
|
|
|
|
|
+ // Populate uploaded columns
|
|
|
|
|
+ $("#uploaded-columns").empty();
|
|
|
|
|
+ headers.forEach(col => {
|
|
|
|
|
+ if (col && String(col).trim() !== "") {
|
|
|
|
|
+ $("#uploaded-columns").append(`<div class="drag-item" draggable="true" data-col="${col}">${col}</div>`);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Reset mappings and remove placeholder text
|
|
|
|
|
+ $("#upload-placeholder").hide();
|
|
|
|
|
+ $("#resetBtn").click(); // Reset any previous mappings upon new file upload
|
|
|
|
|
+
|
|
|
|
|
+ // Populate preview table
|
|
|
|
|
+ $("#preview-header").html(headers.map(h => `<th scope="col">${h}</th>`).join(''));
|
|
|
|
|
+ $("#preview-body").empty();
|
|
|
|
|
+ if (sampleRows.length > 0) {
|
|
|
|
|
+ sampleRows.forEach(row => {
|
|
|
|
|
+ let tr = "<tr>";
|
|
|
|
|
+ headers.forEach((h, i) => tr += `<td>${row[i] || "-"}</td>`);
|
|
|
|
|
+ tr += "</tr>";
|
|
|
|
|
+ $("#preview-body").append(tr);
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $("#preview-body").html('<tr><td colspan="100%" class="text-center text-warning">File uploaded, but no data rows found.</td></tr>');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Re-enable drag-and-drop for new items
|
|
|
|
|
+ enableDragDrop();
|
|
|
|
|
+ };
|
|
|
|
|
+ reader.readAsArrayBuffer(file);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // --- Drag and Drop Logic ---
|
|
|
|
|
+ function enableDragDrop() {
|
|
|
|
|
+ // Drag Start
|
|
|
|
|
+ $(".drag-item").off("dragstart").on("dragstart", function(e) {
|
|
|
|
|
+ e.originalEvent.dataTransfer.setData("text/plain", $(this).data("col"));
|
|
|
|
|
+ $(this).addClass("dragging-active");
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Drag End (optional, for cleanup)
|
|
|
|
|
+ $(".drag-item").off("dragend").on("dragend", function() {
|
|
|
|
|
+ $(this).removeClass("dragging-active");
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Drop Zone Events
|
|
|
|
|
+ $(".drop-zone").off("dragover").on("dragover", function(e) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ $(this).addClass("hover");
|
|
|
|
|
+ e.originalEvent.dataTransfer.dropEffect = "move";
|
|
|
|
|
+ }).off("dragleave").on("dragleave", function() {
|
|
|
|
|
+ $(this).removeClass("hover");
|
|
|
|
|
+ }).off("drop").on("drop", function(e) {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ $(this).removeClass("hover");
|
|
|
|
|
+ const col = e.originalEvent.dataTransfer.getData("text/plain");
|
|
|
|
|
+ const systemCol = $(this).data("system");
|
|
|
|
|
+
|
|
|
|
|
+ // 1. ONE-TO-ONE MAPPING CHECK: Is this uploaded column already mapped?
|
|
|
|
|
+ let isAlreadyMapped = false;
|
|
|
|
|
+ $(".drop-zone").each(function() {
|
|
|
|
|
+ if ($(this).data("mapped") === col) {
|
|
|
|
|
+ isAlreadyMapped = true;
|
|
|
|
|
+ return false; // Exit the loop early
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (isAlreadyMapped) {
|
|
|
|
|
+ alert(`The column "${col}" is already mapped to another system attribute. Please unmap it first.`);
|
|
|
|
|
+ return; // Stop the drop action
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. UNMAP PREVIOUS COLUMN (if the drop zone already has a value)
|
|
|
|
|
+ const previousMappedCol = $(this).data("mapped");
|
|
|
|
|
+ if (previousMappedCol) {
|
|
|
|
|
+ // Show the previously mapped column item again in the source list
|
|
|
|
|
+ $(`.drag-item[data-col="${previousMappedCol}"]`).show();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. APPLY NEW MAPPING
|
|
|
|
|
+ $(this).find('.mapped-content').html(`
|
|
|
|
|
+ <strong>${col}</strong>
|
|
|
|
|
+ <span class="reset-map" title="Unmap Column" data-system="${systemCol}">✖️</span>
|
|
|
|
|
+ `);
|
|
|
|
|
+ $(this).data("mapped", col);
|
|
|
|
|
+ $(this).removeClass("border-danger").addClass("border-success");
|
|
|
|
|
+
|
|
|
|
|
+ // 4. Hide the newly mapped column item from the uploaded list
|
|
|
|
|
+ $(`.drag-item[data-col="${col}"]`).hide();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // --- Reset Single Mapping ---
|
|
|
|
|
+ $(document).on("click", ".reset-map", function() {
|
|
|
|
|
+ const dropZone = $(this).closest(".drop-zone");
|
|
|
|
|
+ const mappedCol = dropZone.data("mapped");
|
|
|
|
|
+
|
|
|
|
|
+ // Reset drop zone appearance and data
|
|
|
|
|
+ dropZone.find('.mapped-content').html(`<span class="text-muted">No column mapped</span>`);
|
|
|
|
|
+ dropZone.removeData("mapped");
|
|
|
|
|
+ dropZone.removeClass("border-success border-danger");
|
|
|
|
|
+
|
|
|
|
|
+ // Show the uploaded column item again
|
|
|
|
|
+ if (mappedCol) {
|
|
|
|
|
+ $(`.drag-item[data-col="${mappedCol}"]`).show();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // --- Reset All Button ---
|
|
|
|
|
+ $("#resetBtn").click(function() {
|
|
|
|
|
+ $(".drop-zone").each(function() {
|
|
|
|
|
+ $(this).find('.mapped-content').html(`<span class="text-muted">No column mapped</span>`);
|
|
|
|
|
+ $(this).removeData("mapped");
|
|
|
|
|
+ $(this).removeClass("border-success border-danger");
|
|
|
|
|
+ });
|
|
|
|
|
+ // Show all uploaded columns again
|
|
|
|
|
+ $(".drag-item").show();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // --- Save Button with Validation ---
|
|
|
|
|
+ $("#saveBtn").click(function() {
|
|
|
|
|
+ let mapping = {};
|
|
|
|
|
+ let missingMaps = [];
|
|
|
|
|
+ let allMapped = true;
|
|
|
|
|
+
|
|
|
|
|
+ $(".drop-zone").each(function() {
|
|
|
|
|
+ const systemCol = $(this).data("system");
|
|
|
|
|
+ const mappedCol = $(this).data("mapped");
|
|
|
|
|
+
|
|
|
|
|
+ mapping[systemCol] = mappedCol || null;
|
|
|
|
|
+
|
|
|
|
|
+ if (!mappedCol) {
|
|
|
|
|
+ allMapped = false;
|
|
|
|
|
+ missingMaps.push(systemCol);
|
|
|
|
|
+ $(this).addClass("border-danger");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ $(this).removeClass("border-danger");
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!allMapped) {
|
|
|
|
|
+ alert(`Please map all system columns before saving. Missing: ${missingMaps.join(', ')}`);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log("Final Mapping:", mapping);
|
|
|
|
|
+ alert(`Mapping saved successfully! System Columns Mapped: ${Object.keys(mapping).length}`);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Initial call to enable drag/drop
|
|
|
|
|
+ enableDragDrop();
|
|
|
|
|
+ });
|
|
|
|
|
+ </script>
|
|
|
|
|
+</body>
|
|
|
|
|
+</html>
|