wdelfuego/nova-calendar
A Laravel Nova calendar tool that displays your Eloquent models as events on a monthly calendar, with customizable event rendering, filtering, badges, and click-through actions—all configurable from a single Nova resource.
Architecture fit. The package is a Nova Tool, not a framework — it slots in as a presentation layer above Eloquent models that already exist. That is the single most important architectural fact for a TPM: it does not introduce a schema, a domain model, or a service boundary you must own. It reads rows, maps start/end attributes, and renders them. The extension model is an adapter per Nova resource (NovaEventSource) plus a synthetic-event path (CustomEventSource) for things that aren't rows — holidays, cutoffs, on-call rotations. If your bookings, shifts, or editorial deadlines are already Eloquent models with date columns, the fit is near-perfect. If they are not, this package is the wrong place to start modeling a scheduling domain.
Integration feasibility. Three artifacts: a composer require, a tool registration in NovaServiceProvider::tools(), and one event-source class mapped into config/nova-calendar.php. A working read-only month view over one trusted table is an hour-to-a-day of engineering. There is no data migration if you keep event data in app-owned tables — the installer's scaffolded model/migration should be treated as a column shape to copy, not a schema to adopt. That decision is what preserves the exit path and is worth making explicit in the spike's acceptance criteria.
Technical risk.
NOASSERTION) is a hard gate, not a footnote. GitHub cannot tell you what you may do with this code. Legal sign-off on commercial use and internal forking must precede engineering hours, not follow them.Key questions to close before approval.
LICENSE file for commercial use and forking?composer.json constrain cleanly against our Nova major, Laravel, and PHP versions — verified on a branch, not inferred?Stack fit. Laravel + Nova only. The package has zero value outside that boundary and must never become the reason we stay on Nova or the reason we cannot leave. Confirm Nova is not itself under review before committing.
Migration path. There is effectively none, which is the point. Event data stays where it already lives. The installer publishes config, and likely stubs/models — inspect every published file before committing, and default to copying the shape into app-owned tables rather than adopting the scaffolded schema. Rollback is a documented one-liner: remove the tool from tools(), delete the adapter classes, config:clear. No data migration, no orphaned tables. Put that sentence in the PR description; it is what makes the approval easy.
Compatibility. Three known friction points, all operational rather than architectural:
php artisan config:clear after any eventSources change; this is the leading cause of "my source doesn't appear."php artisan nova:publish on every Nova and package upgrade; stale assets are the leading cause of a blank panel.Localization is supported but not automatic; publish translations if the audience isn't English.
Sequencing. License clearance → version-constraint verification on a branch → timeboxed spike (one model, read-only, one event source) → explicit validation of multi-day, all-day, zero-length, and end-date-inclusive/exclusive semantics → authorization scoping review with a negative test → staging asset-publish validation → internal ship → standing maintenance slot. Do not let the spike drift into recurrence, permissions, or CRUD — those are separate decisions with separate estimates.
Maintenance. Assume a recurring slot per Nova major upgrade. Pin versions, name an owner, and treat fork readiness as a precondition rather than a contingency. With zero dependents, upstream breakage has no community fix path; our latency to patch is the outage duration. That cost is small for an internal read-only view and disqualifying for anything revenue-critical.
Support. No SLA, no vendor relationship, single maintainer. Internal support is ours by default: document the extension-point ladder (custom event source → custom data provider → published views → fork) and require engineers to descend it in that order rather than reaching for a fork. The package's Event class and event-source base classes in vendor/ are the most reliable documentation available; budget for reading source rather than docs.
Scaling. Query shape is the limit, not request volume. Index starts_at/ends_at, filter to the visible range inside the source, and beware agenda-style views over long windows — payload size and render time degrade before CPU does. Keep accessors pure: no auth(), DB::, or relationship queries inside event-name or colour resolution, since each one multiplies by event count in the window. Use eager loads configured on the source or precomputed accessors.
Failure modes and their signatures.
| Symptom | Likely cause | Owning team |
|---|---|---|
| Blank calendar panel | Stale published assets after upgrade | Eng, routine |
| New event source absent | Config cache not cleared | Eng, routine |
| Blank or provider-level error | Date attribute name mismatch | Eng, routine |
| Events shifted out of window | Timezone/DST conversion at event boundary | Eng, needs test coverage |
| 500 takes down Nova shell | One bad event source throws | Eng — mitigate with a defensive wrapper so one source failing yields an empty calendar plus a logged error |
| Unauthorized rows visible | Missing query scope on the source | Security — incident class |
The last row is the one that should appear in the risk register. The rest are engineering hygiene.
Ramp-up. Low for any Laravel developer. One event source is a thin adapter — if it exceeds roughly forty lines, logic belongs on the model or in a service. Colour and status mapping should be centralised once (config or a static method on the model) rather than scattered across source classes. First working calendar in a day; production-quality scoping and edge-case coverage in a week. Testing should assert on data-provider output for a fixed window against a seeded dataset — "March returns fourteen events, one spanning three days" — rather than driving the Vue UI.
Product framing to carry into approval. This buys a read-oriented lens over data we already model, for internal audiences, at roughly one day of engineering against three-to-six weeks to reproduce in Nova's Vue layer. It does not buy a scheduling product, calendar sync, or vendor guarantees. Approve at low confidence with a timeboxed spike, contingent on license clearance — and hold the line that recurrence and sync are separately priced.
How can I help you explore Laravel packages today?