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

Filament Jev Moderation Laravel Package

phoenix1331/filament-jev-moderation

Filament plugin for JEV moderation that adds review queues, approval and rejection actions, and status tracking to your Laravel admin panel. Designed for teams that need clear, audit-friendly content moderation workflows, it streamlines oversight and keeps moderated items organized.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a Filament panel UI over the phoenix1331/laravel-jev-moderation core (requires core 0.2). Almost nothing here does moderation itself — it reads the core's decisions, config and calibration data, and writes back through approve() / reject(). Install the core and get it producing decisions before touching this package.

  1. Require phoenix1331/laravel-jev-moderation:^0.2 and this plugin.
  2. Register the plugin on your panel:
->plugin(
    JevModerationPlugin::make()
        ->models([Post::class, Comment::class])
        ->modelLabels(['post' => 'Posts', 'comment' => 'Comments'])
        ->authorRelation('user')
)
  1. Set authorize() before deploying anywhere non-local. Without a callback, access is local-environment-only and every page and widget 403s on mount and on each request. This is the single most common "it's broken in staging" cause.
  2. Decide on action-level authorization: authorizeActionsUsing() or a registered ModerationDecision policy (Filament Shield works). Abilities are approve, reject, reclassify.
  3. First use case: open Moderation → Review queue, triage one or a selection, click a row into History to see why Jev decided what it did.

The Overview widget (waiting count, oldest wait, blocks since midnight, median queue-to-review, 7-day override rate) is the best single check that the pipeline is alive.

Implementation Patterns

Triage loop with the queue. The queue is sorted most-confident-first, then longest-waiting. Row actions are icon buttons pinned before the columns so they stay reachable beside an open sidebar at 1280px. Approve/reject accept a selection spanning multiple model types and an optional note (≤2,000 chars). Everything goes through the core, so your existing ReviewResolved listeners fire exactly as they do elsewhere — wire notifications, cache busts, or re-indexing there rather than into Filament action hooks.

Handle stale rows gracefully. A record changed since page load (already reviewed, re-reported, rescanned), deleted, or outside the moderator's action policy is skipped. The notification reports how many were skipped; a record the core refuses (e.g. its policy was removed from config) is reported with the reason without aborting the batch. Treat the queue as eventually-consistent and never assume the batch applied wholesale.

Reclassify instead of reject-with-wrong-category. When Jev flagged the right content under the wrong category, use reclassify: it records a core reject() under the corrected category, so your analytics and calibration stay honest. It only offers the record's other harmful categories from its policy, and disappears if the policy is gone from config.

Tuning is a config + rescan loop. Read the Thresholds page for each policy's review_above, block_above, uncertain_below, version and consuming models. Change config, then run the suggested php artisan jev-moderation:rescan per model, naming the status the change affects — flagged normally, blocked after raising block_above. Then watch the override rate fall in the calibration widget. Do not rescan a status you didn't change; that re-checks decisions moderators already made.

Embed widgets in your own dashboards. ModerationOverview and DecisionVolumeChart are ordinary widgets — add them to a non-moderation dashboard, they refresh every minute. CalibrationTable with onlyFlagged gives you just the bands needing attention; the thresholds page carries the full picture and the Jev version selector.

Deep links back to your resources. Each queued row has a "View" link to your own Filament view page (falls back to edit, respecting permissions, opens in a new tab, hidden when absent). Override with recordUrlUsing(fn ($record) => ...), or return null to hide it entirely — useful when your resources aren't ready for moderators or you want a custom preview.

Author-level investigation. Set authorRelation() (default user) or authorRelations([...]) per model so the author history page resolves. It aggregates every decision across the types that author writes, with 7/30-day per-status counts — the fastest way to spot a repeat offender or a miscalibrated policy hitting one verbose user.

Gotchas and Tips

  • authorize() must return exactly true. A truthy return is not enough; a callback that leaks a model instance or a non-empty array denies access. Return a strict boolean.
  • Local-only default is a footgun for staging. Pages 403 on mount and every subsequent request, and widgets 403 even when mounted directly on someone else's dashboard — so authorization failures look like a broken widget rather than a permission problem.
  • Invalid plugin option values fail at panel boot. Great for catching typos, but one bad closure in modelLabels() takes the whole panel down, not just the moderation pages. Register the plugin last and test after edits.
  • Default model scope is "every type with decisions." A newly moderated model silently joins your queue. Scope with models([...]) if you want the queue curated.
  • includeBlocked() is off by default. Auto-blocks are not in the queue until you opt in — expect "where did the blocks go?"
  • Confidence filter is a percentage, inclusive, and drops reports. Reports have no confidence score and are excluded the moment you use that filter; category filtering matches the category Jev gave before the report, not the reporter's claim.
  • Calibration highlights need both thresholds. A band must exceed overrideWarningRate() and have at least minimumSample() reviews before it is flagged — with a fresh install you will see no warnings for a while. That is deliberate, not a bug.
  • Panel and CLI should agree. The override rate is computed exactly as php artisan jev-moderation:stats does; if the numbers diverge, you have a duplicate/legacy review path, not a UI bug.
  • Config drift is surfaced, not fatal. A policy unreadable from config is listed with its reason on the thresholds page; on a record it makes reclassify vanish and the core refuse with a stated reason. If actions start failing with reasons, diff your config/jev-moderation.php against deployed config.
  • Deleted content keeps its decision history (openable from History) but is absent from author history, since the author can no longer be read. Don't treat the author page as a complete audit log.
  • Queue row actions and batch actions share core semantics — a batch containing one stale record still applies the rest; always read the skip count in the notification before reporting "done".
  • v0.1.0 with a pinned core 0.2 — the public surface (plugin options, widget names, page slugs) is young. Pin both packages together and re-read these notes on every minor bump.
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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
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