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
kinetix-reports-center
kinetix-reports-center
happones/kinetix
claude-code
reports-center
queued-exports
report-runs
cancellation
scheduling
downloads
progress-tracking

Queued, DB-tracked CSV/XLSX report generation for large datasets — live progress, cancellation, retry, disk-agnostic downloads, plus one-off/recurring scheduling. Define a Report class under app/Kinetix/Reports (auto-discovered); mount <KinetixReportLauncher>, <KinetixReportRunsTable>, <KinetixReportSchedules>, or the tabbed <KinetixReportsCenter>. Activates when building large-dataset exports, a 'failed jobs'-style run tracker, or scheduled/recurring reports with a download UI.

Install
php artisan boost:add-skill happones/kinetix

Save this content to: .claude/skills/kinetix-reports-center/SKILL.md

# Kinetix Reports Center Development

## When to Apply

Activate this skill when:
- Generating large CSV/XLSX reports that must be chunked, queued, and tracked.
- Building a launcher UI for report "types," a runs table with progress/cancel/
  retry/download, or a scheduled/recurring reports list.
- The request mentions cancelling a running export, resuming/retrying a failed
  one, or downloading a previously generated report file.

Do **not** use this for a simple recurring email export — that's the lighter
[`kinetix-reports`](../kinetix-reports/SKILL.md) skill (`Happones\Kinetix\Reports`,
config `kinetix.reports`): it emails an Exporter's output on a cadence, with no
DB row, no progress, no cancellation, and no download UI. Reports Center
(`Happones\Kinetix\ReportsCenter`, config `kinetix.reports_center`) is the
productized version — every run is tracked, cancellable, retryable, and
downloadable from a UI. The two systems are independent; use whichever fits,
or both.

## Documentation

For full details, reference `docs/reports-center.md` (published at
https://happones.github.io/kinetix/reports-center).

## Installation & Configuration

No migration to publish — `kinetix_report_schedules` and `kinetix_report_runs`
ship with the package's own migrations.

```php
'reports_center' => [
    'enabled' => env('KINETIX_REPORTS_CENTER_ENABLED', false),

    // Directory (+ namespace) auto-scanned for `Report` subclasses.
    'discover_path'      => app_path('Kinetix/Reports'),
    'discover_namespace' => 'App\\Kinetix\\Reports',

    // Frontend poll interval (ms) for the runs/schedules widgets.
    'poll' => env('KINETIX_REPORTS_CENTER_POLL', 5000),

    // Days a completed run's row + file are kept before pruning.
    'retention_days' => env('KINETIX_REPORTS_CENTER_RETENTION_DAYS', 7),
],
```

Reports are stored on the same disk as the rest of Kinetix
(`config('kinetix.filesystem.disk')`) — no separate disk setting. Queue
placement follows the same per-class `->queue()` override `Exporter` already
has — no separate global queue connection setting either.

## Define a report type

```bash
php artisan kinetix:make-report MonthlyInvoicesReport
```

```php
namespace App\Kinetix\Reports;

use Happones\Kinetix\Exports\ExportColumn;
use Happones\Kinetix\ReportsCenter\Report;
use App\Models\Invoice;

class MonthlyInvoicesReport extends Report
{
    protected static ?string $model = Invoice::class;

public function label(): string { return 'Monthly Invoices'; }
    public function description(): ?string { return 'Every invoice, with line-item totals.'; }
    public function format(): string { return 'xlsx'; }

    public static function getColumns(): array
    {
        return [
            ExportColumn::make('id'),
            ExportColumn::make('customer.name')->label('Customer'),
        ];
    }
}
```

`Report extends Exporter` unchanged — `query()`/`getColumns()`/`chunkSize()`
(default 1000)/`format()` all work the same, so large datasets are always
processed in chunks, never loaded into memory at once. Classes placed under
`discover_path` are picked up automatically — no manual registration. To
register one living elsewhere:

```php
use Happones\Kinetix\ReportsCenter\KinetixReportsCenter;

KinetixReportsCenter::register(\App\Reports\SomeOtherReport::class);
```

## Frontend

```vue
<KinetixReportsCenter />
```

...or the three views independently — both are equally valid:

```vue
<KinetixReportLauncher />     <!-- pick a report type, "Run now" -->
<KinetixReportRunsTable />    <!-- status/progress/download/cancel/retry -->
<KinetixReportSchedules />    <!-- recurring/scheduled definitions -->
```

All take zero props — they self-fetch from gated endpoints and poll on
`kinetix.reports_center.poll`.

## Progress, cancellation, and retry

- **Progress** updates once per chunk (not per row) — `processed_rows` always
  increments; the percentage only shows when `estimatesTotal()` (default
  `true`) could count the query upfront.
- **Cancel** marks the run cancelled; the job notices on its *next* chunk
  boundary and stops there. It does not, and cannot, kill the queue worker
  process — this is cooperative cancellation, working identically across
  every queue driver (database, Redis, SQS, Horizon).
- **Retry** (only once `failed` or `cancelled`) dispatches a **fresh** run
  with the same report/parameters — it does not reuse Laravel's own automatic
  job retries, reserved for the job's own transient-failure handling.
- **Download** is only available while `status === 'completed'` and before
  `expires_at` — a real, row-backed expiry.

## Scheduling & recurrence

```php
use Happones\Kinetix\ReportsCenter\ReportSchedule;

ReportSchedule::create([
    'report_class' => \App\Kinetix\Reports\MonthlyInvoicesReport::class,
    'frequency'    => 'monthly', // once | daily | weekly | monthly
    'enabled'      => true,
    'next_run_at'  => now(),
]);
```

```php
Schedule::command('kinetix:report-schedules:dispatch-due')->everyMinute();
```

Each due schedule creates a new `ReportRun`, advances `next_run_at` per its
frequency, and (for `once`) disables itself after firing.

## Pruning old runs

```bash
php artisan kinetix:report-runs:prune {--days=}
```

Deletes the file + row for completed runs past `expires_at`, and the row
alone for failed/cancelled runs older than `retention_days`.

## UUID / ULID Host Models

This feature's migration builds `created_by_id`, `launched_by_id` and `team_id` with
`Happones\Kinetix\Support\HostKeys`, which types each column after YOUR model
at migrate time from its primary key (`HasUlids` -> ulid, `HasUuids` -> uuid, string `$keyType` ->
string, else bigint). Pin `kinetix.key_types.user|team` when detection cannot
see the setup; morph ids follow `kinetix.key_types.morph` (default bigint) —
set it when the referenced models use UUIDs/ULIDs. Apps migrated on an older
Kinetix have bigint columns on disk and need their own ALTER migration. Full
recipe: the `kinetix-boost` skill, section "UUID / ULID Host Models".

Kinetix Reports Center Development

When to Apply

Activate this skill when:

  • Generating large CSV/XLSX reports that must be chunked, queued, and tracked.
  • Building a launcher UI for report "types," a runs table with progress/cancel/ retry/download, or a scheduled/recurring reports list.
  • The request mentions cancelling a running export, resuming/retrying a failed one, or downloading a previously generated report file.

Do not use this for a simple recurring email export — that's the lighter kinetix-reports skill (Happones\Kinetix\Reports, config kinetix.reports): it emails an Exporter's output on a cadence, with no DB row, no progress, no cancellation, and no download UI. Reports Center (Happones\Kinetix\ReportsCenter, config kinetix.reports_center) is the productized version — every run is tracked, cancellable, retryable, and downloadable from a UI. The two systems are independent; use whichever fits, or both.

Documentation

For full details, reference docs/reports-center.md (published at https://happones.github.io/kinetix/reports-center).

Installation & Configuration

No migration to publish — kinetix_report_schedules and kinetix_report_runs ship with the package's own migrations.

'reports_center' => [
    'enabled' => env('KINETIX_REPORTS_CENTER_ENABLED', false),

    // Directory (+ namespace) auto-scanned for `Report` subclasses.
    'discover_path'      => app_path('Kinetix/Reports'),
    'discover_namespace' => 'App\\Kinetix\\Reports',

    // Frontend poll interval (ms) for the runs/schedules widgets.
    'poll' => env('KINETIX_REPORTS_CENTER_POLL', 5000),

    // Days a completed run's row + file are kept before pruning.
    'retention_days' => env('KINETIX_REPORTS_CENTER_RETENTION_DAYS', 7),
],

Reports are stored on the same disk as the rest of Kinetix (config('kinetix.filesystem.disk')) — no separate disk setting. Queue placement follows the same per-class ->queue() override Exporter already has — no separate global queue connection setting either.

Define a report type

php artisan kinetix:make-report MonthlyInvoicesReport
namespace App\Kinetix\Reports;

use Happones\Kinetix\Exports\ExportColumn;
use Happones\Kinetix\ReportsCenter\Report;
use App\Models\Invoice;

class MonthlyInvoicesReport extends Report
{
    protected static ?string $model = Invoice::class;

public function label(): string { return 'Monthly Invoices'; }
    public function description(): ?string { return 'Every invoice, with line-item totals.'; }
    public function format(): string { return 'xlsx'; }

    public static function getColumns(): array
    {
        return [
            ExportColumn::make('id'),
            ExportColumn::make('customer.name')->label('Customer'),
        ];
    }
}

Report extends Exporter unchanged — query()/getColumns()/chunkSize() (default 1000)/format() all work the same, so large datasets are always processed in chunks, never loaded into memory at once. Classes placed under discover_path are picked up automatically — no manual registration. To register one living elsewhere:

use Happones\Kinetix\ReportsCenter\KinetixReportsCenter;

KinetixReportsCenter::register(\App\Reports\SomeOtherReport::class);

Frontend

<KinetixReportsCenter />

...or the three views independently — both are equally valid:

<KinetixReportLauncher />     <!-- pick a report type, "Run now" -->
<KinetixReportRunsTable />    <!-- status/progress/download/cancel/retry -->
<KinetixReportSchedules />    <!-- recurring/scheduled definitions -->

All take zero props — they self-fetch from gated endpoints and poll on kinetix.reports_center.poll.

Progress, cancellation, and retry

  • Progress updates once per chunk (not per row) — processed_rows always increments; the percentage only shows when estimatesTotal() (default true) could count the query upfront.
  • Cancel marks the run cancelled; the job notices on its next chunk boundary and stops there. It does not, and cannot, kill the queue worker process — this is cooperative cancellation, working identically across every queue driver (database, Redis, SQS, Horizon).
  • Retry (only once failed or cancelled) dispatches a fresh run with the same report/parameters — it does not reuse Laravel's own automatic job retries, reserved for the job's own transient-failure handling.
  • Download is only available while status === 'completed' and before expires_at — a real, row-backed expiry.

Scheduling & recurrence

use Happones\Kinetix\ReportsCenter\ReportSchedule;

ReportSchedule::create([
    'report_class' => \App\Kinetix\Reports\MonthlyInvoicesReport::class,
    'frequency'    => 'monthly', // once | daily | weekly | monthly
    'enabled'      => true,
    'next_run_at'  => now(),
]);
Schedule::command('kinetix:report-schedules:dispatch-due')->everyMinute();

Each due schedule creates a new ReportRun, advances next_run_at per its frequency, and (for once) disables itself after firing.

Pruning old runs

php artisan kinetix:report-runs:prune {--days=}

Deletes the file + row for completed runs past expires_at, and the row alone for failed/cancelled runs older than retention_days.

UUID / ULID Host Models

This feature's migration builds created_by_id, launched_by_id and team_id with Happones\Kinetix\Support\HostKeys, which types each column after YOUR model at migrate time from its primary key (HasUlids -> ulid, HasUuids -> uuid, string $keyType -> string, else bigint). Pin kinetix.key_types.user|team when detection cannot see the setup; morph ids follow kinetix.key_types.morph (default bigint) — set it when the referenced models use UUIDs/ULIDs. Apps migrated on an older Kinetix have bigint columns on disk and need their own ALTER migration. Full recipe: the kinetix-boost skill, section "UUID / ULID Host Models".

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