Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Bar Metrics Laravel Package

insenseanalytics/nova-bar-metrics

A Laravel Nova package that adds a bar chart metric card for visualizing trends and comparisons on your dashboard. Display metric values as clean, responsive bars with customizable labels, colors, and formatting for at-a-glance reporting.

View on GitHub
Deep Wiki
Context7

Getting Started

Run the compatibility check before you run composer require. This package was last released in December 2018, which puts it in the Nova 1.x era (Laravel 5.6/5.7, PHP 7.1/7.2, Vue 2, Laravel Mix). Two commands settle the question:

php -v
composer show laravel/nova

If you are on Nova 3+, PHP 8.x, or Laravel 9+, stop treating this as a dependency and treat it as a reference implementation you will fork or reimplement. The only "install and go" scenario is a legacy admin panel still pinned to Nova 1.x/2.x.

Read the source before you install it — it is small enough to read end-to-end in one sitting:

  1. composer.json — the laravel/nova, php, and illuminate/* constraints tell you immediately whether resolution will succeed. This is the fastest signal you will get.
  2. src/ — the Metric subclass is the entire PHP surface. Note how it implements calculate(Request $request) and the shape it returns (typically a flat associative array of label => value, sometimes nested {label, value} pairs to carry colours).
  3. resources/js/ — the Vue card component. Confirm which charting library it uses (likely Chart.js) and how it consumes the calculate() payload, because that payload shape is your contract.
  4. The service provider — does it register a card, publish assets, or expose a config file? That determines whether you need php artisan vendor:publish and an npm run dev rebuild.

Minimal integration (legacy Nova only):

composer require insenseanalytics/nova-bar-metrics
// app/Nova/Metrics/TicketsByStatus.php
namespace App\Nova\Metrics;

use App\Models\Ticket;
use Illuminate\Http\Request;
use InsenseAnalytics\NovaBarMetrics\BarMetric; // check src/ for the real base class

class TicketsByStatus extends BarMetric
{
    public $name = 'Tickets by Status';
    public $width = '1/2';

    public function calculate(Request $request)
    {
        return $this->count($request, Ticket::class, 'status');
    }
}
// app/Providers/NovaServiceProvider.php
public function cards()
{
    return [
        new \App\Nova\Metrics\TicketsByStatus,
    ];
}

If the service provider requires published assets, finish with php artisan vendor:publish --provider="..." and npm run dev.

First use case to prove it out: "tickets by status." It is a bounded categorical frequency with a handful of known values, so a wrong, stale, or empty chart is immediately obvious, and the query is cheap. Do not start with a numeric histogram — binning is precisely where these implementations get subtle, and you want the rendering pipeline validated before you debug bucket boundaries.

Implementation Patterns

Categorical frequency — the primary pattern. Group by a status/enum/category column and let the card render the bars. Always constrain the query rather than letting the card scan the whole table:

public function calculate(Request $request)
{
    return $this->count(
        $request,
        Ticket::whereNull('archived_at'),
        'status'
    );
}

Numeric histograms — the reason you would pick this over a native Partition card. For "orders by value bucket" you need binning, and the binning belongs in SQL. A dashboard card runs on every page load; never pull every row into PHP to array_filter it.

$buckets = DB::table('orders')
    ->where('created_at', '>=', now()->subDays(30))
    ->selectRaw("
        CASE
            WHEN total < 100  THEN '< 100'
            WHEN total < 500  THEN '100–499'
            WHEN total < 2000 THEN '500–1,999'
            ELSE '2,000+'
        END AS bucket,
        COUNT(*) AS aggregate
    ")
    ->groupBy('bucket')
    ->pluck('aggregate', 'bucket');

Then post-process in PHP to force bucket order and to inject zero-count buckets — GROUP BY silently omits empty ranges, and a missing bar looks like a data bug nobody notices for weeks:

$order = ['< 100', '100–499', '500–1,999', '2,000+'];

return collect($order)
    ->mapWithKeys(fn ($label) => [$label => (int) ($buckets[$label] ?? 0)])
    ->all();

Cache aggressively. Nova metrics support ->cacheFor(...) and ->refreshWhenActionsRun(). A COUNT(*) ... GROUP BY over a large table on every dashboard hit will appear in your slow query log within a day. Pair the cache with an index on the grouped column — a composite index on (status, created_at) covers most filtered counts.

Put authorization on the metric, not the dashboard. Use ->canSee(fn ($request) => $request->user()->can('viewAnalytics')) so the card disappears for support staff rather than 403-ing the whole dashboard. Keep the gate coarse; Nova dashboards get shared across roles fast.

Encode time windows in the metric name. The card has no interactive date-range filter. Bake the window into the query and into $name ("Tickets by Status (30d)") so the reader is never guessing scope. Registering two cards — 7-day and 30-day — is usually clearer than one ambiguous card. Anything more interactive than that belongs on a purpose-built Nova page, not a metric card.

Pin colours to enum values explicitly. If the base class exposes a colour map, define it against your known statuses rather than letting the palette auto-assign, so "Open" is the same green across dashboards, environments, and deployments. Colour drift across environments is the kind of thing that erodes trust in a dashboard.

The reimplementation pattern — the common case. If the package will not install, keep the shape rather than the dependency: one Metric subclass returning a label => value array, plus a small Vue card rendering Chart.js bars against Nova's metric endpoint. Carry the binning logic over verbatim with MIT attribution in the file header, and write two tests — one asserting bucket boundaries, one asserting zero-buckets are present. That is the entire deliverable, and it is about a day of work.

Check native alternatives before you fork anything. A categorical frequency is often expressible as a Nova Partition card, and a time-bucketed distribution as a stacked Trend with ->showValues(). If either satisfies the requirement you have added zero dependency surface. Reach for a custom bar card only when the requirement is genuinely a horizontal category comparison that a pie chart reads poorly — which is real, but rarer than it looks in the demo.

Gotchas and Tips

Composer will likely refuse to resolve. The constraints target PHP 7.1/7.2 and Nova 1.x. Widening them ("php": "^7.1|^8.0") gets you past resolution but not past runtime — code from that era commonly trips PHP 8.2 dynamic-property deprecations and missing-return-type notices. Fix them in a fork. Never patch vendor/.

Nova is a private Composer repo. Any CI runner, container build, or new developer machine needs nova.laravel.com credentials configured. If your fleet has mixed Nova majors, no single package constraint will satisfy all of them — pin per application.

The Vue 2 → Vue 3 break is the hard wall. Nova 4+ moved to Inertia + Vue 3 with a revised asset pipeline. Compiled assets from a Mix/Node 8–10 build will not load, and Nova::script() / Nova::style() registration semantics have shifted. Do not budget this as "a small fix" — it is a front-end rewrite, and that is the point at which reimplementation is cheaper than adoption.

Empty results render as a broken card, not an empty one. Verify what the component does when calculate() returns [] or all-zero values. If it throws or renders a blank white box, guard in calculate() by returning a single 'No data' => 0 entry, or hide the card with ->canSee() when the total is zero.

NULL categories vanish silently. GROUP BY status drops rows where status IS NULL. Decide whether those belong in an "Unknown" bucket and coalesce accordingly: COALESCE(status, 'unknown'). Same for soft-deleted rows — if the model is not already scoped, filter them explicitly.

Ordering is whatever the database returns. Add ORDER BY in SQL or re-sort the array in PHP against a fixed key list. Unsorted bar charts are the single most common "why does this look wrong?" report, because the ordering silently shifts between deploys as data distribution changes.

Debug with the network tab first, dd() second. Every Nova metric is served from a JSON endpoint. Open DevTools, find the metric request, and inspect the {value: [{label, value}, ...]} payload. If the payload is correct, the bug is in the Vue layer — stop debugging PHP. If it is wrong, dd($result) at the end of calculate() and diff it against the raw SQL.

Zero tests, zero CI, and nobody upstream watching. 40 stars, 0 dependents, single publisher, no commits in roughly seven years. There will be no security advisory and no compatibility patch. Your minimum safety net: one test asserting calculate()'s exact output against a seeded fixture, and one screenshot of the rendered card. When you bump the Nova major, that test tells you what broke before a stakeholder does.

Record the MIT attribution. Treat every line you adopt as first-party code you now own. Put the attribution and source URL in the file header so it survives the next refactor — otherwise "why is this file here?" becomes an archaeology project for the next developer.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk