| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 |
- {% 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" id="target-system-wrapper" style="display: none;">
- <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();
- $("#target-system-wrapper").show();
- };
- 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();
- // **NEW LINES: Hide system columns and reset uploaded list**
- // $("#target-system-wrapper").hide();
- // $("#uploaded-columns").empty().append(`<p class="text-center text-muted" id="upload-placeholder">Upload a file to see columns here.</p>`);
- // $("#upload-placeholder").show();
- // $("#fileUpload").val(''); // Clear file input value
- });
- // --- 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>
|