8

Dashboard Template

Hello everyone!

After the    post, I decided to start a new one. This will make it easier to follow the changes.

New features have been added:

  • Sticky buttons. Thanks to   CSS hacks, I was able to make the buttons component sticky. Now the buttons always remain visible to the top of the page as we scroll down.
  • Scroll into View. By clicking a button the page automatically scrolls into the correspoding section. In the attached database press the first button "Accounts" to see it in action.

The combination of the above two features has the potential to transform the way we design our Dashboards. Like the one-page websites, we can present our content in sections on one long Dashboard.

In Safari works but I haven't check it in other browsers. Let me know if you find any bug.

What other functionalities would you like to see?

Leave here your suggestions, ideas or improvements!

Enjoy!

210 replies

null
    • Rafael Sanchis
    • Rafael_Sanchis
    • 2 mths ago
    • Reported - view

     

    Is there a way to enter data in batches, for example I need to enter 5 documents without the dialog box having to enter them one by one?

      • szormpas
      • 2 mths ago
      • Reported - view

        Hi,

      I've attached a version of the Form Widget without the confirmation message.

      To be honest, this was my original plan. Unfortunately, there's a small annoying bug. When you first save a record, the Ninox page refreshes, which closes the widget. This doesn't happen on subsequent saves.

      I've tried to fix it, but I haven't been able to. I think it's related to the Fetch API. I wish someone with JavaScript expertise could help us.

      • Rafael Sanchis
      • Rafael_Sanchis
      • 2 mths ago
      • Reported - view

       Hi Sotirios. That was exactly the idea. Thanks.

    • gold_cat
    • 1 mth ago
    • Reported - view

    Hi Sotirios, is it possible to implement real-time latitude and longitude positioning and write it into the Ninox “Location” field on the map?

      • szormpas
      • 1 mth ago
      • Reported - view

        Hi,

      you can add a map event on click to get the lat-long of the clicked point. Something like below:

      map.on('click', function(e) {
          alert(e.latlng);
      }); (empty)

      Then you can call the Ninox API to save the info.

      If I get a chance, I'll try to add this feature to one of the next versions.

      • gold_cat
      • 1 mth ago
      • Reported - view

        Hi friend, I have set the location to display in real time. How do I configure the Ninox API and write to the Ninox address? I look forward to your help.

      let _accounts := (select Accounts);
      let myGeoJson := {
              type: "FeatureCollection",
              features: _accounts.{
                  type: "Feature",
                  properties: {
                      id: raw('身份标识'),
                      name: "<b>" + 'First Name' + "  " + 'Last Name' + ",  " + age(DOB) + " yrs" + "</b>",
                      status: text(Status)
                  },
                  geometry: {
                      type: "Point",
                      coordinates: [longitude(Address), latitude(Address)]
                  }
              }
          };
      html("
      <link rel='stylesheet' href='https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'
           integrity='sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY='
           crossorigin=''/>
      
      <script src='https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'
           integrity='sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo='
           crossorigin=''></script>
      
      <div id='map' style='height: 100%; width: 100%;'></div>
      
      <script>
          setTimeout(function(){
              var alidadeSmooth = L.tileLayer('" +
      Settings['身份标识' = 5].Value +
      "', {
                    maxZoom: 20,
                    attribution: '" +
      Settings['身份标识' = 6].Value +
      "'
              });
      
              var stadiaOutdoors = L.tileLayer('" +
      Settings['身份标识' = 7].Value +
      "', {
                    maxZoom: 20,
                    attribution: '" +
      Settings['身份标识' = 6].Value +
      "'
              });
      
              var stamenToner = L.tileLayer('" +
      Settings['身份标识' = 8].Value +
      "', {
                    maxZoom: 20,
                    attribution: '" +
      Settings['身份标识' = 6].Value +
      "'
              });
      
              // Initialize map without setView
              var map = L.map('map').addLayer(alidadeSmooth);
      
              var markerOptions = {
                  radius: 20,
                  weight: 2,
                  opacity: 1,
                  fillOpacity: 0.5
              };
      
              function onEachFeature(feature, layer) {
                 layer.bindTooltip(feature.properties.name);
                 layer.on('click', function(e) {
                          ui.popupRecord(feature.properties.id);
                 });
              }
      
              var activeStatusLayer = L.geoJSON(" +
      myGeoJson +
      ", {
                  filter: function(feature, layer) {
                      return (feature.properties.status === 'Active');
                  },
                  pointToLayer: function (feature, latlng) {
                      return L.circleMarker(latlng, markerOptions);
                  },
                  style: {color: '#00FF00'},
                  onEachFeature: onEachFeature
              }).addTo(map);
      
              var suspendedStatusLayer = L.geoJSON(" +
      myGeoJson +
      ", {
                  filter: function(feature, layer) {
                      return (feature.properties.status === 'Suspended');
                  },
                  pointToLayer: function (feature, latlng) {
                      return L.circleMarker(latlng, markerOptions);
                  },
                  style: {color: '#005AFF'},
                  onEachFeature: onEachFeature
              }).addTo(map);
      
              var deactivatedStatusLayer = L.geoJSON(" +
      myGeoJson +
      ", {
                  filter: function(feature, layer) {
                      return (feature.properties.status === 'Deactivated');
                  },
                  pointToLayer: function (feature, latlng) {
                      return L.circleMarker(latlng, markerOptions);
                  },
                  style: {color: '#FF0000'},
                  onEachFeature: onEachFeature
              }).addTo(map);
      
              var baseMaps = {
                   'Alidade Smooth': alidadeSmooth,
                   'Stadia Outdoors': stadiaOutdoors,
                   'Stamen Toner': stamenToner,
              };
      
              var overlayMaps = {
                   '<span style=""color: #00FF00"">Active</span>': activeStatusLayer,
                   '<span style=""color: #005AFF"">Suspended</span>': suspendedStatusLayer,
                   '<span style=""color: #FF0000"">Deactivated</span>': deactivatedStatusLayer,
              };
              var layerControl = L.control.layers(baseMaps, overlayMaps).addTo(map);
      
              // Current location tracking
              var currentLocationMarker = null;
      
              if (navigator.geolocation) {
                  navigator.geolocation.watchPosition(
                      function(position) {
                          var lat = position.coords.latitude;
                          var lng = position.coords.longitude;
      
                          if (currentLocationMarker) {
                              map.removeLayer(currentLocationMarker);
                          }
      
                          currentLocationMarker = L.marker([lat, lng], {
                              icon: L.divIcon({
                                  className: 'current-location-marker',
                                  html: '<div style=""background-color: #1E90FF; border: 2px solid white; border-radius: 50%; width: 15px; height: 15px;""></div>'
                              })
                          }).addTo(map);
      
                          currentLocationMarker.bindPopup('当前位置<br>纬度: ' + lat.toFixed(6) + '<br>经度: ' + lng.toFixed(6))
                              .openPopup();
      
                          map.setView([lat, lng], 15);
                      },
                      function(error) {
                          console.error('获取位置失败:', error);
                          // Set default view if geolocation fails
                          map.setView([40.416775, -3.703790], 6);
                      },
                      {
                          enableHighAccuracy: true,
                          maximumAge: 0,
                          timeout: 5000
                      }
                  );
              } else {
                  console.error('浏览器不支持地理定位');
                  // Set default view if geolocation is not supported
                  map.setView([40.416775, -3.703790], 6);
              }
      
          },1000);
      </script>
      ")
      
    • szormpas
    • 1 mth ago
    • Reported - view

    Version 10.0

    View Widget

    Hello everyone,

     recently mentioned he'd like to be able to change the height of the rows in the View element on the fly.  and I agreed that it'd be easier to build from scratch than try to modify the Ninox code. 

    Building a View component from scratch is no easy task, so I decided to go with a ready-made library. 

    After doing a lot of research, I opted for Ag-Grid. They claim to be the best JavaScript grid in the world, and my experience has been great so far.

    My goal was to build a Widget that could completely replace Ninox's native View element and, if possible, have more functionality.

    I'm happy to present my View Widget with the following features:

    • Add a new record inline using the pinned top row.
    • Select and delete one or more records.
    • Modify any cell inline.
    • Row indexing.
    • Intuitive sort and filter capabilities (simply click on the column header to sort).
    • Pagination.
    • Extensive customization (try the Theme Builder).
    • Compact or normal mode.

    The all-new View Widget is basically a Ninox table inside a formula field, plus a whole lot more.

      what are your thoughts on the compact mode? Is it as compact as you'd like?

    Enjoy! 🙂

      • szormpas
      • 1 mth ago
      • Reported - view

        to get the most out of the View Widget, just paste your Ninox API key into the relevant field in the Settings table.

      • Fred
      • 1 mth ago
      • Reported - view

      Your contributions to the group are most welcome and very appreciated.

      • Kruna
      • 1 mth ago
      • Reported - view

       wow!!! Another great Feature you created - such an added value for ninox!

      I inserted my API key, but somehow it doesnt look like yours, but I think that maybe an CSS issue?

      I also cant Click the boxen on the left. Do you may have an idea why?

      However the buttons do work.

      thnx a lot!👍👍

      • szormpas
      • 1 mth ago
      • Reported - view

        Hi, strange, you have tried to copy paste the code inside your application or this is the original Dashboard v10.0 ?

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      Hi Sotirios on my Tablet Android work perfect. 👍 Whitout problem.

      • Kruna
      • 1 mth ago
      • Reported - view

       this is the Original Dashboard v10.0.

      Now that you mention, I checked the Versions and here we go - in browser everything is ok, but I am using Ninox desktop app.

      Is there a way to adapt it to the Desktop App?😅

      thnx

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      Is there any way to adjust the cell size? 

      • szormpas
      • 1 mth ago
      • Reported - view

        thanks for testing!

      • szormpas
      • 1 mth ago
      • Reported - view

      Hi, I'm just using the web app in the public cloud, so I'm not sure how to adapt it to the desktop app.

      • szormpas
      • 1 mth ago
      • Reported - view

       , what do you mean by "cell size"? Are you talking about adjusting the column width or the row height?

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       Hi The width Sotirios.

      I have a field with a lot carcteres.

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      Ready Sorry.

      • szormpas
      • 1 mth ago
      • Reported - view

        You can resize any column by dragging the top right corner. You can also set the width, minimum width and maximum width for each column. You can find out more here: https://www.ag-grid.com/angular-data-grid/column-sizing/

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      Thanks Sotirios great work, very helpful.

      Adapted.👍

      • szormpas
      • 1 mth ago
      • Reported - view

        Hi,

      Good to hear you found it useful. I have to admit that the Ag-Grid library has a steep learning curve, but once you get to grips with it, the possibilities are endless.

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      👍

       I have a Question, The Column 'Registration Date' on your DB don't Sort correctly.

      • szormpas
      • 1 mth ago
      • Reported - view

         , yes, I know about that. I haven't had a chance to implement it yet because I'm short on free time at the moment due to work commitments.

      Because the dates are read as "Date String," we need to add our own comparator to the code as detailed in the following blog post:

      https://blog.ag-grid.com/valueformatters-part-2/

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      Thanks Sotirios take your time.

      • Rafael Sanchis
      • Rafael_Sanchis
      • 1 mth ago
      • Reported - view

       

      I add this line and apparently it works I will keep checking. I try it on another DB. 😮 The ChartGPT help

      columnDefs: [
          { valueGetter: (params) => (params.data.Id ? params.node.rowIndex + 1 : null), maxWidth: 50, filter: false, editable: false, sortable: false, cellStyle: { color: '#566eb1' } },
          { field: 'Id', headerName: 'Id', cellDataType: 'number', sort: 'desc', hide: true },
          { field: 'Title', headerName: 'Title', cellDataType: 'text' },
          {
            field: 'Release Day',
            headerName: 'Release Day',
            cellDataType: 'dateString',
            comparator: (date1, date2) => {
                // Convierte las fechas en objetos Date antes de compararlas
                const d1 = new Date(date1.split('/').reverse().join('-'));
                const d2 = new Date(date2.split('/').reverse().join('-'));
                return d1 - d2;
            }
          },
          { field: 'Plataforma', headerName: 'Plataforma', cellDataType: 'text' },
          { field: 'Developer', headerName: 'Developer', cellDataType: 'text' },
          { field: 'MetaScore', headerName: 'MetaScore', cellDataType: 'number' },
          { field: 'UserScore', headerName: 'UserScore', cellDataType: 'number' }
      ],

Content aside

  • 8 Likes
  • 6 days agoLast active
  • 210Replies
  • 2530Views
  • 16 Following