nsavinov/nova-percent-field
A Laravel Nova field for entering and displaying percentage values. Adds a dedicated percent input with formatting and validation, so percentages store and render cleanly in Nova resources without custom field code.
Architecture fit. This is a single-purpose Laravel Nova form field that renders a numeric input with percent semantics — a % affordance and, optionally, a scaling transform between the stored value and the displayed value. It lives entirely inside the Nova admin layer: no domain logic, no validation policy, no API or reporting surface, no service-provider wiring beyond Nova's own asset registration. For a TPM the classification should be stated in the intake note plainly: this is a tactical UI convenience, not a platform component. It should never sit on an architecture diagram in a position that implies strategic dependency, and it should never be load-bearing for a pricing, tax, commission, or payout capability.
Integration feasibility. Superficially trivial — one composer require, then Percent::make('Discount Rate') on a resource. The real feasibility question is version compatibility, and that is where the package's age dominates. The last release is 2018-09-26, placing it in the Nova 1/2 era: Vue 2, Laravel Mix, Nova::script() / prebuilt dist/js/field.js asset registration. Nova is not on Packagist, so the laravel/nova constraint in the package composer.json is effectively a hard pin against an old major. On a modern Nova install the require will likely fail to resolve, or install against a stale constraint. That means the honest feasibility answer is "port, not drop-in" until a compatibility spike proves otherwise.
Technical risk.
FormField.vue component may never register. Budget a component rewrite.null coerced to 0 silently turns "no discount set" into "0% discount"; asymmetric save/display transforms produce "saved 1500%" bugs; the default step of 1 blocks 12.5 in the browser with no server-side error; client-side min/max is a hint, not validation, and is bypassed by API writes, Nova actions, and pasted input.15 = 15%) or fraction (0.15 = 15%) — that must be enforced across seeders, factories, API resources, imports, exports, and reporting.Key questions to resolve before any commitment.
laravel/nova constraint actually say?boot() use Nova::script() against a prebuilt dist/js/field.js, and does our Nova still support that pattern?Number::make('Rate')->step(0.01)->min(0)->max(100)->suffix('%')->rules(...) genuinely insufficient, or are we carrying a dependency for cosmetics?Stack fit. Fits Laravel + Nova admin panels only, and realistically Nova 1/2-era stacks. It is not a fit for a Vite-built modern Nova without a port, and it is not a fit for any surface outside the admin panel — public forms, APIs, or customer-facing UI all sit outside its scope.
Migration path. There is no migration to this package worth planning as a project. The decision is framed correctly as a build-vs-buy call where "build" (or rather, "use what Nova already ships") frequently wins. Native Number with step, min, max, suffix('%'), and rules(...) covers most admin needs with zero third-party surface area. Reach for this package only when maintaining an existing install that already depends on it, or when the fraction-to-percent display convention genuinely earns its keep on a compatible Nova version.
Compatibility approach. Verify, don't assume. The sequence is: read composer.json for the laravel/nova constraint → read src/ to confirm the field extends Laravel\Nova\Fields\Number (and inherit rules/nullable/sortable for free) → read boot() for the asset registration pattern → read resources/js/components/FormField.vue, which is where the actual percent behaviour (scaling, rounding, % suffix, null handling) lives. The PHP class is thin; the component is the product. If the constraint won't resolve, fork it, loosen the constraint, and pull it in via a VCS or path repository.
Sequencing.
App\Nova\Fields\Percent that extends or composes the package field and sets standard rules, step, and formatting. One file to patch when we fork, one file to delete when we replace it with native Number.fillUsing/resolveUsing as exact inverses — and test the round trip. This is the single test worth writing.min/max/step into ->rules(); the field will not do it for you.Maintenance. The PM decision is not "adopt a vendor package" — it is "accept and own a small piece of vendored code." With 21 stars, zero dependents, and a 2018 release, assume upstream is inert. The wrapper-field pattern is what keeps the maintenance tax small: one file to own, one file to patch, one file to delete. Approve only if that ownership is explicitly someone's job.
Support. None. No maintained support, no security updates, no community to escalate to. Any defect is ours to fix, and any future Nova major upgrade likely forces a FormField.vue rewrite. That should be a planned cost, not a surprise.
Scaling. Not a meaningful concern for this package — it's a form field with no throughput, storage, or compute footprint of its own. The scaling question is organizational: how many resources adopt it, and how consistently the storage contract is held across them. Inconsistency here is what creates downstream reporting bugs, not load.
Failure modes to instrument and document.
null → 0 coercion silently converting "no discount set" into "0% discount" — add explicit null guards on both sides and check setInitialValue/prepare.step default of 1 blocking 12.5 at the browser level with no server-side error to explain it — set step deliberately.min/max mistaken for validation — API writes, Nova actions, and pasted input bypass it entirely; duplicate in ->rules().dist/js/field.js or a stale nova:publish; also Nova::script() registrations fail silently if two fields reuse the same key, a real hazard if a fork lives alongside the original.12,5; the 2018-era component almost certainly assumes a dot and may truncate silently. Normalise server-side or document the limitation for internal users.sortable() sorts the raw column while the header shows a scaled value. Usually harmless for monotonic transforms; confirm it before someone files a "wrong ordering" bug.Ramp-up. Low for a Nova-fluent engineer — the integration surface is one field and a Vue component. The real ramp-up cost is the compatibility spike and the contingency for a FormField.vue rewrite. Budget both up front, and set the replacement trigger now: the next Nova major upgrade is the natural moment to retire this in favor of native Number formatting or a small custom field.
How can I help you explore Laravel packages today?