jeffersongoncalves/filament-fathom
A Filament plugin that brings Fathom Analytics into your admin panel, letting you view site traffic and key stats directly from a dashboard widget without leaving Filament.
Minimal path from composer require to a working settings page in one of your own navigation groups:
Install both packages together. This release's navigationGroup() feature is only wired up when filament-analytics-core is at ^3.1 or newer. Require the core package explicitly rather than letting the plugin pull an older transitive version:
composer require filament-analytics-core:^3.1 <plugin-package>:^3.4
Register the plugin on your panel in app/Providers/Filament/AdminPanelProvider.php:
public function panel(Panel $panel): Panel
{
return $panel
->plugin(Plugin::make());
}
Publish and run anything the install docs call for (config, migrations) and make sure the panel user can actually reach the page — navigation groups do not affect authorization.
Where to look first: the Plugin class itself is the whole public surface here. It follows Filament's standard fluent-plugin convention (navigationGroup, and typically the sibling navigationLabel / navigationSort / navigationIcon). Read its getNavigationGroup() implementation before you touch config — most surprises come from that one method, not from the settings page.
First real use case: you already have an admin panel with an "Settings" or "System" group full of your own resources, and the analytics settings page shows up in the package's own translated group below them. Adding ->navigationGroup('Settings') folds it into your existing menu with one line and no page subclassing.
Static grouping (the common case). Pass the same string your resources already use:
->plugin(
Plugin::make()->navigationGroup('Settings')
)
Because Filament merges by group name, matching the string exactly is what makes the plugin page appear inside your group rather than in a second group of the same name.
Per-user / per-panel grouping via Closure. Use the Closure form when the group depends on runtime context — the current user's role, the active tenant, or the locale:
->plugin(
Plugin::make()->navigationGroup(
fn () => auth()->user()?->isSuperAdmin() ? 'System' : 'Settings'
)
)
Keep the closure cheap: it is evaluated during navigation build, which happens on most panel requests (or once, if you cache navigation), so no queries against large tables and no remote calls.
Multi-panel setups. Register the plugin separately in each panel provider and give each one its own group. This is the cleanest way to make the analytics settings page appear under "Tenant Settings" for customer panels and "Platform" for the operator panel without any conditional logic in the page itself.
Localisation you control. If you override the group with a string, you own the translation. Wrap it:
->navigationGroup(__('admin.nav.settings'))
Forgetting this is the usual cause of a half-translated sidebar in non-English panels.
Ordering and labelling. Grouping alone rarely looks right; pair it with the sort/icon methods so the page lands in a sensible slot rather than at the end of your group. Chain them in one expression — the plugin methods are fluent:
Plugin::make()
->navigationGroup('Settings')
->navigationSort(50)
Keep the default when it's good enough. Omitting navigationGroup() entirely preserves the package's translated group, which is the right call if you have no settings group of your own and want the plugin's translations to keep working for free (including future language additions).
filament-analytics-core below 3.1 the plugin still installs and the default group still works, but navigationGroup() has no effect. If your group override "silently does nothing", check the locked core version first (composer show filament-analytics-core), not your panel provider.3.4.0 is a minor release with a matching core minor. Bumping only one side is the most likely source of odd behaviour in CI where the lockfile differs from local.'Settings' and 'settings', or a trailing space from a copied resource property, produce two separate groups in the sidebar. Grep your resources for the canonical group name and reuse a constant rather than retyping the literal.dump() inside the closure) before shipping — dependency-injection into the closure depends on the plugin's implementation and is not part of the documented contract.php artisan filament:optimize-clear (or optimize:clear) or you will debug a change that already happened.canViewAny, canAccessPanel) — a user who can't see the group still reaches the route directly if the page itself isn't protected.How can I help you explore Laravel packages today?