<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Coaste</title>
    <description>The latest articles on DEV Community by Coaste (@coaste).</description>
    <link>https://dev.to/coaste</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1281803%2F26547782-a1a6-4076-ae49-f5d74804192f.jpg</url>
      <title>DEV Community: Coaste</title>
      <link>https://dev.to/coaste</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coaste"/>
    <language>en</language>
    <item>
      <title>Using Chart.js the Right Way: UI &amp; UX Strategies for Better Data Visualization</title>
      <dc:creator>Coaste</dc:creator>
      <pubDate>Tue, 29 Sep 2026 13:50:06 +0000</pubDate>
      <link>https://dev.to/coaste/using-chartjs-the-right-way-ui-ux-strategies-for-better-data-visualization-4hia</link>
      <guid>https://dev.to/coaste/using-chartjs-the-right-way-ui-ux-strategies-for-better-data-visualization-4hia</guid>
      <description>&lt;p&gt;Charts are everywhere in modern web applications.&lt;/p&gt;

&lt;p&gt;Dashboards, admin panels, analytics platforms, financial systems, inventory applications, SaaS products — almost every data-driven interface eventually needs some form of visualization.&lt;/p&gt;

&lt;p&gt;But adding a chart is easy.&lt;/p&gt;

&lt;p&gt;Designing a chart that actually helps the user understand the data is much harder.&lt;/p&gt;

&lt;p&gt;In this article, I'll show how to use Chart.js effectively while applying practical UI and UX principles to make dashboards more readable, useful, and engaging.&lt;/p&gt;

&lt;p&gt;The goal isn't to create the most colorful dashboard possible.&lt;/p&gt;

&lt;p&gt;The goal is to make the data easier to understand.&lt;/p&gt;

&lt;p&gt;What Makes a Good Chart?&lt;/p&gt;

&lt;p&gt;A chart should answer a question.&lt;/p&gt;

&lt;p&gt;Before writing any JavaScript, ask:&lt;/p&gt;

&lt;p&gt;What should the user understand after looking at this chart?&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;❌ "Here are our sales."&lt;/p&gt;

&lt;p&gt;✅ "Which products generated the most revenue?"&lt;/p&gt;

&lt;p&gt;❌ "Here is our inventory."&lt;/p&gt;

&lt;p&gt;✅ "Which warehouses currently hold the most inventory?"&lt;/p&gt;

&lt;p&gt;❌ "Here are our monthly numbers."&lt;/p&gt;

&lt;p&gt;✅ "Is our revenue increasing or decreasing?"&lt;/p&gt;

&lt;p&gt;This small change in thinking has a huge impact on the final interface.&lt;/p&gt;

&lt;p&gt;A chart should have a purpose.&lt;/p&gt;

&lt;p&gt;📊 Choosing the Right Chart&lt;/p&gt;

&lt;p&gt;One of the most common mistakes in dashboards is choosing a chart because it looks good instead of choosing it because it communicates the data well.&lt;/p&gt;

&lt;p&gt;A simple rule is:&lt;/p&gt;

&lt;p&gt;Goal    Recommended Chart&lt;br&gt;
Compare categories  Bar&lt;br&gt;
Compare rankings    Horizontal bar&lt;br&gt;
Show a trend    Line&lt;br&gt;
Show composition    Doughnut / Pie&lt;br&gt;
Show relationship   Scatter&lt;br&gt;
Show progress   Doughnut / Progress&lt;br&gt;
Compare multiple metrics    Bar / Line&lt;/p&gt;

&lt;p&gt;For example, if you want to compare sales between products:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const config = {&lt;br&gt;
    type: "bar",&lt;br&gt;
    data: {&lt;br&gt;
        labels: ["Product A", "Product B", "Product C"],&lt;br&gt;
        datasets: [{&lt;br&gt;
            label: "Sales",&lt;br&gt;
            data: [120, 180, 95]&lt;br&gt;
        }]&lt;br&gt;
    }&lt;br&gt;
};&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A bar chart immediately communicates the relative size of each category.&lt;/p&gt;

&lt;p&gt;🧠 UI/UX Rule #1: Don't Visualize Everything&lt;/p&gt;

&lt;p&gt;A dashboard doesn't need 15 charts.&lt;/p&gt;

&lt;p&gt;More charts don't necessarily mean more information.&lt;/p&gt;

&lt;p&gt;They can actually make the interface harder to understand.&lt;/p&gt;

&lt;p&gt;Consider this:&lt;/p&gt;

&lt;p&gt;┌──────────────┐ ┌──────────────┐ ┌──────────────┐&lt;br&gt;
│ Sales        │ │ Customers    │ │ Orders       │&lt;br&gt;
│              │ │              │ │              │&lt;br&gt;
│    CHART     │ │    CHART     │ │    CHART     │&lt;br&gt;
└──────────────┘ └──────────────┘ └──────────────┘&lt;/p&gt;

&lt;p&gt;┌─────────────────────────────────────────────────┐&lt;br&gt;
│                  ANOTHER CHART                  │&lt;br&gt;
└─────────────────────────────────────────────────┘&lt;/p&gt;

&lt;p&gt;Everything is competing for attention.&lt;/p&gt;

&lt;p&gt;Instead, establish a hierarchy:&lt;/p&gt;

&lt;p&gt;┌─────────────────────────────────────────────────┐&lt;br&gt;
│                  KEY METRIC                     │&lt;br&gt;
└─────────────────────────────────────────────────┘&lt;/p&gt;

&lt;p&gt;┌────────────────────────┐ ┌──────────────────────┐&lt;br&gt;
│ Main Chart             │ │ Supporting Metric    │&lt;br&gt;
│                        │ │                      │&lt;br&gt;
└────────────────────────┘ └──────────────────────┘&lt;/p&gt;

&lt;p&gt;The interface should communicate:&lt;/p&gt;

&lt;p&gt;Primary information → Secondary information → Details&lt;/p&gt;

&lt;p&gt;🎯 UI/UX Rule #2: Give Every Chart a Clear Title&lt;/p&gt;

&lt;p&gt;Avoid titles such as:&lt;/p&gt;

&lt;p&gt;Sales&lt;br&gt;
Data&lt;br&gt;
Statistics&lt;br&gt;
Information&lt;/p&gt;

&lt;p&gt;These titles don't provide much context.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;p&gt;Monthly Revenue&lt;br&gt;
Revenue by Product&lt;br&gt;
Inventory by Warehouse&lt;br&gt;
Assets by Cost Center&lt;br&gt;
Orders by Category&lt;/p&gt;

&lt;p&gt;A good title tells the user what they're looking at without requiring additional explanation.&lt;/p&gt;

&lt;p&gt;Chart.js makes this easy:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const config = {&lt;br&gt;
    type: "bar",&lt;br&gt;
    data,&lt;br&gt;
    options: {&lt;br&gt;
        plugins: {&lt;br&gt;
            title: {&lt;br&gt;
                display: true,&lt;br&gt;
                text: "Revenue by Product"&lt;br&gt;
            }&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
};&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The title should communicate the question being answered.&lt;/p&gt;

&lt;p&gt;🧩 UI/UX Rule #3: Use Cards to Create Context&lt;/p&gt;

&lt;p&gt;A chart by itself can lack context.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Revenue

  ████████
  ████████
  ███████████
  ███████████
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The user may still ask:&lt;/p&gt;

&lt;p&gt;Is that good?&lt;/p&gt;

&lt;p&gt;A summary card can provide immediate context:&lt;/p&gt;

&lt;p&gt;┌──────────────────────────┐&lt;br&gt;
│ Total Revenue            │&lt;br&gt;
│                          │&lt;br&gt;
│ $248,420                 │&lt;br&gt;
│                          │&lt;br&gt;
│ ↑ 12.4% vs last month    │&lt;br&gt;
└──────────────────────────┘&lt;/p&gt;

&lt;p&gt;Then the chart can explain where that number came from.&lt;/p&gt;

&lt;p&gt;This creates a useful hierarchy:&lt;/p&gt;

&lt;p&gt;KPI&lt;br&gt;
 ↓&lt;br&gt;
Trend&lt;br&gt;
 ↓&lt;br&gt;
Breakdown&lt;br&gt;
 ↓&lt;br&gt;
Details&lt;/p&gt;

&lt;p&gt;Charts should support the metrics, not compete with them.&lt;/p&gt;

&lt;p&gt;📈 UI/UX Rule #4: Use Line Charts for Trends&lt;/p&gt;

&lt;p&gt;When the user needs to understand change over time, a line chart is usually more appropriate.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const config = {&lt;br&gt;
    type: "line",&lt;br&gt;
    data: {&lt;br&gt;
        labels: [&lt;br&gt;
            "January",&lt;br&gt;
            "February",&lt;br&gt;
            "March",&lt;br&gt;
            "April"&lt;br&gt;
        ],&lt;br&gt;
        datasets: [{&lt;br&gt;
            label: "Revenue",&lt;br&gt;
            data: [&lt;br&gt;
                12000,&lt;br&gt;
                14500,&lt;br&gt;
                13800,&lt;br&gt;
                17100&lt;br&gt;
            ]&lt;br&gt;
        }]&lt;br&gt;
    }&lt;br&gt;
};&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The important UX principle here is that the order of the points matters.&lt;/p&gt;

&lt;p&gt;A line naturally communicates progression.&lt;/p&gt;

&lt;p&gt;This makes it useful for:&lt;/p&gt;

&lt;p&gt;Revenue&lt;br&gt;
Users&lt;br&gt;
Orders&lt;br&gt;
Inventory&lt;br&gt;
Performance&lt;br&gt;
Traffic&lt;br&gt;
Growth&lt;br&gt;
📊 UI/UX Rule #5: Use Bar Charts for Comparisons&lt;/p&gt;

&lt;p&gt;Bar charts are extremely effective when comparing categories.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const config = {&lt;br&gt;
    type: "bar",&lt;br&gt;
    data: {&lt;br&gt;
        labels: [&lt;br&gt;
            "Warehouse A",&lt;br&gt;
            "Warehouse B",&lt;br&gt;
            "Warehouse C",&lt;br&gt;
            "Warehouse D"&lt;br&gt;
        ],&lt;br&gt;
        datasets: [{&lt;br&gt;
            label: "Inventory",&lt;br&gt;
            data: [820, 540, 390, 210]&lt;br&gt;
        }]&lt;br&gt;
    }&lt;br&gt;
};&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This immediately answers:&lt;/p&gt;

&lt;p&gt;Which warehouse has the most inventory?&lt;/p&gt;

&lt;p&gt;For ranking-heavy dashboards, a horizontal bar chart can be even better.&lt;/p&gt;

&lt;p&gt;Warehouse A  ████████████████████ 820&lt;/p&gt;

&lt;p&gt;Warehouse B  █████████████         540&lt;/p&gt;

&lt;p&gt;Warehouse C  █████████              390&lt;/p&gt;

&lt;p&gt;Warehouse D  █████                  210&lt;/p&gt;

&lt;p&gt;Long category names become easier to read.&lt;/p&gt;

&lt;p&gt;🥧 UI/UX Rule #6: Don't Abuse Pie Charts&lt;/p&gt;

&lt;p&gt;Pie and doughnut charts are visually attractive.&lt;/p&gt;

&lt;p&gt;That's exactly why they're often overused.&lt;/p&gt;

&lt;p&gt;They work best when showing part of a meaningful whole.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Inventory Distribution&lt;/p&gt;

&lt;p&gt;Materials     55%&lt;br&gt;
PPE           25%&lt;br&gt;
Uniforms      20%&lt;/p&gt;

&lt;p&gt;But imagine 15 categories:&lt;/p&gt;

&lt;p&gt;Category A&lt;br&gt;
Category B&lt;br&gt;
Category C&lt;br&gt;
Category D&lt;br&gt;
Category E&lt;br&gt;
Category F&lt;br&gt;
...&lt;/p&gt;

&lt;p&gt;The chart quickly becomes difficult to read.&lt;/p&gt;

&lt;p&gt;In those cases, a bar chart usually provides a clearer comparison.&lt;/p&gt;

&lt;p&gt;A useful rule:&lt;/p&gt;

&lt;p&gt;Use a pie/doughnut chart when composition matters. Use a bar chart when comparison matters.&lt;/p&gt;

&lt;p&gt;🎨 UI/UX Rule #7: Don't Let Colors Become Noise&lt;/p&gt;

&lt;p&gt;One of the easiest ways to make a dashboard look chaotic is to assign a different color to everything.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;🔴 Sales&lt;br&gt;
🔵 Revenue&lt;br&gt;
🟢 Orders&lt;br&gt;
🟡 Users&lt;br&gt;
🟣 Inventory&lt;br&gt;
🟠 Customers&lt;/p&gt;

&lt;p&gt;The interface starts looking like a rainbow.&lt;/p&gt;

&lt;p&gt;Instead, establish a visual language.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Primary color&lt;br&gt;
    ↓&lt;br&gt;
Main information&lt;/p&gt;

&lt;p&gt;Neutral colors&lt;br&gt;
    ↓&lt;br&gt;
Supporting information&lt;/p&gt;

&lt;p&gt;Accent color&lt;br&gt;
    ↓&lt;br&gt;
Important state&lt;/p&gt;

&lt;p&gt;Warning / Error&lt;br&gt;
    ↓&lt;br&gt;
Only when necessary&lt;/p&gt;

&lt;p&gt;Color should communicate meaning.&lt;/p&gt;

&lt;p&gt;It shouldn't simply decorate the chart.&lt;/p&gt;

&lt;p&gt;👀 UI/UX Rule #8: Highlight What Matters&lt;/p&gt;

&lt;p&gt;Suppose you have this dataset:&lt;/p&gt;

&lt;p&gt;Product A → 10&lt;br&gt;
Product B → 20&lt;br&gt;
Product C → 150&lt;br&gt;
Product D → 15&lt;/p&gt;

&lt;p&gt;The important information is obvious:&lt;/p&gt;

&lt;p&gt;Product C is significantly higher.&lt;/p&gt;

&lt;p&gt;Your visualization should help the user notice that.&lt;/p&gt;

&lt;p&gt;You can use visual hierarchy through:&lt;/p&gt;

&lt;p&gt;Position&lt;br&gt;
Size&lt;br&gt;
Labels&lt;br&gt;
Typography&lt;br&gt;
Controlled color&lt;br&gt;
Sorting&lt;/p&gt;

&lt;p&gt;The user shouldn't have to inspect every element manually.&lt;/p&gt;

&lt;p&gt;🔢 UI/UX Rule #9: Format Numbers for Humans&lt;/p&gt;

&lt;p&gt;Raw numbers are often difficult to scan.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;1250000&lt;/p&gt;

&lt;p&gt;prefer:&lt;/p&gt;

&lt;p&gt;1.25M&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;1250000.00&lt;/p&gt;

&lt;p&gt;prefer:&lt;/p&gt;

&lt;p&gt;$1.25M&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;0.1537&lt;/p&gt;

&lt;p&gt;prefer:&lt;/p&gt;

&lt;p&gt;15.37%&lt;/p&gt;

&lt;p&gt;Chart.js provides callbacks that can be used to format values.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;options: {&lt;br&gt;
    scales: {&lt;br&gt;
        y: {&lt;br&gt;
            ticks: {&lt;br&gt;
                callback: function(value) {&lt;br&gt;
                    return "$" + value.toLocaleString();&lt;br&gt;
                }&lt;br&gt;
            }&lt;br&gt;
        }&lt;br&gt;
    }&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Readable numbers reduce cognitive load.&lt;/p&gt;

&lt;p&gt;🖱️ UI/UX Rule #10: Use Tooltips for Details&lt;/p&gt;

&lt;p&gt;A chart shouldn't try to display every piece of information at once.&lt;/p&gt;

&lt;p&gt;That's where tooltips become useful.&lt;/p&gt;

&lt;p&gt;The default chart can remain clean:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Revenue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;███&lt;br&gt;
  █████&lt;br&gt;
  ███████&lt;br&gt;
  █████████&lt;/p&gt;

&lt;p&gt;When the user interacts:&lt;/p&gt;

&lt;p&gt;┌────────────────────┐&lt;br&gt;
│ March              │&lt;br&gt;
│ Revenue: $42,500   │&lt;br&gt;
└────────────────────┘&lt;/p&gt;

&lt;p&gt;This is a good UX pattern:&lt;/p&gt;

&lt;p&gt;Show essential information by default. Reveal additional information on interaction.&lt;/p&gt;

&lt;p&gt;🔄 UI/UX Rule #11: Combine Charts With Filters&lt;/p&gt;

&lt;p&gt;Charts become much more useful when users can interact with the dataset.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;[ All ] [ Materials ] [ PPE ] [ Assets ]&lt;/p&gt;

&lt;p&gt;Selecting a category can update the chart.&lt;/p&gt;

&lt;p&gt;`$("#categoryFilter").on("change", function () {&lt;br&gt;
    const category = $(this).val();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;updateChart(category);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});`&lt;/p&gt;

&lt;p&gt;This creates a much more dynamic experience.&lt;/p&gt;

&lt;p&gt;Instead of creating four different charts:&lt;/p&gt;

&lt;p&gt;Chart 1&lt;br&gt;
Chart 2&lt;br&gt;
Chart 3&lt;br&gt;
Chart 4&lt;/p&gt;

&lt;p&gt;you can have:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          FILTER
            ↓
         DATASET
            ↓
          CHART
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The same visualization becomes useful for multiple contexts.&lt;/p&gt;

&lt;p&gt;🧱 UI/UX Rule #12: Use Progressive Disclosure&lt;/p&gt;

&lt;p&gt;Don't expose every detail immediately.&lt;/p&gt;

&lt;p&gt;A dashboard can follow this structure:&lt;/p&gt;

&lt;p&gt;Level 1&lt;br&gt;
Summary&lt;br&gt;
   ↓&lt;br&gt;
Level 2&lt;br&gt;
Charts&lt;br&gt;
   ↓&lt;br&gt;
Level 3&lt;br&gt;
Detailed table&lt;br&gt;
   ↓&lt;br&gt;
Level 4&lt;br&gt;
Movement / History&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Inventory&lt;br&gt;
   ↓&lt;br&gt;
Warehouse&lt;br&gt;
   ↓&lt;br&gt;
Material&lt;br&gt;
   ↓&lt;br&gt;
Movement History&lt;/p&gt;

&lt;p&gt;This prevents the interface from becoming overwhelming.&lt;/p&gt;

&lt;p&gt;The user gets more information only when they ask for it.&lt;/p&gt;

&lt;p&gt;📱 Responsive Charts Matter&lt;/p&gt;

&lt;p&gt;A chart that looks great on desktop can become unusable on mobile.&lt;/p&gt;

&lt;p&gt;Always consider:&lt;/p&gt;

&lt;p&gt;Desktop&lt;br&gt;
┌────────────────────────────────────────────┐&lt;br&gt;
│                 CHART                      │&lt;br&gt;
└────────────────────────────────────────────┘&lt;/p&gt;

&lt;p&gt;Mobile&lt;br&gt;
┌──────────────────────┐&lt;br&gt;
│       CHART          │&lt;br&gt;
│                      │&lt;br&gt;
│      ███             │&lt;br&gt;
│      █████           │&lt;br&gt;
│      ███████         │&lt;br&gt;
└──────────────────────┘&lt;/p&gt;

&lt;p&gt;Chart containers should have controlled dimensions and responsive behavior.&lt;/p&gt;

&lt;p&gt;A simple CSS approach:&lt;/p&gt;

&lt;p&gt;.chart-container {&lt;br&gt;
    position: relative;&lt;br&gt;
    width: 100%;&lt;br&gt;
    height: 320px;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;And in Chart.js:&lt;/p&gt;

&lt;p&gt;options: {&lt;br&gt;
    responsive: true,&lt;br&gt;
    maintainAspectRatio: false&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This allows the chart to adapt to the container instead of forcing a fixed aspect ratio.&lt;/p&gt;

&lt;p&gt;⚡ Performance: Don't Render What the User Doesn't Need&lt;/p&gt;

&lt;p&gt;Large datasets can make dashboards slow.&lt;/p&gt;

&lt;p&gt;One important principle is:&lt;/p&gt;

&lt;p&gt;Don't render everything just because you have everything.&lt;/p&gt;

&lt;p&gt;If the user is looking at the top 10 products, there may be no reason to render 500 categories simultaneously.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;p&gt;500 records&lt;br&gt;
     ↓&lt;br&gt;
Filter&lt;br&gt;
     ↓&lt;br&gt;
Sort&lt;br&gt;
     ↓&lt;br&gt;
Top 10&lt;br&gt;
     ↓&lt;br&gt;
Chart&lt;/p&gt;

&lt;p&gt;This improves both readability and performance.&lt;/p&gt;

&lt;p&gt;🧠 A Practical Dashboard Pattern&lt;/p&gt;

&lt;p&gt;A pattern I like for data-heavy interfaces is:&lt;/p&gt;

&lt;p&gt;┌─────────────────────────────────────────────┐&lt;br&gt;
│ Dashboard                                   │&lt;br&gt;
│                                             │&lt;br&gt;
│ [Filter] [Search] [Date Range]              │&lt;br&gt;
└─────────────────────────────────────────────┘&lt;/p&gt;

&lt;p&gt;┌──────────────┐ ┌──────────────┐ ┌───────────┐&lt;br&gt;
│ Total        │ │ Average      │ │ Growth    │&lt;br&gt;
│ 1,240        │ │ $420         │ │ +12.4%    │&lt;br&gt;
└──────────────┘ └──────────────┘ └───────────┘&lt;/p&gt;

&lt;p&gt;┌────────────────────────────┐ ┌──────────────┐&lt;br&gt;
│                            │ │ Distribution │&lt;br&gt;
│       Main Trend           │ │              │&lt;br&gt;
│                            │ │    CHART     │&lt;br&gt;
│         CHART              │ │              │&lt;br&gt;
│                            │ │              │&lt;br&gt;
└────────────────────────────┘ └──────────────┘&lt;/p&gt;

&lt;p&gt;┌─────────────────────────────────────────────┐&lt;br&gt;
│ Detailed Data                               │&lt;br&gt;
│                                             │&lt;br&gt;
│ Table / Records / History                   │&lt;br&gt;
└─────────────────────────────────────────────┘&lt;/p&gt;

&lt;p&gt;This structure works because each component has a clear responsibility.&lt;/p&gt;

&lt;p&gt;🧩 Reusable Chart Functions&lt;/p&gt;

&lt;p&gt;If your application contains multiple charts, avoid duplicating the same configuration everywhere.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;p&gt;`new Chart(...);&lt;br&gt;
new Chart(...);&lt;br&gt;
new Chart(...);&lt;br&gt;
new Chart(...);&lt;/p&gt;

&lt;p&gt;create a reusable function:&lt;/p&gt;

&lt;p&gt;function createChart(canvas, type, labels, values, label) {&lt;br&gt;
    return new Chart(canvas, {&lt;br&gt;
        type,&lt;br&gt;
        data: {&lt;br&gt;
            labels,&lt;br&gt;
            datasets: [{&lt;br&gt;
                label,&lt;br&gt;
                data: values&lt;br&gt;
            }]&lt;br&gt;
        },&lt;br&gt;
        options: {&lt;br&gt;
            responsive: true,&lt;br&gt;
            maintainAspectRatio: false&lt;br&gt;
        }&lt;br&gt;
    });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;createChart(&lt;br&gt;
    document.getElementById("salesChart"),&lt;br&gt;
    "bar",&lt;br&gt;
    ["A", "B", "C"],&lt;br&gt;
    [120, 180, 90],&lt;br&gt;
    "Sales"&lt;br&gt;
);&lt;br&gt;
`&lt;br&gt;
This keeps the implementation consistent.&lt;/p&gt;

&lt;p&gt;🚫 Common Chart.js Mistakes&lt;/p&gt;

&lt;p&gt;Here are some mistakes I try to avoid when building dashboards.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Too many charts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More charts ≠ better analytics.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Too many colors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Color should communicate meaning.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Missing units&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A value like 1250 doesn't tell the user whether it's dollars, units, users, or something else.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Poor labels&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Users shouldn't need to guess what an axis represents.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No hierarchy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every element shouldn't have the same visual importance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overloaded tooltips&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Tooltips should provide useful context, not become hidden tables.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fixed dimensions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Charts should adapt to their containers.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wrong chart type&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A beautiful chart can still be the wrong visualization.&lt;/p&gt;

&lt;p&gt;🧪 A Complete Example&lt;/p&gt;

&lt;p&gt;Here's a small example combining some of these principles:&lt;/p&gt;

&lt;p&gt;`&lt;/p&gt;
&lt;br&gt;
    &lt;br&gt;


&lt;p&gt;const labels = [&lt;br&gt;
    "Main Warehouse",&lt;br&gt;
    "Secondary Warehouse",&lt;br&gt;
    "PPE Warehouse",&lt;br&gt;
    "Regional Warehouse"&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;const values = [&lt;br&gt;
    820,&lt;br&gt;
    540,&lt;br&gt;
    390,&lt;br&gt;
    210&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;new Chart(&lt;br&gt;
    document.getElementById("inventoryChart"),&lt;br&gt;
    {&lt;br&gt;
        type: "bar",&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    data: {
        labels,

        datasets: [{
            label: "Inventory",
            data: values
        }]
    },

    options: {
        responsive: true,
        maintainAspectRatio: false,

        plugins: {
            legend: {
                display: false
            },

            tooltip: {
                callbacks: {
                    label: function(context) {
                        return ` ${context.raw.toLocaleString()} units`;
                    }
                }
            }
        },

        scales: {
            y: {
                beginAtZero: true,

                ticks: {
                    callback: function(value) {
                        return value.toLocaleString();
                    }
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;);&lt;/p&gt;

&lt;p&gt;`&lt;br&gt;
Notice that the example intentionally keeps the interface simple.&lt;/p&gt;

&lt;p&gt;The chart has:&lt;/p&gt;

&lt;p&gt;One clear metric&lt;br&gt;
A meaningful label&lt;br&gt;
Formatted numbers&lt;br&gt;
Responsive behavior&lt;br&gt;
Useful tooltips&lt;br&gt;
No unnecessary legend&lt;br&gt;
A clear comparison&lt;br&gt;
🎯 The Main Principle&lt;/p&gt;

&lt;p&gt;Chart.js is a tool.&lt;/p&gt;

&lt;p&gt;UI/UX determines whether that tool is actually useful.&lt;/p&gt;

&lt;p&gt;You can have technically perfect JavaScript and still create a poor dashboard.&lt;/p&gt;

&lt;p&gt;The most important questions are not:&lt;/p&gt;

&lt;p&gt;"Which Chart.js option should I use?"&lt;/p&gt;

&lt;p&gt;They are:&lt;/p&gt;

&lt;p&gt;"What does the user need to understand?"&lt;/p&gt;

&lt;p&gt;"What information should receive visual priority?"&lt;/p&gt;

&lt;p&gt;"What can be hidden until the user needs it?"&lt;/p&gt;

&lt;p&gt;"Which visualization communicates this data most naturally?"&lt;/p&gt;

&lt;p&gt;Once those decisions are made, implementing the chart becomes much easier.&lt;/p&gt;

&lt;p&gt;🚀 Final Checklist&lt;/p&gt;

&lt;p&gt;Before publishing a dashboard, I like to ask:&lt;/p&gt;

&lt;p&gt;[ ] Does the chart answer a clear question?&lt;br&gt;
[ ] Is the chart type appropriate?&lt;br&gt;
[ ] Is the title meaningful?&lt;br&gt;
[ ] Are the numbers easy to read?&lt;br&gt;
[ ] Are units clearly defined?&lt;br&gt;
[ ] Is there unnecessary visual noise?&lt;br&gt;
[ ] Are colors being used intentionally?&lt;br&gt;
[ ] Can the chart work on mobile?&lt;br&gt;
[ ] Are tooltips useful?&lt;br&gt;
[ ] Can the user filter the information?&lt;br&gt;
[ ] Is the most important information visually dominant?&lt;br&gt;
[ ] Is the chart actually more useful than a table?&lt;/p&gt;

&lt;p&gt;If the answer to most of these questions is yes, you're probably on the right track.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;Creating charts with Chart.js is relatively easy.&lt;/p&gt;

&lt;p&gt;Creating useful data visualizations requires a little more thought.&lt;/p&gt;

&lt;p&gt;The best dashboards don't try to impress users with dozens of charts, animations, or colors.&lt;/p&gt;

&lt;p&gt;They guide the user's attention.&lt;/p&gt;

&lt;p&gt;They create hierarchy.&lt;/p&gt;

&lt;p&gt;They provide context.&lt;/p&gt;

&lt;p&gt;And most importantly, they make complex information easier to understand.&lt;/p&gt;

&lt;p&gt;Good data visualization isn't about showing more data. It's about making the right data easier to see.&lt;/p&gt;

&lt;p&gt;That's where Chart.js + good UI/UX becomes much more powerful.&lt;/p&gt;

&lt;p&gt;If you're building a dashboard right now, start with the question you're trying to answer — and choose the visualization afterward.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>ux</category>
      <category>ui</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building an Asset &amp; Inventory Dashboard with JavaScript, jQuery, Tailwind CSS and Chart.js</title>
      <dc:creator>Coaste</dc:creator>
      <pubDate>Tue, 29 Sep 2026 13:39:27 +0000</pubDate>
      <link>https://dev.to/coaste/building-an-asset-inventory-dashboard-with-javascript-jquery-tailwind-css-and-chartjs-4i7i</link>
      <guid>https://dev.to/coaste/building-an-asset-inventory-dashboard-with-javascript-jquery-tailwind-css-and-chartjs-4i7i</guid>
      <description>&lt;p&gt;In many business applications, important information is spread across different screens, tables, and systems. Although the data is available, understanding it quickly can still be a challenge.&lt;/p&gt;

&lt;p&gt;This project explores how to build a simple and interactive Asset &amp;amp; Inventory Dashboard that brings materials, assets, PPE, uniforms, values, quantities, and movement history into a single interface.&lt;/p&gt;

&lt;p&gt;The dashboard was originally developed for an internal environment and connected to business APIs. For the public version, the original integrations and company-specific information were removed and replaced with mock data.&lt;/p&gt;

&lt;p&gt;The goal is to provide a generic example of how a lightweight dashboard can be built using HTML, JavaScript, jQuery, Tailwind CSS, and Chart.js, without requiring a frontend framework.&lt;/p&gt;

&lt;p&gt;Overview of the Project&lt;/p&gt;

&lt;p&gt;The dashboard was designed to organize different types of operational information into a single interface.&lt;/p&gt;

&lt;p&gt;The main sections are:&lt;/p&gt;

&lt;p&gt;Materials — Inventory organized by warehouse.&lt;br&gt;
Assets — Assets organized by cost center.&lt;br&gt;
PPE and Uniforms — Dedicated inventory information.&lt;br&gt;
Movement History — Historical movements of materials.&lt;br&gt;
Charts — Visual representation of quantities and values.&lt;br&gt;
Search and Filters — Quick access to specific records.&lt;/p&gt;

&lt;p&gt;The basic concept is:&lt;/p&gt;

&lt;p&gt;Data&lt;br&gt;
  ↓&lt;br&gt;
Application Logic&lt;br&gt;
  ↓&lt;br&gt;
Dashboard&lt;br&gt;
  ↓&lt;br&gt;
Charts / Tables / Cards&lt;/p&gt;

&lt;p&gt;Instead of presenting all information in a large table, the dashboard groups related information and provides different ways to analyze it.&lt;/p&gt;

&lt;p&gt;Project Structure&lt;/p&gt;

&lt;p&gt;The project uses a simple structure:&lt;/p&gt;

&lt;p&gt;dashboard-ativos-materiais/&lt;br&gt;
│&lt;br&gt;
├── index.html&lt;br&gt;
│&lt;br&gt;
├── assets/&lt;br&gt;
│   ├── css/&lt;br&gt;
│   │   └── style.css&lt;br&gt;
│   │&lt;br&gt;
│   ├── img/&lt;br&gt;
│   │   └── favicon.svg&lt;br&gt;
│   │&lt;br&gt;
│   └── js/&lt;br&gt;
│       ├── app.js&lt;br&gt;
│       └── mock-data.js&lt;br&gt;
│&lt;br&gt;
├── README.md&lt;br&gt;
└── .gitignore&lt;/p&gt;

&lt;p&gt;The JavaScript code is separated into two main files.&lt;/p&gt;

&lt;p&gt;mock-data.js contains the data used by the public demonstration.&lt;/p&gt;

&lt;p&gt;app.js contains the application logic, including rendering, filtering, searching, charts, and interactions.&lt;/p&gt;

&lt;p&gt;This separation makes it easier to replace the mock data with a real API later.&lt;/p&gt;

&lt;p&gt;Technologies Used&lt;/p&gt;

&lt;p&gt;The project uses a relatively small frontend stack.&lt;/p&gt;

&lt;p&gt;HTML&lt;/p&gt;

&lt;p&gt;HTML provides the structure of the dashboard and its components.&lt;/p&gt;

&lt;p&gt;Tailwind CSS&lt;/p&gt;

&lt;p&gt;Tailwind CSS is used for the visual interface, including:&lt;/p&gt;

&lt;p&gt;Cards&lt;br&gt;
Buttons&lt;br&gt;
Filters&lt;br&gt;
Tables&lt;br&gt;
Modals&lt;br&gt;
Responsive layouts&lt;br&gt;
Typography&lt;br&gt;
jQuery&lt;/p&gt;

&lt;p&gt;jQuery is responsible for the application's interactions and dynamic content.&lt;/p&gt;

&lt;p&gt;It is used for:&lt;/p&gt;

&lt;p&gt;DOM manipulation&lt;br&gt;
Event handling&lt;br&gt;
Filtering&lt;br&gt;
Searching&lt;br&gt;
Rendering&lt;br&gt;
AJAX requests&lt;br&gt;
Modal interactions&lt;br&gt;
Chart.js&lt;/p&gt;

&lt;p&gt;Chart.js is used to create visual representations of the dashboard data.&lt;/p&gt;

&lt;p&gt;Dashboard Categories&lt;/p&gt;

&lt;p&gt;The dashboard provides a simple category filter:&lt;/p&gt;

&lt;p&gt;ALL&lt;br&gt;
MATERIAL&lt;br&gt;
PPE&lt;br&gt;
ASSET&lt;/p&gt;

&lt;p&gt;When the user changes the selected category, the interface updates the information displayed on the screen.&lt;/p&gt;

&lt;p&gt;A simplified version of the interaction looks like this:&lt;/p&gt;

&lt;p&gt;$("#filterType").on("change", function () {&lt;br&gt;
    const type = $(this).val();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filterDashboard(type);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;/p&gt;

&lt;p&gt;This allows the dashboard to change its content without requiring a page reload.&lt;/p&gt;

&lt;p&gt;Materials&lt;/p&gt;

&lt;p&gt;The materials section organizes inventory by warehouse.&lt;/p&gt;

&lt;p&gt;Each warehouse contains information such as:&lt;/p&gt;

&lt;p&gt;Warehouse&lt;br&gt;
├── Total Quantity&lt;br&gt;
├── Total Value&lt;br&gt;
└── Items&lt;br&gt;
    ├── Item Code&lt;br&gt;
    ├── Item Name&lt;br&gt;
    ├── Quantity&lt;br&gt;
    ├── Unit Value&lt;br&gt;
    └── Total Value&lt;/p&gt;

&lt;p&gt;For example, a simplified data structure could look like this:&lt;/p&gt;

&lt;p&gt;const material = {&lt;br&gt;
    warehouseCode: "WH01",&lt;br&gt;
    warehouseName: "Main Warehouse",&lt;br&gt;
    itemCode: "MAT001",&lt;br&gt;
    itemName: "Example Material",&lt;br&gt;
    quantity: 120,&lt;br&gt;
    unitValue: 25.50,&lt;br&gt;
    totalValue: 3060&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;The interface uses this information to create the corresponding cards and item details dynamically.&lt;/p&gt;

&lt;p&gt;Assets&lt;/p&gt;

&lt;p&gt;Assets follow a different grouping strategy.&lt;/p&gt;

&lt;p&gt;Instead of warehouses, they are organized by cost center.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Cost Center 1001&lt;br&gt;
│&lt;br&gt;
├── Asset 001&lt;br&gt;
├── Asset 002&lt;br&gt;
└── Asset 003&lt;/p&gt;

&lt;p&gt;This makes it possible to display the number of assets and their values according to the organizational structure represented by the data.&lt;/p&gt;

&lt;p&gt;The same rendering concept used for materials can be reused here, while the grouping logic is adapted to the type of information being displayed.&lt;/p&gt;

&lt;p&gt;PPE and Uniforms&lt;/p&gt;

&lt;p&gt;PPE and uniforms have their own section in the dashboard.&lt;/p&gt;

&lt;p&gt;The purpose is to keep these records separated from regular materials while maintaining the same visual language.&lt;/p&gt;

&lt;p&gt;This allows the user to switch between different types of inventory without learning a completely different interface.&lt;/p&gt;

&lt;p&gt;Search&lt;/p&gt;

&lt;p&gt;The dashboard also provides a search field.&lt;/p&gt;

&lt;p&gt;A basic implementation can listen for changes in the input:&lt;/p&gt;

&lt;p&gt;$("#search").on("input", function () {&lt;br&gt;
    const searchTerm = $(this).val().toLowerCase();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;filterItems(searchTerm);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;});&lt;/p&gt;

&lt;p&gt;The search can then be applied to the relevant fields, such as:&lt;/p&gt;

&lt;p&gt;Item code&lt;br&gt;
Item name&lt;br&gt;
Warehouse&lt;br&gt;
Cost center&lt;/p&gt;

&lt;p&gt;For larger datasets, a debounce can be used to avoid executing the filtering function unnecessarily on every keystroke.&lt;/p&gt;

&lt;p&gt;The basic flow becomes:&lt;/p&gt;

&lt;p&gt;User types&lt;br&gt;
    ↓&lt;br&gt;
Wait for input&lt;br&gt;
    ↓&lt;br&gt;
Search / Filter&lt;br&gt;
    ↓&lt;br&gt;
Update results&lt;br&gt;
Using Mock Data&lt;/p&gt;

&lt;p&gt;Because the original application was connected to internal APIs, the public version needed a different data source.&lt;/p&gt;

&lt;p&gt;Instead of exposing the original endpoints, the project uses mock data.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;const materials = [&lt;br&gt;
    {&lt;br&gt;
        warehouseCode: "WH01",&lt;br&gt;
        warehouseName: "Main Warehouse",&lt;br&gt;
        itemCode: "MAT001",&lt;br&gt;
        itemName: "Example Material",&lt;br&gt;
        quantity: 120,&lt;br&gt;
        unitValue: 25.50,&lt;br&gt;
        totalValue: 3060&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
        warehouseCode: "WH02",&lt;br&gt;
        warehouseName: "Secondary Warehouse",&lt;br&gt;
        itemCode: "MAT002",&lt;br&gt;
        itemName: "Another Material",&lt;br&gt;
        quantity: 75,&lt;br&gt;
        unitValue: 18.00,&lt;br&gt;
        totalValue: 1350&lt;br&gt;
    }&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;The important part is that the UI doesn't need to know whether the information came from a mock file or an API.&lt;/p&gt;

&lt;p&gt;The application only needs to receive data in the expected format.&lt;/p&gt;

&lt;p&gt;Connecting the Dashboard to an API&lt;/p&gt;

&lt;p&gt;This separation makes it possible to replace the mock data with a real API.&lt;/p&gt;

&lt;p&gt;The public project follows this concept:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                ┌─────────────┐
                │  Dashboard  │
                └──────┬──────┘
                       │
                       ▼
                ┌─────────────┐
                │ JavaScript  │
                │  + jQuery   │
                └──────┬──────┘
                       │
                       ▼
                ┌─────────────┐
                │ Mock Data   │
                └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With a backend, the last layer can simply be replaced:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                ┌─────────────┐
                │  Dashboard  │
                └──────┬──────┘
                       │
                       ▼
                ┌─────────────┐
                │ JavaScript  │
                │  + jQuery   │
                └──────┬──────┘
                       │
                       ▼
                ┌─────────────┐
                │ REST API    │
                └──────┬──────┘
                       │
                       ▼
                ┌─────────────┐
                │ Database    │
                └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is one of the main reasons for keeping the data layer separated from the interface.&lt;/p&gt;

&lt;p&gt;Data Visualization with Chart.js&lt;/p&gt;

&lt;p&gt;The dashboard provides a graphical view in addition to the traditional data view.&lt;/p&gt;

&lt;p&gt;Chart.js can be used to transform the dataset into charts such as:&lt;/p&gt;

&lt;p&gt;Doughnut charts&lt;br&gt;
Bar charts&lt;br&gt;
Ranking charts&lt;/p&gt;

&lt;p&gt;A basic Chart.js configuration looks like this:&lt;/p&gt;

&lt;p&gt;new Chart(canvas, {&lt;br&gt;
    type: "doughnut",&lt;br&gt;
    data: {&lt;br&gt;
        labels: labels,&lt;br&gt;
        datasets: [{&lt;br&gt;
            data: values&lt;br&gt;
        }]&lt;br&gt;
    }&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;The chart receives the same information used by the dashboard.&lt;/p&gt;

&lt;p&gt;This means that the data doesn't need to be duplicated just to create a visualization.&lt;/p&gt;

&lt;p&gt;Movement History&lt;/p&gt;

&lt;p&gt;Another feature of the dashboard is the movement history for materials.&lt;/p&gt;

&lt;p&gt;Instead of displaying only the current inventory:&lt;/p&gt;

&lt;p&gt;Material: MAT001&lt;br&gt;
Quantity: 120&lt;/p&gt;

&lt;p&gt;the user can inspect previous movements.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;h2&gt;
  
  
  Date         Type       Quantity
&lt;/h2&gt;

&lt;p&gt;09/01/2026   Entry      +50&lt;br&gt;
09/05/2026   Exit       -10&lt;br&gt;
09/12/2026   Entry      +80&lt;br&gt;
09/20/2026   Exit       -20&lt;/p&gt;

&lt;p&gt;This provides additional context about the current quantity.&lt;/p&gt;

&lt;p&gt;The public version uses simulated movement data, but the same structure can be connected to a backend API.&lt;/p&gt;

&lt;p&gt;Why jQuery?&lt;/p&gt;

&lt;p&gt;One question that naturally comes up is:&lt;/p&gt;

&lt;p&gt;Why use jQuery instead of React, Vue, or another frontend framework?&lt;/p&gt;

&lt;p&gt;The answer is mostly about project requirements.&lt;/p&gt;

&lt;p&gt;This dashboard needs:&lt;/p&gt;

&lt;p&gt;DOM manipulation&lt;br&gt;
Event handling&lt;br&gt;
Search&lt;br&gt;
Filtering&lt;br&gt;
Dynamic rendering&lt;br&gt;
API communication&lt;br&gt;
Modal interactions&lt;br&gt;
Chart integration&lt;/p&gt;

&lt;p&gt;These requirements can be handled comfortably with jQuery.&lt;/p&gt;

&lt;p&gt;Using a framework would certainly be possible, but it would also introduce additional project structure and dependencies that aren't necessary for this particular implementation.&lt;/p&gt;

&lt;p&gt;The goal was to keep the application:&lt;/p&gt;

&lt;p&gt;Simple → Lightweight → Easy to understand → Easy to deploy&lt;/p&gt;

&lt;p&gt;UI and Design&lt;/p&gt;

&lt;p&gt;The dashboard uses a clean interface focused on the information itself.&lt;/p&gt;

&lt;p&gt;The design uses:&lt;/p&gt;

&lt;p&gt;White backgrounds&lt;br&gt;
Green accent colors&lt;br&gt;
Rounded cards&lt;br&gt;
Subtle borders&lt;br&gt;
Clear typography&lt;br&gt;
Responsive layouts&lt;br&gt;
Minimal visual noise&lt;/p&gt;

&lt;p&gt;The intention was to avoid the traditional heavy-looking enterprise dashboard and create something closer to a modern productivity application.&lt;/p&gt;

&lt;p&gt;The visual hierarchy follows a simple structure:&lt;/p&gt;

&lt;p&gt;Header&lt;br&gt;
  ↓&lt;br&gt;
Filters&lt;br&gt;
  ↓&lt;br&gt;
Summary&lt;br&gt;
  ↓&lt;br&gt;
Data&lt;br&gt;
  ↓&lt;br&gt;
Charts / Details&lt;/p&gt;

&lt;p&gt;This helps keep the most important information visible without overwhelming the user.&lt;/p&gt;

&lt;p&gt;How the Application Works&lt;/p&gt;

&lt;p&gt;The complete flow can be summarized as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load application
    ↓&lt;/li&gt;
&lt;li&gt;Load data
    ↓&lt;/li&gt;
&lt;li&gt;Normalize data
    ↓&lt;/li&gt;
&lt;li&gt;Render dashboard
    ↓&lt;/li&gt;
&lt;li&gt;User selects a filter
    ↓&lt;/li&gt;
&lt;li&gt;Filter dataset
    ↓&lt;/li&gt;
&lt;li&gt;Update interface
    ↓&lt;/li&gt;
&lt;li&gt;Update charts&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a search operation:&lt;/p&gt;

&lt;p&gt;User enters search&lt;br&gt;
        ↓&lt;br&gt;
Search term is normalized&lt;br&gt;
        ↓&lt;br&gt;
Dataset is filtered&lt;br&gt;
        ↓&lt;br&gt;
Matching records are rendered&lt;br&gt;
        ↓&lt;br&gt;
Dashboard is updated&lt;/p&gt;

&lt;p&gt;This keeps the interaction entirely on the client side in the public version.&lt;/p&gt;

&lt;p&gt;Benefits and Use Cases&lt;br&gt;
Reusable&lt;/p&gt;

&lt;p&gt;The dashboard isn't tied to a specific company or database in its public version.&lt;/p&gt;

&lt;p&gt;The data layer can be replaced with another source.&lt;/p&gt;

&lt;p&gt;Lightweight&lt;/p&gt;

&lt;p&gt;There is no complex frontend build system required.&lt;/p&gt;

&lt;p&gt;The project can run as a simple web application.&lt;/p&gt;

&lt;p&gt;Scalable&lt;/p&gt;

&lt;p&gt;The same structure can be extended with additional categories, filters, charts, or API endpoints.&lt;/p&gt;

&lt;p&gt;Easy to Customize&lt;/p&gt;

&lt;p&gt;Because the interface is built with HTML, Tailwind CSS and JavaScript, the visual design can be modified without changing the entire application architecture.&lt;/p&gt;

&lt;p&gt;API Ready&lt;/p&gt;

&lt;p&gt;The mock data layer can be replaced with a REST API when real data becomes available.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;p&gt;This project reinforced an important idea:&lt;/p&gt;

&lt;p&gt;A dashboard isn't just about displaying data. It's about making data easier to understand.&lt;/p&gt;

&lt;p&gt;Creating a chart is relatively easy.&lt;/p&gt;

&lt;p&gt;The more interesting challenge is deciding:&lt;/p&gt;

&lt;p&gt;What information should be highlighted?&lt;br&gt;
What should be grouped?&lt;br&gt;
What should be a card?&lt;br&gt;
What should be a table?&lt;br&gt;
What should become a chart?&lt;br&gt;
What does the user actually need to find?&lt;/p&gt;

&lt;p&gt;These decisions have a direct impact on the usefulness of the application.&lt;/p&gt;

&lt;p&gt;Future Improvements&lt;/p&gt;

&lt;p&gt;There are several things that could be added to future versions:&lt;/p&gt;

&lt;p&gt;REST API integration&lt;/p&gt;

&lt;p&gt;Authentication&lt;/p&gt;

&lt;p&gt;Role-based access&lt;/p&gt;

&lt;p&gt;Advanced filters&lt;/p&gt;

&lt;p&gt;Excel export&lt;/p&gt;

&lt;p&gt;PDF reports&lt;/p&gt;

&lt;p&gt;Pagination&lt;/p&gt;

&lt;p&gt;More detailed analytics&lt;/p&gt;

&lt;p&gt;Mobile improvements&lt;/p&gt;

&lt;p&gt;Dark mode&lt;/p&gt;

&lt;p&gt;The current version intentionally focuses on the frontend and uses mock data.&lt;/p&gt;

&lt;p&gt;Running the Project&lt;/p&gt;

&lt;p&gt;Clone the repository:&lt;/p&gt;

&lt;p&gt;git clone YOUR_REPOSITORY_URL&lt;/p&gt;

&lt;p&gt;Enter the project directory:&lt;/p&gt;

&lt;p&gt;cd dashboard-ativos-materiais&lt;/p&gt;

&lt;p&gt;Then open the project using a local development server.&lt;/p&gt;

&lt;p&gt;For example, you can use VS Code with Live Server.&lt;/p&gt;

&lt;p&gt;Since the public version uses mock data, no backend configuration is required.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;This project started as a practical internal dashboard and evolved into a generic version that can be shared publicly.&lt;/p&gt;

&lt;p&gt;The main goal was not to build the most complex dashboard possible.&lt;/p&gt;

&lt;p&gt;It was to create a solution that is:&lt;/p&gt;

&lt;p&gt;Simple&lt;br&gt;
Visual&lt;br&gt;
Responsive&lt;br&gt;
Easy to understand&lt;br&gt;
Easy to customize&lt;br&gt;
Ready for API integration&lt;/p&gt;

&lt;p&gt;The public version is available on GitHub, using mock data instead of the original internal integrations.&lt;/p&gt;

&lt;p&gt;If you're interested in dashboards, data visualization, jQuery, Tailwind CSS, or lightweight frontend applications, feel free to explore the project.&lt;/p&gt;

&lt;p&gt;GitHub Repository&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/C0aste/Dashboard-Estoque" rel="noopener noreferrer"&gt;https://github.com/C0aste/Dashboard-Estoque&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have suggestions or ideas for improvements, feel free to share them in the comments.&lt;/p&gt;

&lt;p&gt;Thanks for reading! 🚀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>jquery</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Tracking Video Watch Progress with JavaScript</title>
      <dc:creator>Coaste</dc:creator>
      <pubDate>Wed, 11 Dec 2024 11:45:21 +0000</pubDate>
      <link>https://dev.to/coaste/tracking-video-watch-progress-with-javascript-2j5j</link>
      <guid>https://dev.to/coaste/tracking-video-watch-progress-with-javascript-2j5j</guid>
      <description>&lt;p&gt;In modern web applications, it is essential to provide users with a seamless experience where their progress is saved across sessions. One of the most common use cases for this is tracking how much of a video a user has watched, so they can pick up where they left off. This tutorial will walk you through how to implement such a system using JavaScript, localStorage, and event listeners, while also integrating server-side communication to store watched time.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview of the Code
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
The provided solution allows for tracking the watched time of multiple videos on a webpage. It stores the watch progress in the browser’s localStorage and, if a user exceeds the last watched time, updates the progress in a server-side database using a POST request. The goal is to offer a generic, scalable method that works for all videos with minimal effort.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;video class="videoCourse" controls data-idvideo="123"&amp;gt;
  &amp;lt;source src="path/to/video.mp4" type="video/mp4"&amp;gt;
  Your browser does not support the video tag.
&amp;lt;/video&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;video class="videoCourse" controls data-idvideo="123"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;: This element embeds a video player on the page.&lt;/p&gt;

&lt;p&gt;The class="videoCourse" is used to select all videos on the page via JavaScript.&lt;br&gt;
The data-idvideo="123" is a custom attribute that holds a unique identifier for each video. This ID allows us to track and store the watch progress for individual videos.&lt;br&gt;
: Specifies the path to the video file and the video format (in this case, MP4)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Function to update the watched time
function updateWatchedTime(video, videoId) {
    var timeInSeconds = video.currentTime; // Time watched in seconds
    var minutes = Math.floor(timeInSeconds / 60); // Whole part (minutes)
    var seconds = timeInSeconds % 60; // Remaining seconds
    var decimalTime = minutes + (seconds / 60); // Converts seconds into a fractional minute

    // Get the last recorded time for the video (saved in localStorage or a global variable)
    var lastRecordedTime = localStorage.getItem("lastRecordedTime_" + videoId);
    if (lastRecordedTime === null) {
        lastRecordedTime = 0; // Default value if no previous time is found
    } else {
        lastRecordedTime = parseFloat(lastRecordedTime);
    }

    // Check if the current time is greater than the last recorded time
    if (decimalTime &amp;gt; lastRecordedTime) {
        // If the current time is greater, save the new time
        var requestData = "VIDEO_ID=" + videoId + "&amp;amp;WATCHED_TIME=" + decimalTime.toFixed(4);
        console.log("Sending: " + requestData); // Shows the watched time in decimal (4 decimal places)

        // Send the data to the server (replace with actual URL)
        $.post("path/to/api", requestData, function(response) {
            // Handle server response if needed
            console.log("Server response: " + response);
            // After saving, update the localStorage with the new watched time
            localStorage.setItem("lastRecordedTime_" + videoId, decimalTime.toFixed(4));
        });
    } else {
        console.log("Watched time is less than the last recorded time. No update sent.");
    }
}

// Add an event listener for the 'timeupdate' event to all video elements
document.querySelectorAll('.videoCourse').forEach(function(video) {
    // Get the video ID (should be uniquely assigned to each video element)
    var videoId = video.getAttribute('data-idvideo');
    // Add the 'timeupdate' event listener
    video.addEventListener('timeupdate', function(event) {
        updateWatchedTime(video, videoId); // Pass the video and its ID directly
    });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation of the Code
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Update Watched Time Function**
The core of this solution is the updateWatchedTime() function. This function is responsible for:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Calculating the Watch Progress: The function first retrieves the currentTime of the video (in seconds) and converts this into minutes and seconds. The time is then converted to a decimal format (e.g., 3 minutes 30 seconds becomes 3.50).&lt;/p&gt;

&lt;p&gt;Checking the Last Recorded Time: Using the localStorage.getItem() method, we retrieve the last recorded time for the video. If no time has been recorded yet (i.e., the first time the user watches the video), it defaults to 0. This ensures that progress tracking starts from zero.&lt;/p&gt;

&lt;p&gt;Storing the Time: If the current time is greater than the last recorded time, it means the user has watched more of the video since the last update. The function sends the updated time to the server using a POST request. After the data is successfully sent, the localStorage is updated with the new time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Handling Multiple Videos&lt;/strong&gt;&lt;br&gt;
The script adds an event listener to all videos on the page with the class="videoCourse". It uses the data-idvideo attribute to get the unique video ID and tracks the progress for each individual video.&lt;/p&gt;

&lt;p&gt;Event Listener: Every time the video’s time is updated (i.e., as the user watches the video), the timeupdate event is triggered. This event fires continuously as the video plays, providing an opportunity to track the progress.&lt;/p&gt;

&lt;p&gt;querySelectorAll(): This method selects all video elements on the page, making the script adaptable for any number of videos. It loops through each video, attaching the timeupdate listener, ensuring that the watch progress is tracked for each video independently.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Works: A Step-by-Step Flow
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
User Watches Video: As the user watches the video, the timeupdate event continuously triggers.&lt;br&gt;
Watch Progress Calculated: The script calculates how much of the video has been watched in minutes and seconds, then converts it to a decimal format.&lt;br&gt;
Last Recorded Time: The script compares the current watch time to the last recorded time saved in localStorage.&lt;br&gt;
Update if Necessary: If the current watch time is greater than the previously saved time, the new time is sent to the server. Afterward, the new time is saved in localStorage.&lt;br&gt;
Next Visit: The next time the user visits the page, the script checks localStorage for the last saved time. If available, it starts tracking from where the user left off.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits and Use Cases
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
Scalability: This approach works for any number of videos on a page. It uses the data-idvideo attribute to uniquely identify each video, making the system scalable without modification.&lt;br&gt;
Persistence: Using localStorage, the user’s progress is saved across sessions. Even if the page is reloaded or if the user leaves and returns, their progress is retained.&lt;br&gt;
Seamless Integration: The solution integrates smoothly with existing video players, making it easy to implement on websites that already use the HTML5  tag.&lt;br&gt;
Server Integration: The system allows you to send the watch progress to the server, providing a way to track user engagement and provide insights into how users interact with video content.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>learning</category>
      <category>html</category>
    </item>
    <item>
      <title>Tailwind - Fixed bottom</title>
      <dc:creator>Coaste</dc:creator>
      <pubDate>Thu, 14 Mar 2024 13:47:27 +0000</pubDate>
      <link>https://dev.to/coaste/tailwind-fixed-bottom-6ll</link>
      <guid>https://dev.to/coaste/tailwind-fixed-bottom-6ll</guid>
      <description>&lt;p&gt;In Tailwind's docs, they don't mention how to create a button to take you back to the top, but I've come across one solution for this issue:&lt;/p&gt;

&lt;p&gt;when you create and set up your application, copy and paste this code in the top of your page.&lt;/p&gt;

&lt;p&gt;`&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;img src="yourimage.png" alt="Up Your Page"&amp;gt;
 &amp;lt;span&amp;gt;Subir Página&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;`&lt;/p&gt;

&lt;p&gt;This button (when you click) does your page return to the first div.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Tailwind - Forms</title>
      <dc:creator>Coaste</dc:creator>
      <pubDate>Thu, 15 Feb 2024 17:30:53 +0000</pubDate>
      <link>https://dev.to/coaste/tailwind-forms-5cce</link>
      <guid>https://dev.to/coaste/tailwind-forms-5cce</guid>
      <description>&lt;p&gt;Installation,&lt;br&gt;
Latest version (Using &lt;strong&gt;NPM&lt;/strong&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install flowbite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creating a "&lt;strong&gt;tailwind.config.js&lt;/strong&gt;" file, put this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {

    plugins: [
      require('flowbite/plugin')

 ]
    content: [
        "./node_modules/flowbite/**/*.js"
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Require the JavaScript code that powers the interactive elements before the end of your *&lt;em&gt;body *&lt;/em&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="../path/to/flowbite/dist/flowbite.min.js"&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or include via CDN (&lt;strong&gt;not recommended&lt;/strong&gt;)&lt;br&gt;
If you want to quickly test out Flowbite you can easily include the following CSS and JavaScript files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.css"  rel="stylesheet" /&amp;gt;
&amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"&amp;gt;&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tailwindcss</category>
      <category>css</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
