Jelajahi Sumber

Filters added for datatable

Devansh Vakharia 1 bulan lalu
induk
melakukan
2e5fc88101

+ 106 - 14
src/app/contact/contact.component.css

@@ -63,24 +63,116 @@ th{background: #ffe8cb !important;}
     box-sizing: border-box !important;
 }
 
-/* Style for Excel filter row */
-#filterRow input.col-filter {
-    width: 100%;
-    padding: 4px 6px;
-    border: 1px solid #dad8d8;
-    border-radius: 3px;
+/* Prevent thead from expanding when filter dropdown is added */
+#example thead th {
+    max-width: 150px;              /* Control column width */
+    white-space: nowrap;           /* Prevent stretching */
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+/* Ensure dropdown fits inside column and doesn't stretch it */
+.column-filter {
+    max-width: 130px;              /* must be less than 150px above */
+    width: 100%;                   /* fit inside cell */
+    height: 28px;                  /* same height as your table cells */
+    padding: 2px 4px;
     font-size: 13px;
     box-sizing: border-box;
 }
 
-/* On focus */
-#filterRow input.col-filter:focus {
-    border: 1px solid #a7a7a7 !important;
-    outline: none;
+.sort-btn {
+    padding: 0 4px;
+    background: none;
+    border: none;
+    cursor: pointer;
+    font-size: 14px;
+}
+.sort-btn:hover {
+    color: #007bff;
+}
+
+.column-filter-container {
+  position: relative;
+  display: inline-block;
+}
+
+.filter-dropdown {
+  position: absolute;
+  background: white;
+  border: 1px solid #cacdd1;
+  z-index: 100;
+  padding: 5px;
+  max-height: 200px;
+  overflow-y: auto;
+  min-width: 120px;
+  box-shadow: 0px 2px 5px rgba(0,0,0,0.2);
+}
+
+.filter-dropdown div {
+  margin-bottom: 2px;
+}
+
+.filter-dropdown input {
+  margin-right: 5px;
+}
+
+/* .sort-btn {
+  padding: 0 4px;
+  background: none;
+  border: none;
+  cursor: pointer;
+  font-size: 12px;
+}
+.sort-btn:hover {
+  color: #007bff;
+} */
+
+.floating-filter-dropdown {
+  position: absolute;
+  background: white;
+  border: 1px solid #cacdd1;
+  box-shadow: 0px 2px 5px rgba(0,0,0,0.2);
+  padding: 5px;
+  max-height: 50px;      /* Limit dropdown height */
+  overflow-y: auto;       /* Add scrollbar if content exceeds max-height */
+  min-width: 50px;
+  display: none;
+  z-index: 1000;
+}
+
+.floating-filter-dropdown div {
+  margin-bottom: 2px;
+}
+
+.floating-filter-dropdown input {
+  margin-right: 5px;
+}
+
+.floating-filter-dropdown label {
+  transition: color 0.2s, opacity 0.2s;
+}
+
+/* Filtered out (unchecked) items appear semi-transparent */
+.floating-filter-dropdown input:not(:checked) + label {
+  opacity: 0.7;
+}
+
+.filter-btn {
+  font-size: 12px;
+  padding: 2px 5px;
+  cursor: pointer;
+  margin-left: 4px;
+}
+
+.sort-btn {
+  padding: 0 4px;
+  background: none;
+  border: none;
+  cursor: pointer;
+  font-size: 12px;
 }
 
-/* Keep height consistent with table */
-#filterRow th {
-    background: #fff !important;
-    padding: 3px !important;
+.sort-btn:hover, .filter-btn:hover {
+  color: #007bff;
 }

+ 1 - 1
src/app/contact/contact.component.html

@@ -311,7 +311,7 @@
                     </select>
 </div>
             
-                    <table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger"
+                    <table id="example" datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger"
                         class="dataTable row-border hover display" cellspacing="0" width="100%"> 
 
                         <thead>

+ 140 - 68
src/app/contact/contact.component.ts

@@ -70,85 +70,157 @@ selectedCustomerFilter: string = ''; // Holds the name selected in the new filte
 
   constructor(private commonservice: CommonFunctionService, private router: Router, private fb: FormBuilder) {
 
-    this.dtOptions = {
-      pagingType: 'full_numbers',
-      pageLength: 15,
-      processing: true,
-      order: [],
-      lengthMenu: [15, 25, 35],
-
-      ordering: false,
-      dom: 'Blfrtip',
-
-      // Use initComplete to manipulate the DOM after DataTables is ready
-    initComplete: function (settings: any, json: any) {
-        
-        // Get the DataTables API instance
-        const api = new $.fn.dataTable.Api(settings);
+ this.dtOptions = {
+  pagingType: 'full_numbers',
+  pageLength: 15,
+  processing: true,
+  order: [],
+  lengthMenu: [15, 25, 35],
+  ordering: true,
+  dom: 'Blfrtip',
+
+initComplete: function(settings: any, json: any) {
+  const api = new $.fn.dataTable.Api(settings);
+
+    /* Existing filter relocation */
   const filterContainer = document.querySelector('.dataTables_filter');
   const dropdown = document.getElementById('customer-filter-container');
 
   if (filterContainer && dropdown) {
-    dropdown.style.display = 'inline-block';  // Align nicely
-    filterContainer.insertBefore(dropdown, filterContainer.firstChild); 
+    dropdown.style.display = 'inline-block';
+    filterContainer.insertBefore(dropdown, filterContainer.firstChild);
   }
 
+  // Add filter row for spacing (optional)
+  const tableHeader = $(settings.nTable).find('thead');
+  if (!tableHeader.find('.filter-row').length) {
+    tableHeader.append('<tr class="filter-row"></tr>');
+  }
+  const filterRow = tableHeader.find('.filter-row');
+  // var skipFilterCols = [2, 3, 4, 5];
+  const skipFilterCols = ['First Name', 'Last Name', 'Email ID', 'Mobile Number', 'Alternate Mobile Number'];
+
+  console.log(skipFilterCols);
+
+  api.columns().every(function () {
+    const column = this;
+    const colIndex = column.index();
+    const header = $(column.header());
+    const colName = header.text().trim(); // get column name text
+
+    // --- SORT BUTTON (always added) ---
+    // const sortBtn = $('<button class="sort-btn"></button>');
+    const sortBtn = $('<button class="sort-btn" style="margin-left:4px;border:none;background:none;cursor:pointer;font-size:12px;"></button>');
+
+    let sortState = 0;
+    sortBtn.on('click', function () {
+      // if (sortState === 0) { api.order([colIndex,'asc']).draw(); sortBtn.text('▲'); sortState = 1; }
+      // else if (sortState === 1) { api.order([colIndex,'desc']).draw(); sortBtn.text('▼'); sortState = 2; }
+      // else { api.order([]).draw(); sortBtn.text('⬍'); sortState = 0; }
+    });
+    header.append(sortBtn);
+
+    // --- Skip filter if column is in skip list ---
+    // if (skipFilterCols.includes(colIndex) || header.hasClass('no-export')) return;
+    // Skip filter if column is in skipFilterCols or has 'no-export' class
+    if (skipFilterCols.includes(colName) || header.hasClass('no-export')) return;
+
+    // --- FILTER BUTTON AND DROPDOWN ---
+    const filterBtn = $('<button type="button" class="filter-btn">⬇</button>').appendTo(header);
+    const dropdown = $('<div style="opacity: 1; background: white; overflow-y: auto; max-height: 400px;" class="floating-filter-dropdown"></div>').appendTo('body').hide();
+
+    // Unique sorted values
+    const uniqueValues = column.data()
+      .unique()
+      .toArray()
+      .filter(d => d != null && d !== '')
+      .map(d => d.toString())
+      .sort((a,b) => a.localeCompare(b));
+
+    // "All" checkbox
+    const allCheckbox = $(`
+      <div>
+        <input type="checkbox" id="all_${colIndex}" value="all" checked>
+        <label for="all_${colIndex}">All</label>
+      </div>
+    `);
+    dropdown.append(allCheckbox);
+
+    // Individual checkboxes
+    uniqueValues.forEach(val => {
+      const id = `filter_${colIndex}_${val.replace(/\s+/g,'_')}`;
+      const checkbox = $(`
+        <div>
+          <input type="checkbox" id="${id}" value="${val}" checked>
+          <label for="${id}">${val}</label>
+        </div>
+      `);
+      dropdown.append(checkbox);
+    });
 
-    //     // 1. Find the search input element created by DataTables
-    //     const searchInput = $(api.table().container()).find('.dataTables_filter');
-
-    //     // 2. Find your placeholder div
-    //     const customFilterHtml = $('#CustomerFilterContainer').html(); 
-
-    //     // 3. Prepend your filter container's content (the dropdown) to the search div
-    //     // You'll need to create the container HTML (step 3)
-    //     // For now, let's use the standard search input parent.
-        
-    //     // Find the main filter div (which contains the label and input)
-    //     const filterWrapper = $(api.table().container()).find('.dataTables_filter').parent();
-        
-    //     // 4. Insert the new dropdown HTML before the search box wrapper
-    //     // NOTE: Replace the placeholder ID with the actual HTML of your filter dropdown
-    //     // This assumes you rendered the filter HTML somewhere else and are moving it.
-        
-    //     // Since it's complex to move Angular components with jQuery, a simpler client-side filter is usually done by:
-        
-    //     // Get the original placeholder element
-    //     const placeholder = $('#custom-filter-placeholder'); 
-        
-    //     // Get the search wrapper
-    //     const searchWrapper = $(api.table().container()).find('.dataTables_filter');
-        
-    //     // Move the HTML content from the placeholder into the search wrapper.
-    //     // You'll need to render the Angular select box outside and move it, OR 
-    //     // use an Angular ViewChild to access the native element.
-        
-    //     // --- RECOMMENDED ANGULAR APPROACH ---
-    //     // Access the native search input and inject the filter HTML *before* it.
-    //     // This requires access to the 'this' context, so use an arrow function.
-    },
+    // --- Checkbox handling ---
+    dropdown.on('change', 'input[type=checkbox]', function () {
+      const allChecked = allCheckbox.find('input').prop('checked');
 
-      buttons: [
-        {
-          extend: 'print',
-          title: 'Contact Details Reports',
-          exportOptions: {
-            columns: ':not(.no-export)'
-          }
-        },
-        {
-          extend: 'excelHtml5',
-          title: 'Contact Details Reports',
-          exportOptions: {
-            columns: ':not(.no-export)'
-          }
-        }
+      if ($(this).val() === 'all') {
+        const checked = $(this).prop('checked');
+        dropdown.find('input[type=checkbox]').prop('checked', checked);
+      } else {
+        const total = dropdown.find('input[type=checkbox]').not('[value="all"]').length;
+        const checkedCount = dropdown.find('input[type=checkbox]:checked').not('[value="all"]').length;
+        allCheckbox.find('input').prop('checked', checkedCount === total);
+      }
 
-      ]
-    }
+      const selectedValues: string[] = dropdown.find('input[type=checkbox]:checked').not('[value="all"]').map((i, el) => $(el).val()?.toString() || '').get();
+      if (selectedValues.length === 0 || allChecked) column.search('').draw();
+      else {
+        // const regex = selectedValues.map(v => $.fn.dataTable.util.escapeRegex(v)).join('|');
+        const regex = selectedValues.map(v => '^' + $.fn.dataTable.util.escapeRegex(v) + '$').join('|');
+        column.search(regex, true, false).draw();
+      }
+    });
 
+    // --- Show/hide dropdown ---
+    filterBtn.on('click', function (e) {
+      e.stopPropagation();
+      const offset = filterBtn.offset();
+      dropdown.css({
+        top: offset.top + filterBtn.outerHeight() + 2,
+        left: offset.left,
+        position: 'absolute',
+        zIndex: 1000
+      }).toggle();
+    });
 
+// Prevent clicks inside the dropdown from closing it
+dropdown.on('click', function (e) {
+  e.stopPropagation(); // stops the document click from firing
+});
 
+// Only hide dropdown when clicking outside
+$(document).on('click', function () {
+  dropdown.hide();
+});
+  });
+}
+,
+  buttons: [
+    {
+      extend: 'print',
+      title: 'Contact Details Reports',
+      exportOptions: {
+        columns: ':not(.no-export)'
+      }
+    },
+    {
+      extend: 'excelHtml5',
+      title: 'Contact Details Reports',
+      exportOptions: {
+        columns: ':not(.no-export)'
+      }
+    }
+  ]
+};
 
     console.log("in contact");
     console.log(this.router.url);
@@ -734,7 +806,7 @@ rerender(): void {
 filterByCustomerName(): void {
     if (this.dtElement && this.dtElement.dtInstance) {
         this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
-            
+            console.log(this.role);
             const filterValue = this.selectedCustomerFilter; 
             const customerNameColumnIndex = this.role === 'admin' ? 1 : 0; // Index 1 is 'Customer Name' based on your template