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 Timenow Laravel Package

richardkeep/nova-timenow

A Laravel Nova field that shows the current time and updates live in your resources and dashboards. Configure timezone and display format to add a real-time clock wherever you need it.

View on GitHub
Deep Wiki
Context7

Getting Started

Hard precondition: this package only does anything inside a Laravel Nova admin panel. No Nova, no evaluation — there is nothing to wire up.

Step 0 — confirm what the artifact actually is before you plan any work. The metadata is inconsistent: description.md describes a field that pre-fills "now" into date/time inputs, while the repo name and the "current time from different timezones" tagline describe a dashboard card. Those are different use cases (data-entry hygiene vs. at-a-glance awareness) with different effort. Spend fifteen minutes in the source, not in the README:

git clone https://github.com/richardkeep/nova-timenow /tmp/timenow
ls -R /tmp/timenow/src /tmp/timenow/resources
cat /tmp/timenow/composer.json     # php/laravel/nova constraints
grep -rn "class .* extends" /tmp/timenow/src

If you find extends Laravel\Nova\Card, it's a dashboard widget. If you find extends Laravel\Nova\Fields\Field, it's a form input. Also check whether the clock is computed in PHP or in the Vue component — that changes what the thing is trustworthy for.

Step 1 — install pinned, on a branch.

composer require richardkeep/nova-timenow:^1.0 --no-interaction
php artisan nova:publish   # only if the package ships assets you need

Pin the tag. With 9 stars and 0 dependents, you are the compatibility test suite; a floating constraint buys you nothing but surprise.

Step 2 — register on a scratch dashboard, not the default one. Create app/Nova/Dashboards/OpsOverview.php and add the card there so you can look at it without committing to it:

public function cards()
{
    return [
        (new \Richardkeep\TimeNow\TimeNow())
            ->withMeta(['timezones' => ['UTC', 'Europe/London', 'America/New_York']])
            ->width('1/3'),
    ];
}

Adjust the constructor/meta shape to whatever src/ actually exposes — the three common Nova card shapes are a bare new TimeNow(), a fluent ->timezones([...]), or ->withMeta(['timezones' => [...]]).

First use case worth the install: an ops/support/on-call overview dashboard where someone scheduling an escalation can read APAC/EMEA/AMER wall-clock time without a browser tab switch. Configure two or three real team timezones — not a globe of twenty. That's the whole value proposition: a repeated micro-friction removed from every scheduling decision.

Done when: the card renders at the intended width in your Nova version, in light and dark mode, every configured zone resolves to a real row, and you've verified rollback (deregister the card, composer remove). No migrations are involved, so the spike costs an hour, not a sprint.

Implementation Patterns

One card per region group, sized to Nova's grid. Grouping beats a single card with nine rows — it stays scannable, and you can reorder or delete a region without editing an array:

public function cards()
{
    return [
        (new TimeNow())->withMeta(['timezones' => ['UTC']])->width('1/3'),
        (new TimeNow())->withMeta(['timezones' => ['Europe/London', 'Europe/Berlin']])->width('1/3'),
        (new TimeNow())->withMeta(['timezones' => ['America/New_York', 'America/Los_Angeles']])->width('1/3'),
    ];
}

Derive the zones at request time, don't hardcode them. If your Nova User carries a timezone or team attribute, build the card list per request so people see their own team's clocks:

public function cards(Request $request)
{
    $zones = $request->user()->team_timezones ?: ['UTC'];

    return [
        (new TimeNow())->withMeta([
            'timezones' => array_values(array_intersect($zones, timezone_identifiers_list())),
        ])->width('1/2'),
    ];
}

The array_intersect guard is not optional — see Gotchas.

Dashboard-scoped by default, global only by request. Put the card in a purpose-built Dashboard class's cards(). Reach for NovaServiceProvider::cards() only if it genuinely belongs on the main dashboard for everyone; an unrequested card on the default dashboard is how admin panels accrete clutter.

If the source turns out to be a field, the pattern is different and simpler:

public function fields(NovaRequest $request)
{
    return [
        TimeNow::make('Logged At')
            ->default(fn () => now($request->user()->timezone))
            ->sortable(),
    ];
}

Here the day-to-day leverage is data-entry accuracy — stamping the current moment without a click — and it's only worth it on resources where operators repeatedly do that. Verify the underlying column type (time/timestamp, not a string) and how it serializes to the model; a mistyped date field is worse than a plain DateTime::make().

Fork into nova-components for anything load-bearing. The functional surface is roughly fifty lines: a config array of IANA identifiers, a render loop, and a view. When you need real behaviour, own it:

php artisan nova:card acme/timezones
// nova-components/Timezones/src/Card.php
public function component() { return 'timezones'; }

public function timezones(array $zones): static
{
    return $this->withMeta(['timezones' => $zones]);
}
<!-- nova-components/Timezones/resources/js/components/Card.vue -->
<template>
  <Card class="flex flex-col justify-center px-6 py-4">
    <div v-for="tz in zones" :key="tz" class="flex justify-between py-1">
      <span class="text-80">{{ tz }}</span>
      <span class="font-bold">{{ format(tz) }}</span>
    </div>
  </Card>
</template>

<script>
export default {
  props: ['card'],
  data: () => ({ tick: null }),
  computed: {
    zones() { return this.card.timezones || ['UTC'] }
  },
  mounted() { this.tick = setInterval(() => this.$forceUpdate(), 1000) },
  beforeUnmount() { clearInterval(this.tick) },
  methods: {
    format(tz) {
      return new Intl.DateTimeFormat(undefined, {
        timeZone: tz, hour: '2-digit', minute: '2-digit', hour12: false,
      }).format(new Date())
    }
  }
}
</script>

Intl.DateTimeFormat with a timeZone option gives you DST correctness, viewer locale, and 24-hour formatting for free — no dependency, no per-request Carbon cost, no assets to rebuild when Nova bumps.

Verify in staging before promoting:

  • Card renders (an empty box or a Vue console error almost always means stale or unpublished assets).
  • Every configured zone resolves — watch for InvalidTimeZoneException or a silently blank row.
  • Layout holds at 1/3, 1/2, and full width; Nova's grid is unforgiving.
  • Dark mode, and whichever Tailwind steps (text-80, text-90, etc.) your Nova major ships.

Gotchas and Tips

The field-vs-card ambiguity is a real trap, not a documentation nit. Scoping a "one-click now" ticket against a timezone-clock card (or the reverse) burns a sprint. If README and description.md still disagree after your read, trust src/.

Invalid IANA identifiers fail quietly. EST, PST, CST are not IANA zones at all; US/Eastern is valid but deprecated. Depending on where the conversion happens you get a thrown exception or a blank row. Filter defensively:

$zones = array_values(array_filter($zones, fn ($tz) => in_array($tz, timezone_identifiers_list())));

Note timezone_identifiers_list() also returns UTC offsets like +05:00 — if you only want city zones, restrict with DateTimeZone::PER_COUNTRY or a whitelist.

Client clock vs server clock is a correctness question. If the Vue component reads the browser clock, the card shows the viewer's laptop time converted — fine for "what time is it in London," wrong the moment someone screenshots it into an incident timeline, because the viewer's system clock may be skewed. Prefer computing from a server-supplied timestamp (now()->timestamp) passed through card meta. If the package doesn't expose one, that alone justifies the fork.

Stale assets after a Nova major bump. These cards ship compiled JS built against a specific Nova/Tailwind version. After upgrading Nova, run php artisan nova:publish and rebuild the package (or your fork) — otherwise you get a blank card or [Vue warn]: Unknown custom element. Add "re-verify the timezone card" to your Nova upgrade checklist; nothing upstream will test it for you.

Config location is not obvious. Timezones may live in withMeta, in a published config/timenow.php, or be hardcoded in the Vue component. Grep before assuming:

grep -rn "timezones" vendor/richardkeep/nova-timenow --include="*.php" --include="*.vue"

12/24-hour and locale. If the package hardcodes HH:mm or h:mm A, no config will fix it. Intl.DateTimeFormat with hour12 unset follows the viewer's locale, which is usually what you want — another fork trigger if your team spans locales.

Rollback is free — use it as your evaluation budget. Deregister the card, composer remove richardkeep/nova-timenow, php artisan view:clear, php artisan nova:publish. Because removal costs nothing, trial it on a non-critical dashboard with real users for a week before deciding. The cost is an hour, not a project.

Supply-chain reality check. Nine stars, zero dependents, single maintainer, December 2024 release. Recent is not maintained. Record the maintainer in your dependency register, pin the tag, and set a reminder to re-check the repo at your next Nova upgrade. Don't let on-call procedures depend on it; if it becomes load-bearing, that's the signal to fork — not to open an issue and wait.

Extend, don't patch. For business-hours highlighting, a "next handoff" marker, or per-user zones, run php artisan nova:card and own the fifty lines. Subclassing a vendor card buys a merge conflict at the next Nova major for no benefit at this size.

Never derive business rules from the display. No SLA windows, on-call routing, or shift boundaries computed from what the card shows. It's a clock, not a time service — every consumer should read its own Carbon::now($tz) (or its own Intl conversion) from an authoritative source.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect